Java8 stream

1,631 views

Published on

Published in: Technology
0 Comments
8 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,631
On SlideShare
0
From Embeds
0
Number of Embeds
9
Actions
Shares
0
Downloads
44
Comments
0
Likes
8
Embeds 0
No embeds

No notes for slide

Java8 stream

  1. 1. Java 8 Stream @kojilin 2014/3/29@TWJUG
  2. 2. Stream •java.util.stream •Support functional-style operations on streams of elements
 int sum = 
 widgetList.stream()
 .filter(b -> b.color == RED)
 .mapToInt(b -> b.getWeight())
 .sum();
  3. 3. External Iteration long sum = 0; for(int i = 0; i < N; ++i){ sum += i * i; }
  4. 4. Internal Iteration long sum = LongStream .range(0, N) .map(i -> i * i) .sum();
  5. 5. •為什麼需要 Stream ? •從外部迭代轉為內部迭代 •外部迭代需要注意要做什麼跟如何做 •內部迭代著重在要做什麼比如何做更多 •將行為資料化 •讓函式庫幫你更多的忙 •讓程式碼更簡潔好讀
  6. 6. Stream •Stream 不是資料結構,沒有 存資料 •包裹在現有的資料結構之上 •透過內部迭代 •Multi-thread •Laziness •Stream 有 iterator 方法,但沒有實作 Iterable •所以不能寫 for (String item : stream)
  7. 7. Stream 的種類 •Stream<T> •Primitive Streams •IntStream •DoubleStream •LongStream •彼此之間可以透過像 mapToInt, mapToDouble, mapToObj 等方式切換
  8. 8. Stream 的使用方式 long sum = LongStream .range(0, N) .map(i -> i * i) .filter(i -> i % 2 == 0) .sum(); •建立 Stream
  9. 9. Stream 的使用方式 long sum = LongStream .range(0, N) .map(i -> i * i) .filter(i -> i % 2 == 0) .sum(); •透過連續的 intermediate operation 操作
  10. 10. Stream 的使用方式 long sum = LongStream .range(0, N) .map(i -> i * i) .filter(i -> i % 2 == 0) .sum(); •最後用 terminal operation 結束
  11. 11. 如何取得 Stream? •Collection#stream() •Arrays.stream(Object[]) •Stream.of(Object[]) •IntStream.range(int, int) •BufferedReader#lines() •Stream#filter() •etc.

  12. 12. Intermediate Operation •回傳 Stream,可以接下一個 operation •Lazy •直到遇到 terminal operation 的方法前不 會有效果
  13. 13. •filter
 •map
 •flatMap
 
 •sorted stream.filter( p -> p > 20); stream.map( p -> p.name); stream.flatMap( p -> 
 p.cars.stream()); stream.sorted(p1, p2 -> 
 p1.name.compareTo(p2.name));
  14. 14. •limit
 •distinct
 •skip stream.limit(5); stream.distinct(); stream.skip(5);
  15. 15. Stateful & Stateless •Stateful 表示在操作元素時,除了當下的元 素外需要考慮其他狀態 •sorted, skip, limit •Stateless 表示在操作元素時,除了當下的元 素外不用考慮其他狀態 •filter, map
  16. 16. Terminal Operation •不會回傳 stream,整個 stream 只能有一個 terminal operation 呼叫 •呼叫後才會開始走查 stream,最後產生並回 傳結果或是製造 side-effect •呼叫完 terminal operation 後 stream 會被 當作被使用完畢 •Stream -> (Intermediate)* -> Terminal
  17. 17. •forEach
 •reduce
 •collect
 
 •count stream.forEach( p -> print(p)); stream.reduce( 0, (a, b) -> a + b); stream.collect(
 Collectors.toList()); stream.count();
  18. 18. •toArray
 •max
 •sum ( Primitive Stream )
 •average ( Primitive Stream ) stream.toArray(String[]::new); stream.max(String::compareTo); intStream.sum(); intStream.average();
  19. 19. Reduction •將元素組合成一個結果 •reduce, collect, sum, max, count intStream.sum(); numbers.stream().reduce(0, 
 (x,y) -> x + y);
  20. 20. <U> U reduce( U identity, BiFunction<U, ? super T, U > accumulator, BinaryOperator<U> combiner); Stream<T>#reduce •accumulator 和 combiner 每次處理完元素 後,必須回傳新的值
  21. 21. Integer result = stream.reduce(0, (sum, b) -> sum + b.getWeight(), Integer::sum); Stream<T>#reduce
  22. 22. list.stream().reduce( new ArrayList<>(), (integers, o) -> { integers.add(o); return integers; }, (integers, integers2) -> { integers.addAll(integers2); return integers; }); ❌
  23. 23. list.stream().reduce( new ArrayList<>(), (integers, o) -> { ArrayList<Integer> list = new ArrayList<>(integers); list.add(o); return list; }, (integers, integers2) -> { ArrayList<Integer> list = new ArrayList<>(integers); list.addAll(integers2); return list; }); △
  24. 24. Mutable Reduction •將元素累加到 mutable 的容器中 •Stream#collect •和 reduce 不同,是改變已經存在的值
  25. 25. <R> R collect( Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner); Stream<T>#collect •accumulator 和 combiner 都是 consumer
  26. 26. ArrayList<String> asList = stringStream.collect( ArrayList::new, ArrayList::add, ArrayList::addAll); Stream<T>#collect
  27. 27. <R> R collect( Collector<? super T,A,R> collector); Stream<T>#collect •Collector 並不是很容易實作
  28. 28. •所以 Collectors 提供了許多預設的方法提供 常用的 Collector •toList, groupingBy, toSet, toMap, counting stream.collect(Collectors.toList()); stream.collect(Collectors
 .groupingBy(…)); strStream.collect(Collectors
 .join(","));
  29. 29. ???
  30. 30. ????
  31. 31. ! List<Item> items = new ArrayList<>…; Map<String, List<Item>> result = 
 items.stream().collect(groupingBy(
 Item::getOwner)); ! >> {owner1=[i1, i2], owner2=[i3],…}
  32. 32. ! List<Item> items = new ArrayList<>…; Map<String, Set<String>> result = 
 items.stream().collect(groupingBy(
 Item::getOwner,
 toSet()));
 
 >> {owner1=[i1], owner2=[i3], …}
  33. 33. List<Item> items = new ArrayList<>…; Map<String, Set<String>> result = 
 items.stream().collect(groupingBy(
 Item::getOwner,
 mapping(Item::getName(),
 toSet()))); ! >> {owner1=[name1], owner2=[name2],…}
  34. 34. Lazy Evaluation List<Integer> items = 
 Arrays.asList(1, 2, 3, 4); items.stream() .filter( i -> { sout("A"+i); i % 2 == 0; }) .map( i -> { sout("B"+i); return i; }) .map( i -> { sout("C"+i); return i; }); •不會印出東西
  35. 35. Lazy Evaluation List<Integer> items = 
 Arrays.asList(1, 2, 3, 4); items.stream() .filter( i -> { sout("A"+i); i % 2 == 0; }) .map( i -> { sout("B"+i); return i; }) .map( i -> { sout("C"+i); return i; }) .collect(toList());
  36. 36. Lazy Evaluation List<Integer> items = 
 Arrays.asList(1, 2, 3, 4); items.stream() .filter( i -> { sout("A"+i); i % 2 == 0; }) .map( i -> { sout("B"+i); return i; }) .map( i -> { sout("C"+i); return i; }) .collect(toList()); >> A1, A2, B2, C2, A3, A4, B4, C4
  37. 37. Parallel List<Integer> items = 
 Arrays.asList(1, 2, 3, 4); items.parallelStream() .filter( i -> { sout("A"+i); return i % 2 == 0; }) .map( i -> { sout("B"+i); return i; }) .map( i -> { sout("C"+i); return i; }) .collect(toList()); >> A1, A3, A4, A2, B4, B2, C2, C4
  38. 38. Short-circuit Operation •Intermediate operation •能將無限的輸入轉換成有限的 stream •limit long sum = IntStream .iterate(1, n -> n+1) .limit(10) .sum();
  39. 39. Short-circuit Operation •Terminal operation •能將無限的輸入, 在有限時間內結束 •findFirst, anyMatch, allMatch long sum = IntStream .iterate(1, n -> n+1) .filter(i -> i > 100) .findFirst();
  40. 40. Example 1 •找出第一個 age > 20 的 Student for (Student student : students) { if (student.age > 20) { return student; } } }
  41. 41. Optional<Student> student = students.stream() .filter(s -> s.age > 20) .findFirst();
  42. 42. Example 2 •尋找 age > 20,成績 > 90 的 10 位 Student 的 id
  43. 43. List<String> result = …; for (Student student : students) { if (student.age > 20 && student.grade > 90) { result.add(student.id); if(result.size() >= 10){ break; } } }
  44. 44. List<String> result = students.stream() .filter(student -> student.age > 20) .filter(student -> student.grade > 90) .limit(10) .map(student -> student.id) .collect(Collectors.toList());
  45. 45. Example 3 •找出所有 Article 評論大於 20 的 Category,並依照名稱排序。
  46. 46. List<Category> result = …; for (Category category : categories) { for (Article a : c.getArticles()) { if (a.getCommentCount() >= 20) { result.add(c); break; } } } Collections.sort(result, 
 Comparator.comparing(c -> c.getName()));
  47. 47. categories.stream() .filter(c -> c.getArticles() .stream() .anyMatch(a -> a.getCommentCount() >= 20)) .sorted(Comparator.comparing(c -> c.getName())) .collect(Collectors.toList());
  48. 48. Example 4 •將 Person 依照年齡分類到不同 List •Map<Integer, List< Person >>
  49. 49. Map<Integer, List<Person>> result = …; for (Person person : people) { result.computeIfAbsent(person.age, t -> new ArrayList<>()) .add(person); }
  50. 50. Map<Integer,List<Person>> peopleByAge = people.stream() .collect(groupingBy(Person::getAge));
  51. 51. •Stream 走查很容易跟預期的不同,所以操作 過程要避免產生副作用 •順序,走了 些元素,是否並行 注意
  52. 52. ArrayList<String> results = new 
 ArrayList<>(); stream.filter(…)
 .forEach(s -> results.add(s)); List<String>results = stream
 .filter(…)
 .collect(Collectors.toList()); Side Effects
  53. 53. Set<Integer> seen = 
 Collections.synchronizedSet(
 new HashSet<>()); stream.parallel().map(e -> { 
 if (seen.add(e)) return 0; 
 else return e; 
 })... Stateless Behaviors
  54. 54. List<String> source = ...; source.stream()
 .filter(s -> {
 source.add(...);
 return s.length() > 10; 
 })
 ... Non-inferences •走查過程中不該動到來源資料結構 •除非該資料結構是支援 concurrent 存取
  55. 55. •使用 Stream 的方式和外部迭代差異不小 •很多方法只看參數型態很難理解,從方法和 變數名稱去理解使用方式 •只能多寫多習慣 •通常寫完後會有較佳的可讀性 最後

×