java8 stream

55
Java 8 Stream @kojilin 2014/3/29@TWJUG

Upload: koji-lin

Post on 10-Jun-2015

1.229 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Java8 stream

Java 8 Stream@kojilin

2014/3/29@TWJUG

Page 2: Java8 stream

Stream•java.util.stream •Support functional-style operations on

streams of elementsint sum = widgetList.stream() .filter(b -> b.color == RED) .mapToInt(b -> b.getWeight()) .sum();

Page 3: Java8 stream

External Iteration

long sum = 0;

for(int i = 0; i < N; ++i){

sum += i * i;

}

Page 4: Java8 stream

Internal Iteration

long sum = LongStream

.range(0, N)

.map(i -> i * i)

.sum();

Page 5: Java8 stream

•為什麼需要 Stream ?

•從外部迭代轉為內部迭代

•外部迭代需要注意要做什麼跟如何做

•內部迭代著重在要做什麼比如何做更多

•將行為資料化

•讓函式庫幫你更多的忙

•讓程式碼更簡潔好讀

Page 6: Java8 stream

Stream•Stream 不是資料結構,沒有儲存資料

•包裹在現有的資料結構之上

•透過內部迭代

•Multi-thread

•Laziness

•Stream 有 iterator 方法,但沒有實作 Iterable

•所以不能寫 for (String item : stream)

Page 7: Java8 stream

Stream 的種類•Stream<T>

•Primitive Streams

•IntStream

•DoubleStream

•LongStream

•彼此之間可以透過像 mapToInt, mapToDouble, mapToObj 等方式切換

Page 8: Java8 stream

Stream 的使用方式

long sum = LongStream

.range(0, N)

.map(i -> i * i)

.filter(i -> i % 2 == 0)

.sum();•建立 Stream

Page 9: Java8 stream

Stream 的使用方式

long sum = LongStream

.range(0, N)

.map(i -> i * i)

.filter(i -> i % 2 == 0)

.sum();•透過連續的 intermediate operation 操作

Page 10: Java8 stream

Stream 的使用方式

long sum = LongStream

.range(0, N)

.map(i -> i * i)

.filter(i -> i % 2 == 0)

.sum();•最後用 terminal operation 結束

Page 11: Java8 stream

如何取得 Stream?•Collection#stream() •Arrays.stream(Object[]) •Stream.of(Object[]) •IntStream.range(int, int) •BufferedReader#lines() •Stream#filter() •etc.

Page 12: Java8 stream

Intermediate Operation•回傳 Stream,可以接下一個 operation

•Lazy

•直到遇到 terminal operation 的方法前不會有效果

Page 13: Java8 stream

•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));

Page 14: Java8 stream

•limit

•distinct

•skip

stream.limit(5);

stream.distinct();

stream.skip(5);

Page 15: Java8 stream

Stateful & Stateless•Stateful 表示在操作元素時,除了當下的元

素外需要考慮其他狀態

•sorted, skip, limit

•Stateless 表示在操作元素時,除了當下的元素外不用考慮其他狀態

•filter, map

Page 16: Java8 stream

Terminal Operation•不會回傳 stream,整個 stream 只能有一個

terminal operation 呼叫

•呼叫後才會開始走查 stream,最後產生並回傳結果或是製造 side-effect

•呼叫完 terminal operation 後 stream 會被當作被使用完畢

•Stream -> (Intermediate)* -> Terminal

Page 17: Java8 stream

•forEach

•reduce

•collect

•count

stream.forEach( p -> print(p));

stream.reduce( 0, (a, b) -> a + b);

stream.collect( Collectors.toList());

stream.count();

Page 18: Java8 stream

•toArray

•max

•sum ( Primitive Stream )

•average ( Primitive Stream )

stream.toArray(String[]::new);

stream.max(String::compareTo);

intStream.sum();

intStream.average();

Page 19: Java8 stream

Reduction•將元素組合成一個結果

•reduce, collect, sum, max, count

intStream.sum();

numbers.stream().reduce(0, (x,y) -> x + y);

Page 20: Java8 stream

<U> U reduce(

U identity,

BiFunction<U, ? super T, U >

accumulator,

BinaryOperator<U> combiner);

Stream<T>#reduce

•accumulator 和 combiner 每次處理完元素後,必須回傳新的值

Page 21: Java8 stream

Integer result = stream.reduce(0,

(sum, b) -> sum + b.getWeight(),

Integer::sum);

Stream<T>#reduce

Page 22: Java8 stream

list.stream().reduce(

new ArrayList<>(),

(integers, o) -> {

integers.add(o);

return integers;

},

(integers, integers2) -> {

integers.addAll(integers2);

return integers;

}); ❌

Page 23: Java8 stream

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;

});△

Page 24: Java8 stream

Mutable Reduction•將元素累加到 mutable 的容器中

•Stream#collect

•和 reduce 不同,是改變已經存在的值

Page 25: Java8 stream

<R> R collect(

Supplier<R> supplier,

BiConsumer<R,? super T>

accumulator,

BiConsumer<R,R> combiner);

Stream<T>#collect

•accumulator 和 combiner 都是 consumer

Page 26: Java8 stream

ArrayList<String> asList =

stringStream.collect(

ArrayList::new,

ArrayList::add,

ArrayList::addAll);

Stream<T>#collect

Page 27: Java8 stream

<R> R collect(

Collector<? super T,A,R>

collector);

Stream<T>#collect

•Collector 並不是很容易實作

Page 28: Java8 stream

•所以 Collectors 提供了許多預設的方法提供常用的 Collector

•toList, groupingBy, toSet, toMap, counting

stream.collect(Collectors.toList());

stream.collect(Collectors .groupingBy(…));

strStream.collect(Collectors .join(","));

Page 29: Java8 stream

???

Page 30: Java8 stream

????

Page 31: Java8 stream

!

List<Item> items = new ArrayList<>…;

Map<String, List<Item>> result = items.stream().collect(groupingBy( Item::getOwner));

!

>> {owner1=[i1, i2], owner2=[i3],…}

Page 32: Java8 stream

!

List<Item> items = new ArrayList<>…;

Map<String, Set<String>> result = items.stream().collect(groupingBy( Item::getOwner, toSet()));>> {owner1=[i1], owner2=[i3], …}

Page 33: Java8 stream

List<Item> items = new ArrayList<>…;

Map<String, Set<String>> result = items.stream().collect(groupingBy( Item::getOwner, mapping(Item::getName(), toSet())));

!

>> {owner1=[name1], owner2=[name2],…}

Page 34: Java8 stream

Lazy EvaluationList<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; });

•不會印出東西

Page 35: Java8 stream

Lazy EvaluationList<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());

Page 36: Java8 stream

Lazy EvaluationList<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

Page 37: Java8 stream

ParallelList<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

Page 38: Java8 stream

Short-circuit Operation•Intermediate operation

•能將無限的輸入轉換成有限的 stream

•limitlong sum = IntStream

.iterate(1, n -> n+1)

.limit(10)

.sum();

Page 39: Java8 stream

Short-circuit Operation•Terminal operation

•能將無限的輸入, 在有限時間內結束

•findFirst, anyMatch, allMatchlong sum = IntStream

.iterate(1, n -> n+1)

.filter(i -> i > 100)

.findFirst();

Page 40: Java8 stream

Example 1•找出第一個 age > 20 的 Student

for (Student student : students) {

if (student.age > 20) {

return student;

}

}

}

Page 41: Java8 stream

Optional<Student> student = students.stream()

.filter(s -> s.age > 20)

.findFirst();

Page 42: Java8 stream

Example 2•尋找 age > 20,成績 > 90 的 10 位

Student 的 id

Page 43: Java8 stream

List<String> result = …;

for (Student student : students) {

if (student.age > 20 && student.grade > 90) {

result.add(student.id);

if(result.size() >= 10){

break;

}

}

}

Page 44: Java8 stream

List<String> result = students.stream()

.filter(student -> student.age > 20)

.filter(student -> student.grade > 90)

.limit(10)

.map(student -> student.id)

.collect(Collectors.toList());

Page 45: Java8 stream

Example 3•找出所有 Article 評論大於 20 的

Category,並依照名稱排序。

Page 46: Java8 stream

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()));

Page 47: Java8 stream

categories.stream()

.filter(c -> c.getArticles()

.stream()

.anyMatch(a -> a.getCommentCount() >= 20))

.sorted(Comparator.comparing(c -> c.getName()))

.collect(Collectors.toList());

Page 48: Java8 stream

Example 4•將 Person 依照年齡分類到不同 List

•Map<Integer, List< Person >>

Page 49: Java8 stream

Map<Integer, List<Person>> result = …;

for (Person person : people) {

result.computeIfAbsent(person.age,

t -> new ArrayList<>())

.add(person);

}

Page 50: Java8 stream

Map<Integer,List<Person>> peopleByAge =

people.stream()

.collect(groupingBy(Person::getAge));

Page 51: Java8 stream

•Stream 走查很容易跟預期的不同,所以操作過程要避免產生副作用

•順序,走了哪些元素,是否並行

注意

Page 52: Java8 stream

ArrayList<String> results = new ArrayList<>();

stream.filter(…) .forEach(s -> results.add(s));

List<String>results = stream .filter(…) .collect(Collectors.toList());

Side Effects

Page 53: Java8 stream

Set<Integer> seen = Collections.synchronizedSet( new HashSet<>());

stream.parallel().map(e -> { if (seen.add(e)) return 0; else return e; })...

Stateless Behaviors

Page 54: Java8 stream

List<String> source = ...;

source.stream() .filter(s -> { source.add(...); return s.length() > 10; }) ...

Non-inferences•走查過程中不該動到來源資料結構

•除非該資料結構是支援 concurrent 存取

Page 55: Java8 stream

•使用 Stream 的方式和外部迭代差異不小

•很多方法只看參數型態很難理解,從方法和變數名稱去理解使用方式

•只能多寫多習慣

•通常寫完後會有較佳的可讀性

最後