2015年6月16日 星期二

Stream (java8 五)

Collection<E>新增了default Stream<E> stream()方法,會回傳java.util.stream包的Stream<T>介面

BaseStream<T,S extends BaseStream<T,S>>介面有4個子介面
DoubleStream、IntStream、LongStream、Stream<T>,其中一個就是Stream<T>

java.lang.CharSequence、java.util.Collection、java.util.Arrays 和 Stream 本身都有新增方法可以取得 Stream,可能還有其他套件


※取得 Steam 的幾種方式

// String 取得 Stream
new String("abcde").chars().forEach(System.out::println);
    
// List 取得 Stream
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); // 順序
Stream<String> parallelStream = list.parallelStream(); // 並行,速度較順序快
    
// Arrays 取得 Stream
Stream<Integer> arraysStream = Arrays.stream(new Integer[10]);
    
// Stream 本身取得 Stream
Stream<Integer> streamOf = Stream.of(1, 2, 3, 4, 5, 6);
    
// 無窮的 Stream
Stream<Integer> streamIterate = Stream.iterate(0, x -> x + 2).limit(5);
streamIterate.forEach(System.out::println);
    
// 無窮的 Stream
Stream<Double> streamGenerate = Stream.generate(Math::random).limit(3);
streamGenerate.forEach(System.out::println);



※of、collect

// 原本的方法
List<String> ox = Arrays.asList("xxx", "ooo", "xox");
System.out.println(ox);
    
// Stream
Stream<String> st = Stream.of("xxx", "ooo", "xox");
List<String> xo = st.collect(Collectors.toList());
// Stream.of("xxx", "ooo", "xox").collect(Collectors.toList());
System.out.println(xo);

※of會按照順序,回傳裡面的值;collect可以回傳集合,此例也可以回傳toSet()


※count

// 原本的方法
List<Integer> list = Arrays.asList(1, 2, 3);
System.out.println("size=" + list.size());
    
// Stream
long count = Stream.of(1, 2, 3).count();
System.out.println("count=" + count);




※Collectors.joining

// 原本的方法
final List<String> animal = new ArrayList<>();
animal.add("elephant");
animal.add("dog");
animal.add("pig");
animal.add("rat");
animal.add("tiger");
animal.add("cat");
    
StringBuilder sb = new StringBuilder("[");
for (String s : animal) {
    sb.append(",");
    sb.append(s);
}
sb.append("]").delete(1, 2); // 刪除第一個逗點
System.out.println(sb.toString());
    
// Stream
String result = animal.stream().collect(Collectors.joining(",", "[", "]"));
System.out.println(result);

※Collectors裡面有超多方法,這個還不錯用


※map

// ===将属性取代===
// 原本的方法
List<String> ox = new ArrayList<>();
for (String data : Arrays.asList("xxx", "ooo", "xox")) {
    ox.add(data.replace("o", "z"));
}
System.out.println(ox);
    
// Stream
Stream<String> st = Stream.of("xxx", "ooo", "xox");
Stream<String> map = st.map(data -> data.replace("o", "z"));
List<String> xo = map.collect(Collectors.toList());
// Stream.of("xxx", "ooo", "xox").map(data -> data.replace("o", "z")).collect(Collectors.toList());
System.out.println(xo);
    
    
    
// ===只取得部分属性===
Animal a1 = new Animal(1, "aaa");
Animal a2 = new Animal(2, "bbb");
Animal a3 = new Animal(3, "ccc");
List<Animal> animals = Stream.of(a1, a2, a3).collect(Collectors.toList());
    
// 原本的方法
List<String> c1 = new ArrayList<>();
for (Animal a : animals) {
    c1.add(a.getName());
}
c1.forEach(System.out::println);
    
// Stream
List<String> c2 = animals.stream().map(Animal::getName).collect(Collectors.toList());
c2.forEach(System.out::println);


※map 裡放的參數是 Function


※flatMap

public class Java8Test {
    public static void main(String[] args) {
        // 使用 map
        Stream<Stream<String>> stream1 = Stream.of("a1", "b2", "c3").map(Java8Test::xxx);
        stream1.forEach(stream -> stream.forEach(System.out::println));
    
        // 使用 flatMap
        Stream<String> stream2 = Stream.of("a1", "b2", "c3").flatMap(Java8Test::xxx);
        stream2.forEach(System.out::println);
    
        // List 的 add 和 addAll
        List<String> list1 = Arrays.asList("xxx", "ooo", "xox");
        List<Object> list2 = Stream.of("a1", "b2", "c3").collect(Collectors.toList());
        // list2.add(list1);
        // System.out.println(list2); // [a1, b2, c3, [xxx, ooo, xox]]
        list2.addAll(list1);
        System.out.println(list2); // [a1, b2, c3, xxx, ooo, xox]
    }
    
    public static Stream<String> xxx(String s) {
        List<String> l = new ArrayList<>();
        l.add(s.substring(0, 1));
        return l.stream();
    }
}

※map 和 flatMap 的關係就相當於 List 裡的 add 和 addAll


※filter

// 原本的方法
String[] animal = { "elephant", "dog", "pig", "rat", "tiger", "cat" };
    
List<String> result = new ArrayList<>();
for (String a : animal) {
    if (a.endsWith("t")) {
        result.add(a);
    }
}
System.out.println(result);
    
// Stream
Stream<String> st = Stream.of(animal);
Stream<String> filt = st.filter(a -> a.endsWith("t"));
List<String> rtn = filt.collect(Collectors.toList());
System.out.println(rtn);

※filter 裡放的參數是 Predicate


※allMatch、anyMatch、noneMatch

static List<String> animal = new ArrayList<>();
static {
    animal.add("elephant");
    animal.add("dog");
    animal.add("pig");
    animal.add("rat");
    animal.add("tiger");
    animal.add("cat");
}
    
public static void main(String[] args) {
    animal.stream().map(str -> str.toUpperCase()).distinct().
        collect(Collectors.toList()).
        forEach(System.out::println);
    
    if (animal.stream().allMatch(str -> str.contains("a"))) {
        System.out.println("全有a");
    }
    
    if (animal.stream().anyMatch(str -> str.contains("n"))) {
        System.out.println("其中一個以上有n");
    }
    
    if (animal.stream().noneMatch(str -> str.contains("A"))) {
        System.out.println("全部都沒有A");
    }
}

※這三個方法的參數,都是 Predicate

※注意使用 distinct 時,必須覆寫 hashCode 和 equals 方法,只是此例的 String 內鍵已經寫好了


※多條件判斷

Predicate有提供and/or方法

List<String> animal = new ArrayList<>();
animal.add("ELEPHANT");
animal.add("RAT");
animal.add("CAT");
animal.add("12345678");
Predicate<String> p1 = (str) -> str.startsWith("E");
Predicate<String> p2 = (str) -> str.length() == 8;
animal.stream().filter(p1.or(p2)).forEach(System.out::println);
System.out.println();
animal.stream().filter(p1.and(p2)).forEach(System.out::println);




※sorted

// 原本的方法
final List<Integer> list = Arrays.asList(1, 3, 5, 2, 4);
// 正序
Collections.sort(list);
System.out.println("asc=" + list);
// 倒序
Collections.reverse(list);
System.out.println("desc=" + list);
    
// Stream
final Integer[] i = { 1, 3, 5, 2, 4 };
// 正序 (自然排序)
List<Integer> asc = Stream.of(i).sorted().collect(Collectors.toList());
System.out.println("asc=" + asc);
    
// 倒序 (自訂排序)
List<Integer> desc = Stream.of(i).sorted((p1, p2) -> p2.compareTo(p1)).collect(Collectors.toList());
System.out.println("desc=" + desc);




※flatMap

List<Integer> listA = new ArrayList<>();
listA.add(1);
listA.add(2);
listA.add(3);
    
List<Integer> listB = new ArrayList<>();
listB.add(4);
listB.add(5);
listB.add(6);
    
/*
 * 原本的方法,使用Arrays.asList然後用add、addAll、remove執行時期會報錯
 * Arrays.asList()API有說明,它是固定的長度,不能改的,但可以用如下的方式解決
 * List<Integer> xxx = Arrays.asList(1, 2, 3); 
 * List<Integer> listA = new ArrayList<>(xxx);
 */
listA.addAll(listB);
System.out.println(listA);
    
// Stream,使用Arrays.asList和List都可以
List<Integer> aList = Arrays.asList(1, 2, 3);
List<Integer> bList = Arrays.asList(4, 5, 6);
    
Stream<List<Integer>> abList = Stream.of(aList, bList);
Stream<Integer> rtn = abList.flatMap(x -> x.stream());
List<Integer> result = rtn.collect(Collectors.toList());
System.out.println(result);

※可將兩個相同集合合併


※min、max

// 原本的方法
List<Integer> list = Arrays.asList(1, 3, 5, 4, 2);
Collections.sort(list);
System.out.println("min=" + list.get(0));
System.out.println("max=" + list.get(list.size() - 1));
    
// Stream
Stream<Integer> st1 = Stream.of(1, 3, 5, 4, 2);
Optional<Integer> opt1 = st1.min(Comparator.comparing(p -> p));
int min = opt1.get();
System.out.println("min=" + min);
    
Stream<Integer> st2 = Stream.of(1, 3, 5, 4, 2);
Optional<Integer> opt2 = st2.max(Comparator.comparing(p -> p));
int max = opt2.get();
System.out.println("max=" + max);

※注意回傳的是Optional,下一篇會說明


※reduce

// 原本的做法
List<String> list = Arrays.asList("xxx", "ooo", "xox");
StringBuffer sb = new StringBuffer();
for (String s : list) {
    sb.append(s);
}
System.out.println("sb=" + sb);
    
// Stream 的做法
class User {
    private Integer age;
    
    public User(Integer age) {
        this.age = age;
    }
    
    public Integer getAge() {
        return age;
    }
}
    
    
List<User> users = List.of(new User(1), new User(2), new User(3), new User(4), new User(5));
    
// 一個參數 (p1 為第一次或上次的值;p2 為每次循環的值)
User one1 = users.stream().reduce((p1, p2) -> new User(p1.getAge() + p2.getAge())).get();
System.out.println(one1.getAge()); // 15
    
User one2 = users.stream().parallel().reduce((p1, p2) -> new User(p1.getAge() + p2.getAge())).get();
System.out.println(one2.getAge()); // 15
    
// 兩個參數 (和一個參數比較,第一個參數為初始值,第二個參數和一個參數一樣)
User two1 = users.stream().reduce(new User(10), (p1, p2) -> new User(p1.getAge() + p2.getAge()));
System.out.println(two1.getAge()); // 25
    
User two2 = users.stream().parallel().reduce(new User(10), (p1, p2) -> new User(p1.getAge() + p2.getAge()));
System.out.println(two2.getAge()); // 65, 第二個參數配合 parallel 是 11+12+13+14+15
    
// 三個參數 (和兩個參數比較,前兩個參數一樣,最後的參數是 BinaryOperator)
int three1 = users.stream().reduce(10, (i, u) -> i + u.getAge(), Integer::sum);
System.out.println(three1); // 25
    
int three2 = users.stream().parallel().reduce(10, (i, u) -> i + u.getAge(), Integer::sum);
System.out.println(three2); // 65, 算法和兩個參數一樣,但回傳值不同

※reduce是個overloading,有三個,分別是一個參數、兩個參數、三個參數

※一個參數的回傳的是 Optional,用它的 get 方法可取出

※兩個參數相當於第一個參數給初始值,但回傳的是第一個參數的型態,所以不用像一個參數那樣,還要用 get 方法
因為有給初始值了,所以不可能為 null,就不需要 Optional 了

※三個參數可以直接用想要的回傳值去接 (兩個參數也可用 map 達到類似的效果,寫法很多)


※Statistics

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
IntSummaryStatistics result = list.stream().mapToInt(i -> i).summaryStatistics();
    
System.out.println("最大值=" + result.getMax());
System.out.println("最小值=" + result.getMin());
System.out.println("加總=" + result.getSum());
System.out.println("個數=" + result.getCount());
System.out.println("平均值=" + result.getAverage());

※統計用的類別,在java.util包下,實作IntConsumer介面


※mapToInt

class Chess {
    private String name;
    private int amount;
    private int price;
    
    public Chess(String name, int price, int amount) {
        this.name = name;
        this.price = price;
        this.amount = amount;
    }
    
    public Chess(){}
    
    // setter/getter...
}



List<Chess> chess = new ArrayList<>();
chess.add(new Chess("jump", 30, 1));
chess.add(new Chess("road", 40, 5));
chess.add(new Chess("elephant", 55, 2));
chess.add(new Chess("animal", 25, 3));
chess.stream().map((Chess ch) -> {
    return ch.getAmount() * ch.getPrice();
}).forEachOrdered(System.out::println);
    
// reduce(),將集合中的資料合為一個結果
int result = chess.stream().map((Chess ch) -> {
    return ch.getAmount() * ch.getPrice();
}).reduce((Integer s, Integer c) -> s + c).get();
System.out.println("result = " + result);
    
IntSummaryStatistics iss = chess.stream().mapToInt((Chess ch) -> { // 還有mapToDouble、mapToLong,用法差不多
    return ch.getAmount() * ch.getPrice();
}).summaryStatistics();
System.out.println("result = " + iss); // 這一行包括下面5個方法
System.out.println("result = " + iss.getCount());
System.out.println("result = " + iss.getSum());
System.out.println("result = " + iss.getMin());
System.out.println("result = " + iss.getAverage());
System.out.println("result = " + iss.getMax());



※sequential、parallel

public static void main(String[] args) {
    final List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    sequential(list);
    parallel(list);
}
    
// 順序運算
private static void sequential(List<Integer> list) {
    long start = System.nanoTime();
    list.stream().sequential().sorted().collect(Collectors.toList());
    long end = System.nanoTime();
    long resule = TimeUnit.NANOSECONDS.toMillis(end - start);
    System.out.println("sequntial=" + resule);
}
    
// 並行運算
private static void parallel(List<Integer> list) {
    long start = System.nanoTime();
    list.stream().parallel().sorted().collect(Collectors.toList());
    long end = System.nanoTime();
    long resule = TimeUnit.NANOSECONDS.toMillis(end - start);
    System.out.println("parallel=" + resule);
}

※並行就是充份的利用CPU,所以通常速度比較快,但沒有順序,都不寫預設為順序運算

※如果先執行parallel再執行sequential,會發現sequential是0,可能並行(parallel)會先儲在某個地方吧!

※TimeUnit、nanoTime也是java8新增的,後續篇幅會說明


※例二

List<String> animal = new ArrayList<>();
animal.add("ELEPHANT");
animal.add("RAT");
animal.add("CAT");
animal.add("1234A5678");
Predicate<String> p1 = (str) -> str.contains("A");
Predicate<String> p2 = (str) -> str.length() == 8;

//是否並行(預設false,預設使用順行); 並行(Parallel)比順行(sequential)快,但沒順行
System.out.println(animal.stream().filter(p1.or(p2)).isParallel());

animal.stream().filter(p1.or(p2)).forEach(System.out :: println);
//ELEPHANT RAT CAT 1234A5678,有順序  順序運算
System.out.println();

animal.stream().filter(p1.or(p2)).parallel().forEach(System.out :: println);
//CAT 1234A5678 RAT ELEPHANT,沒順序  並行運算
System.out.println();

animal.stream().filter(p1.or(p2)).parallel().sequential().forEach(System.out :: println);
// 兩個都寫會後者蓋前者,所以為順序運算


※forEach、limit

// 使用lambda
IntStream.range(0, 10).forEach(i -> System.out.print(i));
System.out.println();
    
// 使用引用
IntStream.range(0, 10).forEach(System.out::print);
System.out.println();
    
// range改成rangeClosed會包括最後的10
IntStream ist = new Random().ints();
IntStream isl = ist.limit(3);
isl.forEach(i -> System.out.println(i));
    
// 重覆使用Stream
Stream<Integer> stream = Stream.of(1, 3, 5, 4, 2);
stream.forEach(i -> System.out.print(i));
System.out.println();
//    stream.forEachOrdered(i -> System.out.print(i));// stream has already been operated upon or closed
    
Supplier<Stream<Integer>> streamSup = () -> Stream.of(1, 3, 5, 4, 2);
streamSup.get().forEach(System.out::println);
System.out.println();
streamSup.get().forEachOrdered(System.out::println);

※還有個forEachOrdered,用法和forEach一樣,但它保證會按照順序輸出,不是排序哦!

※limit是限制輸出多少個

※註解打開會錯誤,因為它已經關閉了,Stream只能使用一次

※可以利用Supplier來達到多次的目的


沒有留言:

張貼留言