Assembled by GimunLee (2020-01-06)
- java 8 Streamμ λν΄ μ€λͺ ν μ μλ€.
java 8 μμ μΆκ°ν Streamμ λλ€λ₯Ό νμ©ν μ μλ κΈ°μ μ€ νλμ
λλ€. Java 8 μ΄μ μλ λ°°μ΄ λλ 컬λ μ
μΈμ€ν΄μ€λ₯Ό λ€λ£¨λ λ°©λ²μ for
λλ foreach
λ¬Έμ λλ©΄μ μμ νλμ©μ κΊΌλ΄μ λ€λ£¨λ λ°©λ²μ΄μμ΅λλ€. κ°λ¨ν κ²½μ°λΌλ©΄ μκ΄μμ§λ§ λ‘μ§μ΄ 볡μ‘ν΄μ§μλ‘ μ½λμ μμ΄ λ§μμ Έ μ¬λ¬ λ‘μ§μ΄ μμ΄κ² λκ³ , λ©μλλ₯Ό λλ κ²½μ° λ£¨νλ₯Ό μ¬λ¬ λ² λλ κ²½μ°κ° λ°μν©λλ€.
Streamμ λ°μ΄ν°μ νλ¦ μ λλ€. λ°°μ΄ λλ 컬λ μ μΈμ€ν΄μ€μ ν¨μ μ¬λ¬ κ°λ₯Ό μ‘°ν©ν΄μ μνλ κ²°κ³Όλ₯Ό νν°λ§νκ³ κ°κ³΅λ κ²°κ³Όλ₯Ό μ»μ μ μμ΅λλ€. λν λλ€λ₯Ό μ΄μ©ν΄μ μ½λμ μμ μ€μ΄κ³ κ°κ²°νκ² ννν μ μμ΅λλ€. μ¦, λ°°μ΄κ³Ό 컬λ μ μ ν¨μνμΌλ‘ μ²λ¦¬ν μ μμ΅λλ€.
λ νλμ μ₯μ μ κ°λ¨νκ² λ³λ ¬μ²λ¦¬(multi-threading) κ° κ°λ₯νλ€λ μ μ λλ€. νλμ μμ μ λ μ΄μμ μμ μΌλ‘ μκ² λλ μ λμμ μ§ννλ κ²μ λ³λ ¬ μ²λ¦¬(parallel processing)λΌκ³ ν©λλ€. μ¦ μ°λ λλ₯Ό μ΄μ©ν΄ λ§μ μμλ€μ λΉ λ₯΄κ² μ²λ¦¬ν μ μμ΅λλ€.
Streamμ λν λ΄μ©μ ν¬κ² μΈ κ°μ§λ‘ λλ μ μμ΅λλ€.
- μμ±νκΈ° : Stream instance μμ±
- κ°κ³΅νκΈ° : νν°λ§(filtering) λ° λ§΅ν(mapping) λ± μνλ κ²°κ³Όλ₯Ό λ§λ€μ΄κ°λ μ€κ° μμ (intermediate operations)
- κ²°κ³Ό λ§λ€κΈ° : μ΅μ’ μ μΌλ‘ κ²°κ³Όλ₯Ό λ§λ€μ΄λ΄λ μμ (terminal operations)
λ³΄ν΅ λ°°μ΄κ³Ό 컬λ μ μ μ΄μ©ν΄μ μ€νΈλ¦Όμ λ§λ€μ§λ§ μ΄ μΈμλ λ€μν λ°©λ²μΌλ‘ μ€νΈλ¦Όμ λ§λ€ μ μμ΅λλ€.
μ€νΈλ¦Όμ μ΄μ©νκΈ° μν΄μλ λ¨Όμ μμ±μ ν΄μΌ ν©λλ€. μ€νΈλ¦Όμ λ°°μ΄ λλ 컬λ μ
μΈμ€ν΄μ€λ₯Ό μ΄μ©ν΄μ μμ±ν μ μμ΅λλ€. λ°°μ΄μ λ€μκ³Ό κ°μ΄ Arrays.stream
λ©μλλ₯Ό μ¬μ©ν©λλ€.
String[] arr = new String[]{"a", "b", "c"};
Stream<String> stream = Arrays.stream(arr);
Stream<String> streamOfArrayPart = Arrays.stream(arr, 1, 3); // 1~2 μμ [b, c]
컬λ μ
νμ
(Collection, List, Set)μ κ²½μ° μΈν°νμ΄μ€μ μΆκ°λ Default Method Stream
μ μ΄μ©ν΄μ μ€νΈλ¦Όμ λ§λ€ μ μμ΅λλ€.
public interface Collection<E> extends Iterable<E> {
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
// ...
}
κ·Έλ¬λ©΄ λ€μκ³Ό κ°μ΄ μμ±ν μ μμ΅λλ€.
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
Stream<String> parallelStream = list.parallelStream(); // λ³λ ¬ μ²λ¦¬ μ€νΈλ¦Ό
λΉμ΄ μλ μ€νΈλ¦Ό(empty stream)λ μμ±ν μ μμ΅λλ€. λΉ μ€νΈλ¦Όμ μμκ° μμ λ null
λμ μ¬μ©ν μ μμ΅λλ€.
public Stream<String> streamOf(List<String> list) {
return list == null || list.isEmpty()
? Stream.empty()
: list.stream();
}
λΉλ(Builder)λ₯Ό μ¬μ©νλ©΄ μ€νΈλ¦Όμ μ§μ μ μΌλ‘ μνλ κ° μ λ£μ μ μμ΅λλ€. λ§μ§λ§μ build
λ©μλλ‘ μ€νΈλ¦Όμ 리ν΄ν©λλ€.
Stream<String> builderStream = Stream.<String>builder()
.add("str1").add("str2").add("str3")
.build();
generate
λ©μλλ₯Ό μ΄μ©νλ©΄ Supplier<T>
μ ν΄λΉνλ λλ€λ‘ κ°μ λ£μ μ μμ΅λλ€. Supplier
λ μΈμλ μκ³ λ¦¬ν΄κ°λ§ μλ ν¨μν μΈν°νμ΄μ€ μ
λλ€. λλ€μμ 리ν΄νλ κ°μ΄ λ€μ΄κ°λλ€.
public static <T> Stream<T> generate(Supplier<T> s) { ... }
μ΄ λ μμ±λλ μ€νΈλ¦Όμ ν¬κΈ°κ° μ ν΄μ Έμμ§ μκ³ λ¬΄ν(infinite)νκΈ° λλ¬Έμ νΉμ μ¬μ΄μ¦λ‘ μ΅λ ν¬κΈ°λ₯Ό μ νν΄μΌ ν©λλ€.
Stream<String> generatedStream = Stream.generate(() -> "gen").limit(5);
5κ°μ βgenβ μ΄ λ€μ΄κ° μ€νΈλ¦Όμ΄ μμ±λ©λλ€.
iterate
λ©μλλ₯Ό μ΄μ©νλ©΄ μ΄κΈ°κ°κ³Ό ν΄λΉ κ°μ λ€λ£¨λ λλ€λ₯Ό μ΄μ©ν΄μ μ€νΈλ¦Όμ λ€μ΄κ° μμλ₯Ό λ§λλλ€. λ€μ μμ μμλ 30μ΄ μ΄κΈ°κ°μ΄κ³ κ°μ΄ 2μ© μ¦κ°νλ κ°λ€μ΄ λ€μ΄κ°κ² λ©λλ€. μ¦ μμκ° λ€μ μμμ μΈνμΌλ‘ λ€μ΄κ°λλ€. μ΄ λ°©λ²λ μ€νΈλ¦Όμ μ¬μ΄μ¦κ° 무ννκΈ° λλ¬Έμ νΉμ μ¬μ΄μ¦λ‘ μ νν΄μΌ ν©λλ€.
Stream<Integer> iteratedStream =
Stream.iterate(30, n -> n + 2).limit(5); // [30, 32, 34, 36, 38]
λ¬Όλ‘ μ λ€λ¦μ μ¬μ©νλ©΄ 리μ€νΈλ λ°°μ΄μ μ΄μ©ν΄μ κΈ°λ³Έ νμ
(int, long, double) μ€νΈλ¦Όμ μμ±ν μ μμ΅λλ€. νμ§λ§ μ λ€λ¦μ μ¬μ©νμ§ μκ³ μ§μ μ μΌλ‘ ν΄λΉ νμ
μ μ€νΈλ¦Όμ λ€λ£° μλ μμ΅λλ€. range
μ rangeClosed
λ λ²μμ μ°¨μ΄μ
λλ€. λ λ²μ§Έ μΈμμΈ μ’
λ£μ§μ μ΄ ν¬ν¨λλλ μλλλμ μ°¨μ΄μ
λλ€.
IntStream intStream = IntStream.range(1, 5); // [1, 2, 3, 4]
LongStream longStream = LongStream.rangeClosed(1, 5); // [1, 2, 3, 4, 5]
μ λ€λ¦μ μ¬μ©νμ§ μκΈ° λλ¬Έμ λΆνμν μ€ν λ°μ±(auto-boxing)μ΄ μΌμ΄λμ§ μμ΅λλ€. νμν κ²½μ° boxed
λ©μλλ₯Ό μ΄μ©ν΄μ λ°μ±(boxing)ν μ μμ΅λλ€.
Stream<Integer> boxedIntStream = IntStream.range(1, 5).boxed();
Java 8 μ Random
ν΄λμ€λ λμλ₯Ό κ°μ§κ³ μΈ κ°μ§ νμ
μ μ€νΈλ¦Ό(IntStream, LongStream, DoubleStream)μ λ§λ€μ΄λΌ μ μμ΅λλ€. μ½κ² λμ μ€νΈλ¦Όμ μμ±ν΄μ μ¬λ¬κ°μ§ νμ μμ
μ μ·¨ν μ μμ΄ μ μ©ν©λλ€.
μ€νΈλ§μ μ΄μ©ν΄μ μ€νΈλ¦Όμ μμ±ν μλ μμ΅λλ€. λ€μμ μ€νΈλ§μ κ° λ¬Έμ(char)λ₯Ό IntStream
μΌλ‘ λ³νν μμ μ
λλ€. char
λ λ¬Έμμ΄μ§λ§ λ³Έμ§μ μΌλ‘λ μ«μμ΄κΈ° λλ¬Έμ κ°λ₯ν©λλ€.
IntStream charsStream =
"Stream".chars(); // [83, 116, 114, 101, 97, 109]
λ€μμ μ κ·ννμ(RegEx)μ μ΄μ©ν΄μ λ¬Έμμ΄μ μλ₯΄κ³ , κ° μμλ€λ‘ μ€νΈλ¦Όμ λ§λ μμ μ λλ€.
Stream<String> stringStream =
Pattern.compile(", ").splitAsStream("str1, str2, str3");
μλ° NIOμ Files
ν΄λμ€μ lines
λ©μλλ ν΄λΉ νμΌμ κ° λΌμΈμ μ€νΈλ§ νμ
μ μ€νΈλ¦ΌμΌλ‘ λ§λ€μ΄μ€λλ€.
Stream<String> lineStream = Files.lines(Paths.get("file.txt"), Charset.forName("UTF-8"));
μ€νΈλ¦Ό μμ± μ μ¬μ©νλ stream
λμ parallelStream
λ©μλλ₯Ό μ¬μ©ν΄μ λ³λ ¬ μ€νΈλ¦Όμ μ½κ² μμ±ν μ μμ΅λλ€. λ΄λΆμ μΌλ‘λ μ°λ λλ₯Ό μ²λ¦¬νκΈ° μν΄ μλ° 7λΆν° λμ
λ Fork/Join framework λ₯Ό μ¬μ©ν©λλ€.
// λ³λ ¬ μ€νΈλ¦Ό μμ±
Stream<Product> parallelStream = productList.parallelStream();
// λ³λ ¬ μ¬λΆ νμΈ
boolean isParallel = parallelStream.isParallel();
λ°λΌμ λ€μ μ½λλ κ° μμ μ μ°λ λλ₯Ό μ΄μ©ν΄ λ³λ ¬ μ²λ¦¬λ©λλ€.
boolean isMany = parallelStream
.map(product -> product.getAmount() * 10)
.anyMatch(amount -> amount > 200);
λ€μμ λ°°μ΄μ μ΄μ©ν΄μ λ³λ ¬ μ€νΈλ¦Όμ μμ±νλ κ²½μ°μ λλ€.
Arrays.stream(arr).parallel();
컬λ μ
κ³Ό λ°°μ΄μ΄ μλ κ²½μ°λ λ€μκ³Ό κ°μ΄ parallel
λ©μλλ₯Ό μ΄μ©ν΄μ μ²λ¦¬ν©λλ€.
IntStream intStream = IntStream.range(1, 150).parallel();
boolean isParallel = intStream.isParallel();
λ€μ μνμ
(sequential) λͺ¨λλ‘ λλ¦¬κ³ μΆλ€λ©΄ λ€μμ²λΌ sequential
λ©μλλ₯Ό μ¬μ©ν©λλ€. λ€μμ νλ² λ λ€λ£¨κ² μ§λ§ λ°λμ λ³λ ¬ μ€νΈλ¦Όμ΄ μ’μ κ²μ μλλλ€.
IntStream intStream = intStream.sequential();
boolean isParallel = intStream.isParallel();
Stream.concat
λ©μλλ₯Ό μ΄μ©ν΄ λ κ°μ μ€νΈλ¦Όμ μ°κ²°ν΄μ μλ‘μ΄ μ€νΈλ¦Όμ λ§λ€μ΄λΌ μ μμ΅λλ€.
Stream<String> stream1 = Stream.of("Java", "Scala", "Groovy");
Stream<String> stream2 = Stream.of("Python", "Go", "Swift");
Stream<String> concat = Stream.concat(stream1, stream2);
// [Java, Scala, Groovy, Python, Go, Swift]
μ 체 μμ μ€μμ λ€μκ³Ό κ°μ API λ₯Ό μ΄μ©ν΄μ λ΄κ° μνλ κ²λ§ λ½μλΌ μ μμ΅λλ€. μ΄λ¬ν κ°κ³΅ λ¨κ³λ₯Ό μ€κ° μμ (intermediate operations)μ΄λΌκ³ νλλ°, μ΄λ¬ν μμ μ μ€νΈλ¦Όμ 리ν΄νκΈ° λλ¬Έμ μ¬λ¬ μμ μ μ΄μ΄ λΆμ¬μ(chaining) μμ±ν μ μμ΅λλ€.
List<String> names = Arrays.asList("str1", "str2", "java");
μλ λμ€λ μμ μ½λλ μμ κ°μ 리μ€νΈλ₯Ό λμμΌλ‘ ν©λλ€.
νν°(filter)μ μ€νΈλ¦Ό λ΄ μμλ€μ νλμ© νκ°ν΄μ κ±Έλ¬λ΄λ μμ μ λλ€. μΈμλ‘ λ°λ Predicate λ boolean μ 리ν΄νλ ν¨μν μΈν°νμ΄μ€λ‘ νκ°μμ΄ λ€μ΄κ°κ² λ©λλ€.
Stream<T> filter(Predicate<? super T> predicate);
κ°λ¨ν μμ μ λλ€.
Stream<String> stream = names.stream()
.filter(name -> name.contains("t")); // ["str1", "str2"]
μ€νΈλ¦Όμ κ° μμμ λν΄μ νκ°μμ μ€ννκ² λκ³ βtβ κ° λ€μ΄κ° μ΄λ¦λ§ λ€μ΄κ° μ€νΈλ¦Όμ΄ 리ν΄λ©λλ€.
맡(map)μ μ€νΈλ¦Ό λ΄ μμλ€μ νλμ© νΉμ κ°μΌλ‘ λ³νν΄μ€λλ€. μ΄ λ κ°μ λ³ννκΈ° μν λλ€λ₯Ό μΈμλ‘ λ°μ΅λλ€.
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
μ€νΈλ¦Όμ λ€μ΄κ° μλ κ°μ΄ input μ΄ λμ΄μ νΉμ λ‘μ§μ κ±°μΉ ν output μ΄ λμ΄ (리ν΄λλ) μλ‘μ΄ μ€νΈλ¦Όμ λ΄κΈ°κ² λ©λλ€. μ΄λ¬ν μμ μ 맡ν(mapping)μ΄λΌκ³ ν©λλ€.
κ°λ¨ν μμ μ
λλ€. μ€νΈλ¦Ό λ΄ String μ toUpperCase
λ©μλλ₯Ό μ€νν΄μ λλ¬Έμλ‘ λ³νν κ°λ€μ΄ λ΄κΈ΄ μ€νΈλ¦Όμ 리ν΄ν©λλ€.
Stream<String> stream = names.stream()
.map(String::toUpperCase); // ["STR1", "STR2", "STR3"]
λ€μμ²λΌ μμ λ΄ λ€μ΄μλ Product κ°μ²΄μ μλμ κΊΌλ΄μ¬ μλ μμ΅λλ€. κ° βμνβμ βμνμ μλβμΌλ‘ 맡ννλκ±°μ£ .
Stream<Integer> stream = productList.stream()
.map(Product::getAmount); // [23, 14, 13, 23, 13]
map
μ΄μΈμλ μ‘°κΈ λ 볡μ‘ν flatMap
λ©μλλ μμ΅λλ€.
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
μΈμλ‘ mapper
λ₯Ό λ°κ³ μλλ°, λ¦¬ν΄ νμ
μ΄ Stream μ
λλ€. μ¦, μλ‘μ΄ μ€νΈλ¦Όμ μμ±ν΄μ 리ν΄νλ λλ€λ₯Ό λ겨μΌν©λλ€. flatMap
μ μ€μ²© ꡬ쑰λ₯Ό ν λ¨κ³ μ κ±°νκ³ λ¨μΌ 컬λ μ
μΌλ‘ λ§λ€μ΄μ£Όλ μν μ ν©λλ€. μ΄λ¬ν μμ
μ νλνΈλ(flattening) μ΄λΌκ³ ν©λλ€.
λ€μκ³Ό κ°μ μ€μ²©λ 리μ€νΈκ° μμ΅λλ€.
List<List<String>> list =
Arrays.asList(Arrays.asList("a"),
Arrays.asList("b")); // [["a"], ["b"]]
μ΄λ₯Ό flatMap
μ μ¬μ©ν΄μ μ€μ²© ꡬ쑰λ₯Ό μ κ±°ν ν μμ
ν μ μμ΅λλ€.
List<String> flatList =
list.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList()); // [a, b]
μ΄λ²μ κ°μ²΄μ μ μ©ν΄λ³΄κ² μ΅λλ€.
students.stream()
.flatMapToInt(student ->
IntStream.of(student.getKor(),
student.getEng(),
student.getMath()))
.average().ifPresent(avg ->
System.out.println(Math.round(avg * 10)/10.0));
μ μμ μμλ νμ κ°μ²΄λ₯Ό κ°μ§ μ€νΈλ¦Όμμ νμμ κ΅μμ μ μλ₯Ό λ½μ μλ‘μ΄ μ€νΈλ¦Όμ λ§λ€μ΄ νκ· μ ꡬνλ μ½λμ
λλ€. μ΄λ map
λ©μλ μ체λ§μΌλ‘λ νλ²μ ν μ μλ κΈ°λ₯μ
λλ€.
μ λ ¬μ λ°©λ²μ λ€λ₯Έ μ λ ¬κ³Ό λ§μ°¬κ°μ§λ‘ Comparator λ₯Ό μ΄μ©ν©λλ€.
Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
μΈμ μμ΄ κ·Έλ₯ νΈμΆν κ²½μ° μ€λ¦μ°¨μμΌλ‘ μ λ ¬ν©λλ€.
IntStream.of(14, 11, 20, 39, 23)
.sorted()
.boxed()
.collect(Collectors.toList()); // [11, 14, 20, 23, 39]
μΈμλ₯Ό λκΈ°λ κ²½μ°μ λΉκ΅ν΄λ³΄κ² μ΅λλ€. μ€νΈλ§ 리μ€νΈμμ μνλ²³ μμΌλ‘ μ λ ¬ν μ½λμ Comparator λ₯Ό λ겨μ μμμΌλ‘ μ λ ¬ν μ½λμ λλ€.
List<String> lang =
Arrays.asList("Java", "Scala", "Groovy", "Python", "Go", "Swift");
lang.stream()
.sorted()
.collect(Collectors.toList());
// [Go, Groovy, Java, Python, Scala, Swift]
lang.stream()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
// [Swift, Scala, Python, Java, Groovy, Go]
Comparator μ compare
λ©μλλ λ μΈμλ₯Ό λΉκ΅ν΄μ κ°μ 리ν΄ν©λλ€.
int compare(T o1, T o2)
κΈ°λ³Έμ μΌλ‘ Comparator μ¬μ©λ²κ³Ό λμΌν©λλ€. μ΄λ₯Ό μ΄μ©ν΄μ λ¬Έμμ΄ κΈΈμ΄λ₯Ό κΈ°μ€μΌλ‘ μ λ ¬ν΄λ³΄κ² μ΅λλ€.
lang.stream()
.sorted(Comparator.comparingInt(String::length))
.collect(Collectors.toList());
// [Go, Java, Scala, Swift, Groovy, Python]
lang.stream()
.sorted((s1, s2) -> s2.length() - s1.length())
.collect(Collectors.toList());
// [Groovy, Python, Scala, Swift, Java, Go]
μ€νΈλ¦Ό λ΄ μμλ€ κ°κ°μ λμμΌλ‘ νΉμ μ°μ°μ μννλ λ©μλλ‘λ peek
μ΄ μμ΅λλ€. βpeekβ μ κ·Έλ₯ νμΈν΄λ³Έλ€λ λ¨μ΄ λ»μ²λΌ νΉμ κ²°κ³Όλ₯Ό λ°ννμ§ μλ ν¨μν μΈν°νμ΄μ€ Consumer λ₯Ό μΈμλ‘ λ°μ΅λλ€.
Stream<T> peek(Consumer<? super T> action);
λ°λΌμ μ€νΈλ¦Ό λ΄ μμλ€ κ°κ°μ νΉμ μμ μ μνν λΏ κ²°κ³Όμ μν₯μ λ―ΈμΉμ§ μμ΅λλ€. λ€μμ²λΌ μμ μ μ²λ¦¬νλ μ€κ°μ κ²°κ³Όλ₯Ό νμΈν΄λ³Ό λ μ¬μ©ν μ μμ΅λλ€.
int sum = IntStream.of(1, 3, 5, 7, 9)
.peek(System.out::println)
.sum();
κ°κ³΅ν μ€νΈλ¦Όμ κ°μ§κ³ λ΄κ° μ¬μ©ν κ²°κ³Όκ°μΌλ‘ λ§λ€μ΄λ΄λ λ¨κ³μ λλ€. λ°λΌμ μ€νΈλ¦Όμ λλ΄λ μ΅μ’ μμ (terminal operations)μ λλ€.
μ€νΈλ¦Ό API λ λ€μν μ’ λ£ μμ μ μ 곡ν©λλ€. μ΅μ, μ΅λ, ν©, νκ· λ± κΈ°λ³Έν νμ μΌλ‘ κ²°κ³Όλ₯Ό λ§λ€μ΄λΌ μ μμ΅λλ€.
long count = IntStream.of(1, 3, 5, 7, 9).count();
long sum = LongStream.of(1, 3, 5, 7, 9).sum();
λ§μ½ μ€νΈλ¦Όμ΄ λΉμ΄ μλ κ²½μ° count
μ sum
μ 0μ μΆλ ₯νλ©΄ λ©λλ€. νμ§λ§ νκ· , μ΅μ, μ΅λμ κ²½μ°μλ ννν μκ° μκΈ° λλ¬Έμ Optionalμ μ΄μ©ν΄ 리ν΄ν©λλ€.
OptionalInt min = IntStream.of(1, 3, 5, 7, 9).min();
OptionalInt max = IntStream.of(1, 3, 5, 7, 9).max();
μ€νΈλ¦Όμμ λ°λ‘ ifPresent
λ©μλλ₯Ό μ΄μ©ν΄μ Optional μ μ²λ¦¬ν μ μμ΅λλ€.
DoubleStream.of(1.1, 2.2, 3.3, 4.4, 5.5)
.average()
.ifPresent(System.out::println);
μ΄ μΈμλ μ¬μ©μκ° μνλλλ‘ κ²°κ³Όλ₯Ό λ§λ€μ΄λ΄κΈ° μν΄ reduce
μ collect
λ©μλλ₯Ό μ 곡ν©λλ€. μ΄ λ κ°μ§ λ©μλλ₯Ό μ’ λ μμλ³΄κ² μ΅λλ€.
μ€νΈλ¦Όμ reduce
λΌλ λ©μλλ₯Ό μ΄μ©ν΄μ κ²°κ³Όλ₯Ό λ§λ€μ΄λ
λλ€. λ€μμ reduce
λ©μλλ μ΄ μΈ κ°μ§μ νλΌλ―Έν°λ₯Ό λ°μ μ μμ΅λλ€.
- accumulator : κ° μμλ₯Ό μ²λ¦¬νλ κ³μ° λ‘μ§. κ° μμκ° μ¬ λλ§λ€ μ€κ° κ²°κ³Όλ₯Ό μμ±νλ λ‘μ§
- identity : κ³μ°μ μν μ΄κΈ°κ°μΌλ‘ μ€νΈλ¦Όμ΄ λΉμ΄μ κ³μ°ν λ΄μ©μ΄ μλλΌλ μ΄ κ°μ 리ν΄
- combiner : λ³λ ¬(parallel) μ€νΈλ¦Όμμ λλ κ³μ°ν κ²°κ³Όλ₯Ό νλλ‘ ν©μΉλ λμνλ λ‘μ§
// 1κ° (accumulator)
Optional<T> reduce(BinaryOperator<T> accumulator);
// 2κ° (identity)
T reduce(T identity, BinaryOperator<T> accumulator);
// 3κ° (combiner)
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);
λ¨Όμ μΈμκ° νλλ§ μλ κ²½μ°μ
λλ€. μ¬κΈ°μ BinaryOperator
λ κ°μ νμ
μ μΈμ λ κ°λ₯Ό λ°μ κ°μ νμ
μ κ²°κ³Όλ₯Ό λ°ννλ ν¨μν μΈν°νμ΄μ€μ
λλ€. λ€μ μμ μμλ λ κ°μ λνλ λλ€λ₯Ό λκ²¨μ£Όκ³ μμ΅λλ€. λ°λΌμ κ²°κ³Όλ 6(1 + 2 + 3)μ΄ λ©λλ€.
OptionalInt reduced =
IntStream.range(1, 4) // [1, 2, 3]
.reduce((a, b) -> {
return Integer.sum(a, b);
});
μ΄λ²μ λ κ°μ μΈμλ₯Ό λ°λ κ²½μ°μ λλ€. μ¬κΈ°μ 10μ μ΄κΈ°κ°μ΄κ³ , μ€νΈλ¦Ό λ΄ κ°μ λν΄μ κ²°κ³Όλ 16(10 + 1 + 2 + 3)μ΄ λ©λλ€. μ¬κΈ°μ λλ€λ λ©μλ μ°Έμ‘°(method reference)λ₯Ό μ΄μ©ν΄μ λκΈΈ μ μμ΅λλ€.
int reducedTwoParams =
IntStream.range(1, 4) // [1, 2, 3]
.reduce(10, Integer::sum); // method reference
λ§μ§λ§μΌλ‘ μΈ κ°μ μΈμλ₯Ό λ°λ κ²½μ°μ λλ€. Combiner κ° νλ μν μ μ€λͺ λ§ λ΄€μ λλ μ μ΄ν΄κ° μκ° μ μλλ°μ, μ½λλ₯Ό νλ² μ΄ν΄λ΄ μλ€. κ·Έλ°λ° λ€μ μ½λλ₯Ό μ€νν΄λ³΄λ©΄ μ΄μνκ² λ§μ§λ§ μΈμμΈ combiner λ μ€νλμ§ μμ΅λλ€.
Integer reducedParams = Stream.of(1, 2, 3)
.reduce(10, // identity
Integer::sum, // accumulator
(a, b) -> {
System.out.println("combiner was called");
return a + b;
});
Combiner λ λ³λ ¬ μ²λ¦¬ μ κ°μ λ€λ₯Έ μ°λ λμμ μ€νν κ²°κ³Όλ₯Ό λ§μ§λ§μ ν©μΉλ λ¨κ³μ λλ€. λ°λΌμ λ³λ ¬ μ€νΈλ¦Όμμλ§ λμν©λλ€.
Integer reducedParallel = Arrays.asList(1, 2, 3)
.parallelStream()
.reduce(10,
Integer::sum,
(a, b) -> {
System.out.println("combiner was called");
return a + b;
});
κ²°κ³Όλ λ€μκ³Ό κ°μ΄ 36μ΄ λμ΅λλ€. λ¨Όμ accumulator λ μ΄ μΈ λ² λμν©λλ€. μ΄κΈ°κ° 10μ κ° μ€νΈλ¦Ό κ°μ λν μΈ κ°μ κ°(10 + 1 = 11, 10 + 2 = 12, 10 + 3 = 13)μ κ³μ°ν©λλ€. Combiner λ identity μ accumulator λ₯Ό κ°μ§κ³ μ¬λ¬ μ°λ λμμ λλ κ³μ°ν κ²°κ³Όλ₯Ό ν©μΉλ μν μ λλ€. 12 + 13 = 25, 25 + 11 = 36 μ΄λ κ² λ λ² νΈμΆλ©λλ€.
combiner was called
combiner was called
36
λ³λ ¬ μ€νΈλ¦Όμ΄ 무쑰건 μνμ λ³΄λ€ μ’μ κ²μ μλλλ€. μ€νλ € κ°λ¨ν κ²½μ°μλ μ΄λ κ² λΆκ°μ μΈ μ²λ¦¬κ° νμνκΈ° λλ¬Έμ μ€νλ € λ릴 μλ μμ΅λλ€.
collect
λ©μλλ λ λ€λ₯Έ μ’
λ£ μμ
μ
λλ€. Collector
νμ
μ μΈμλ₯Ό λ°μμ μ²λ¦¬λ₯Ό νλλ°μ, μμ£Ό μ¬μ©νλ μμ
μ Collectors
κ°μ²΄μμ μ 곡νκ³ μμ΅λλ€.
μ΄λ² μμ μμλ λ€μκ³Ό κ°μ κ°λ¨ν 리μ€νΈλ₯Ό μ¬μ©ν©λλ€. Product κ°μ²΄λ μλ(amout)κ³Ό μ΄λ¦(name)μ κ°μ§κ³ μμ΅λλ€.
List<Product> productList =
Arrays.asList(new Product(23, "potatoes"),
new Product(14, "orange"),
new Product(13, "lemon"),
new Product(23, "bread"),
new Product(13, "sugar"));
μ€νΈλ¦Όμμ μμ
ν κ²°κ³Όλ₯Ό λ΄μ 리μ€νΈλ‘ λ°νν©λλ€. λ€μ μμ μμλ map
μΌλ‘ κ° μμμ μ΄λ¦μ κ°μ Έμ¨ ν Collectors.toList
λ₯Ό μ΄μ©ν΄μ 리μ€νΈλ‘ κ²°κ³Όλ₯Ό κ°μ Έμ΅λλ€.
List<String> collectorCollection =
productList.stream()
.map(Product::getName)
.collect(Collectors.toList());
// [potatoes, orange, lemon, bread, sugar]
μ€νΈλ¦Όμμ μμ ν κ²°κ³Όλ₯Ό νλμ μ€νΈλ§μΌλ‘ μ΄μ΄ λΆμΌ μ μμ΅λλ€.
String listToString =
productList.stream()
.map(Product::getName)
.collect(Collectors.joining());
// potatoesorangelemonbreadsugar
Collectors.joining
μ μΈ κ°μ μΈμλ₯Ό λ°μ μ μμ΅λλ€. μ΄λ₯Ό μ΄μ©νλ©΄ κ°λ¨νκ² μ€νΈλ§μ μ‘°ν©ν μ μμ΅λλ€.
- delimiter : κ° μμ μ€κ°μ λ€μ΄κ° μμλ₯Ό ꡬλΆμμΌμ£Όλ ꡬλΆμ
- prefix : κ²°κ³Ό 맨 μμ λΆλ λ¬Έμ
- suffix : κ²°κ³Ό 맨 λ€μ λΆλ λ¬Έμ
String listToString =
productList.stream()
.map(Product::getName)
.collect(Collectors.joining(", ", "<", ">"));
// <potatoes, orange, lemon, bread, sugar>
μ«μ κ°(Integer value )μ νκ· (arithmetic mean)μ λ λλ€.
Double averageAmount =
productList.stream()
.collect(Collectors.averagingInt(Product::getAmount));
// 17.2
μ«μκ°μ ν©(sum)μ λ λλ€.
Integer summingAmount =
productList.stream()
.collect(Collectors.summingInt(Product::getAmount));
// 86
IntStream μΌλ‘ λ°κΏμ£Όλ mapToInt
λ©μλλ₯Ό μ¬μ©ν΄μ μ’ λ κ°λ¨νκ² ννν μ μμ΅λλ€.
Integer summingAmount =
productList.stream()
.mapToInt(Product::getAmount)
.sum(); // 86
λ§μ½ ν©κ³μ νκ· λͺ¨λ νμνλ€λ©΄ μ€νΈλ¦Όμ λ λ² μμ±ν΄μΌ ν κΉμ? μ΄λ° μ 보λ₯Ό νλ²μ μ»μ μ μλ λ°©λ²μΌλ‘λ summarizingInt
λ©μλκ° μμ΅λλ€.
IntSummaryStatistics statistics =
productList.stream()
.collect(Collectors.summarizingInt(Product::getAmount));
μ΄λ κ² λ°μμ¨ IntSummaryStatistics κ°μ²΄μλ λ€μκ³Ό κ°μ μ λ³΄κ° λ΄κ²¨ μμ΅λλ€.
IntSummaryStatistics {count=5, sum=86, min=13, average=17.200000, max=23}
- κ°μ getCount()
- ν©κ³ getSum()
- νκ· getAverage()
- μ΅μ getMin()
- μ΅λ getMax()
μ΄λ₯Ό μ΄μ©νλ©΄ collect
μ μ μ΄λ° ν΅κ³ μμ
μ μν map
μ νΈμΆν νμκ° μκ² λ©λλ€. μμμ μ΄ν΄λ³Έ averaging, summing, summarizing λ©μλλ κ° κΈ°λ³Έ νμ
(int, long, double)λ³λ‘ μ 곡λ©λλ€.
νΉμ 쑰건μΌλ‘ μμλ€μ κ·Έλ£Ήμ§μ μ μμ΅λλ€. μλμ κΈ°μ€μΌλ‘ κ·Έλ£Ήνν΄λ³΄κ² μ΅λλ€. μ¬κΈ°μ λ°λ μΈμλ ν¨μν μΈν°νμ΄μ€ Function μ λλ€.
Map<Integer, List<Product>> collectorMapOfLists =
productList.stream()
.collect(Collectors.groupingBy(Product::getAmount));
κ²°κ³Όλ Map νμ μΌλ‘ λμ€λλ°μ, κ°μ μλμ΄λ©΄ 리μ€νΈλ‘ λ¬Άμ΄μ 보μ¬μ€λλ€.
{23=[Product{amount=23, name='potatoes'},
Product{amount=23, name='bread'}],
13=[Product{amount=13, name='lemon'},
Product{amount=13, name='sugar'}],
14=[Product{amount=14, name='orange'}]}
μμ groupingBy
ν¨μν μΈν°νμ΄μ€ Function μ μ΄μ©ν΄μ νΉμ κ°μ κΈ°μ€μΌλ‘ μ€νΈλ¦Ό λ΄ μμλ€μ λ¬Άμλ€λ©΄, partitioningBy
μ ν¨μν μΈν°νμ΄μ€ Predicate λ₯Ό λ°μ΅λλ€. Predicate λ μΈμλ₯Ό λ°μμ boolean κ°μ 리ν΄ν©λλ€.
Map<Boolean, List<Product>> mapPartitioned =
productList.stream()
.collect(Collectors.partitioningBy(el -> el.getAmount() > 15));
λ°λΌμ νκ°λ₯Ό νλ ν¨μλ₯Ό ν΅ν΄μ μ€νΈλ¦Ό λ΄ μμλ€μ true μ false λ κ°μ§λ‘ λλ μ μμ΅λλ€.
{false=[Product{amount=14, name='orange'},
Product{amount=13, name='lemon'},
Product{amount=13, name='sugar'}],
true=[Product{amount=23, name='potatoes'},
Product{amount=23, name='bread'}]}
νΉμ νμ
μΌλ‘ κ²°κ³Όλ₯Ό collect
ν μ΄νμ μΆκ° μμ
μ΄ νμν κ²½μ°μ μ¬μ©ν μ μμ΅λλ€. μ΄ λ©μλμ μκ·Έλμ³λ λ€μκ³Ό κ°μ΅λλ€. finisher
κ° μΆκ°λ λͺ¨μμΈλ°, μ΄ νΌλμ
λ collect λ₯Ό ν νμ μ€νν μμ
μ μλ―Έν©λλ€.
public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(
Collector<T,A,R> downstream,
Function<R,RR> finisher) { ... }
λ€μ μμ λ Collectors.toSet
μ μ΄μ©ν΄μ κ²°κ³Όλ₯Ό Set μΌλ‘ collect ν ν μμ λΆκ°ν Set μΌλ‘ λ³ννλ μμ
μ μΆκ°λ‘ μ€ννλ μ½λμ
λλ€.
Set<Product> unmodifiableSet =
productList.stream()
.collect(Collectors.collectingAndThen(Collectors.toSet(), Collections::unmodifiableSet));
μ¬λ¬κ°μ§ μν©μμ μ¬μ©ν μ μλ λ©μλλ€μ μ΄ν΄λ΄€μ΅λλ€. μ΄ μΈμ νμν λ‘μ§μ΄ μλ€λ©΄ μ§μ collector λ₯Ό λ§λ€ μλ μμ΅λλ€. accumulator μ combiner λ reduce
μμ μ΄ν΄λ³Έ λ΄μ©κ³Ό λμΌν©λλ€.
public static<T, R> Collector<T, R, R> of(
Supplier<R> supplier, // new collector μμ±
BiConsumer<R, T> accumulator, // λ κ°μ κ°μ§κ³ κ³μ°
BinaryOperator<R> combiner, // κ³μ°ν κ²°κ³Όλ₯Ό μμ§νλ ν¨μ.
Characteristics... characteristics) { ... }
μ½λλ₯Ό 보μλ©΄ λ μ΄ν΄κ° μ¬μ°μ€ κ²λλ€. λ€μ μ½λμμλ collector λ₯Ό νλ μμ±ν©λλ€. 컬λ ν°λ₯Ό μμ±νλ supplier μ LinkedList μ μμ±μλ₯Ό λ겨μ€λλ€. κ·Έλ¦¬κ³ accumulator μλ 리μ€νΈμ μΆκ°νλ add
λ©μλλ₯Ό λκ²¨μ£Όκ³ μμ΅λλ€. λ°λΌμ μ΄ μ»¬λ ν°λ μ€νΈλ¦Όμ κ° μμμ λν΄μ LinkedList λ₯Ό λ§λ€κ³ μμλ₯Ό μΆκ°νκ² λ©λλ€. λ§μ§λ§μΌλ‘ combiner λ₯Ό μ΄μ©ν΄ κ²°κ³Όλ₯Ό μ‘°ν©νλλ°, μμ±λ 리μ€νΈλ€μ νλμ 리μ€νΈλ‘ ν©μΉκ³ μμ΅λλ€.
Collector<Product, ?, LinkedList<Product>> toLinkedList =
Collector.of(LinkedList::new,
LinkedList::add,
(first, second) -> {
first.addAll(second);
return first;
});
λ°λΌμ λ€μκ³Ό κ°μ΄ collect
λ©μλμ μ°λ¦¬κ° λ§λ 컀μ€ν
컬λ ν°λ₯Ό λκ²¨μ€ μ μκ³ , κ²°κ³Όκ° λ΄κΈ΄ LinkedList κ° λ°νλ©λλ€.
LinkedList<Product> linkedListOfPersons =
productList.stream()
.collect(toLinkedList);
맀μΉμ 쑰건μ λλ€ Predicate λ₯Ό λ°μμ ν΄λΉ 쑰건μ λ§μ‘±νλ μμκ° μλμ§ μ²΄ν¬ν κ²°κ³Όλ₯Ό 리ν΄ν©λλ€. λ€μκ³Ό κ°μ μΈ κ°μ§ λ©μλκ° μμ΅λλ€.
- νλλΌλ 쑰건μ λ§μ‘±νλ μμκ° μλμ§(anyMatch)
- λͺ¨λ 쑰건μ λ§μ‘±νλμ§(allMatch)
- λͺ¨λ 쑰건μ λ§μ‘±νμ§ μλμ§(noneMatch)
boolean anyMatch(Predicate<? super T> predicate);
boolean allMatch(Predicate<? super T> predicate);
boolean noneMatch(Predicate<? super T> predicate);
κ°λ¨ν μμ μ
λλ€. λ€μ λ§€μΉ κ²°κ³Όλ λͺ¨λ true
μ
λλ€.
List<String> names = Arrays.asList("str1", "str2", "str3");
boolean anyMatch = names.stream()
.anyMatch(name -> name.contains("s"));
boolean allMatch = names.stream()
.allMatch(name -> name.length() > 3);
boolean noneMatch = names.stream()
.noneMatch(name -> name.endsWith("2"));
foreach
λ μμλ₯Ό λλ©΄μ μ€νλλ μ΅μ’
μμ
μ
λλ€. λ³΄ν΅ System.out.println
λ©μλλ₯Ό λ겨μ κ²°κ³Όλ₯Ό μΆλ ₯ν λ μ¬μ©νκ³€ ν©λλ€. μμ μ΄ν΄λ³Έ peek
κ³Όλ μ€κ° μμ
κ³Ό μ΅μ’
μμ
μ μ°¨μ΄κ° μμ΅λλ€.
names.stream().forEach(System.out::println);