JavaSE8+
StreamStream
μ Java 8 μμ μ§μνλ Lambda
λ₯Ό μ΄μ©ν κΈ°μ μ΄λ€.
μ€νΈλ¦Όμ Array
νΉμ Collection Instance
λ₯Ό μ΄μ©νμ¬ μ¬μ©νλ©° κΈ°μ‘΄μ for
/ foreach
λ¬Έμ λλ©΄μ νλμ© κΊΌλ΄μ λ€λ£¨λ λ°©λ²μ μ½λμ μμ΄ λ§μμ Έ μ¬λ¬ λ‘μ§μ΄ μμ΄κ² λκ³ λ©μλλ₯Ό λλκ²½μ° λ£¨νλ₯Ό μ¬λ¬λ² λλ κ²½μ°κ° λ°μλ μ μλ€.
μ€νΈλ¦Όμ λ°μ΄ν°μ νλ¦μ΄λ©° Array
λλ Collection Instance
μ ν¨μ μ¬λ¬κ°λ₯Ό μ‘°ν©ν΄μ μνλ κ²°κ³Όλ₯Ό νν°λ§νκ³ κ°κ³΅λ κ²°κ³Όλ₯Ό μ»μμ μλ€.
κ°μ₯ ν° μ₯μ μ λ³λ ¬μ²λ¦¬ (multi-threading) μ΄ κ°λ₯νμ¬ νλμ μμ
μ λ μ΄μμ μμ
μΌλ‘ μκ² λλ λμμ μ²λ¦¬νλ λ³λ ¬μ²λ¦¬κ° κ°λ₯νλ€.
Thread λ₯Ό μ΄μ©νμ¬ λ§μ μμλ€μ λΉ λ₯΄κ² μ²λ¦¬ κ°λ₯νλ€.
Stream Lifecycle
μμ± νκΈ°
Stream Instance
λ₯Ό μμ±νλ κ³Όμ μΌλ‘ μ΅μ΄μ Array
νΉ Collction Instance
λ₯Ό μ΄μ©νμ¬ μμ±ν μ μλ€.
Array Stream
String[] arrayStr = new String[]{"a", "b", "c"};
Stream<String> stream = Arrays.stream(arrayStr);
stream.forEach(System.out::println); // [a, b, c]
String[] arrayStr = new String[]{"a", "b", "c"};
Stream<String> streamOfPart = Arrays.stream(arrayStr, 1, 3);
streamOfPart.forEach(System.out::println); // [b, c]
Collection Stream
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
Stream<String> parallelStream = list.parallelStream(); // λ³λ ¬μ²λ¦¬ μ€νΈλ¦Ό
κΈ°λ³Έ νμ ν μ€νΈλ¦Ό
IntStream intStream = InsStream.range(1, 5); // [1, 2, 3, 4]
LongStream intStream = LongStream.range(1, 5); // [1, 2, 3, 4, 5]
μ λ€λ¦μ μ¬μ©νμ§ μκΈ° λλ¬Έμ λΆνμν Auto-Boxing μ΄ μΌμ΄λμ§ μλλ€.
νμν κ²½μ° boxed
λ©μλλ₯Ό μ΄μ©νμ¬ λ°μ±νμ¬ μ¬μ©νλ€.
Stream<Integer> boxedIntStream = IntStream.range(1, 5).boxed();
λ¬Έμμ΄ μ€νΈλ¦Ό
κ° λ¬Έμλ₯Ό char
λ‘ IntStream μΌλ‘ λ³ννλ€.
IntStream charsStream = "Stream".chars(); //[83, 116, 114, 101, 97, 109]
μ κ·ννμμ μ΄μ©νμ¬ κ° μμλ€μ ꡬμ±νλ€.
Stream<String> regExStream = Pattern.compile(", ").splitAsStream("Eric, Elena, Java"); // [Eric, Elena, Java]
νμΌ μ€νΈλ¦Ό
ν΄λΉ νμΌμ κ° λΌμΈμ μ€νΈλ¦ΌμΌλ‘ λ§λ€μ΄μ€λ€.
Stream<String> lineStream = File.lines(Paths.get("file.txt"), Charset.forName("UTF-8"));
λ³λ ¬ μ€νΈλ¦Ό
μΌλ°μ μΌλ‘ μ€νΈλ¦Όμ μμ±νλ stream
λ©μλ λμ parallelStream
λ©μλλ₯Ό μ¬μ©νμ¬ λ³λ ¬ μ€νΈλ¦Όμ μ½κ² μμ±ν μ μλ€.
// λ³λ ¬ μ€νΈλ¦Ό μμ±
Stream<Product> parallelStream = productList.parallelStream();
// λ³λ ¬ μ¬λΆ νμΈ
boolean isParallel = parallelStream.isParallel();
μλ μ½λλ κ° μμ μ μ°λ λλ₯Ό μ΄μ©νμ¬ λ³λ ¬μ²λ¦¬ λ©λλ€.
boolean isMany = parallelStream
.map(product -> product.getAmount() * 10)
.anyMatch(amount -> amount > 200);
λ€μμ λ°°μ΄μ μ΄μ©νμ¬ λ³λ ¬ μ€νΈλ¦Όμ μμ±νλ κ²½μ°μ λλ€.
Arrays.stream(array).parallel();
컬λ μ
κ³Ό λ°°μ΄μ΄ μλ κ²½μ°μλ parallel
λ©μλλ₯Ό μ΄μ©ν΄ μ²λ¦¬νλ€.
IntStream intStream = IntStream.range(1, 150).parallel();
boolean isParallel = intStream.isParallel();
λ€μ μνμ
(Sequential) λͺ¨λλ‘ λλ¦¬κ³ μΆλ€λ©΄ sequential
λ©μλλ₯Ό μ¬μ©νλ€.
IntStream intStream = intStream.sequential();
boolean isParallel = intStream.isParallel();
κ·Έ μΈ λ°©λ²
λΉμ΄μλ μ€νΈλ¦Ό
Stream μμκ° μμλ null
λμ μ¬μ©
public Stream<String> streamOf(List<String> list) {
return (list == null || list.isEmpty())
? Stream.empty()
: list.stream()
}
Stream.builder()
λΉλ (Builder) λ₯Ό μ¬μ©νμ¬ μ€νΈλ¦Όμ μ§μ μ μΌλ‘ μνλ κ°μ λ£μμ μλ€.
λ§μ§λ§μΌλ‘ builder
λ©μλλ‘ μ€νΈλ¦Όμ 리ν΄νλ€.
Stream<String> builder = Stream.<String>builder()
.add("Eric").add("Elena").add("Java")
.build();
Stream.generate
μ€νΈλ¦Όμ ν¬κΈ°κ° μ§μ λμ§ μκ³ λ¬΄νν νμ₯νλ limit
μΌλ‘ ν¬κΈ°λ₯Ό μ νν΄μ€μΌ νλ€.
Stream<String> generatedStream = Stream.generate(() -> "gen").limit(5); // [gen, gen, gen, gen, gen]
Stream.iterate()
Stream<Integer> iteratedStream = Stream.iterate(30, n -> n + 2).limit(5); // [30, 32, 34, 36, 38]
μ€νΈλ¦Ό μ°κ²°νκΈ°
Stream.concat
λ κ°μ μ€νΈλ¦Όμ μ°κ²°ν΄μ μλ‘μ΄ μ€νΈλ¦Όμ μμ±νλ€.
Stream<String> stream1 = Stream.of("Java", "Scala", "Groovy");
Stream<String> stream2 = Stream.of("Python", "Go", "Swift");
Stream<String> concatStream = Stream.concat(stream1, stream2);
// [Java, Scala, Groovy, Python, Go, Swift]
κ°κ³΅νκΈ°
Filtering
μ€νΈλ¦Όλ΄μ μμλ€μ κ±Έλ¬λ΄λ μμ μ λλ€.
μΈμλ‘ λ°λ Predicate
λ boolean
νμ
μ λ°ννλ ν¨μν μΈν°νμ΄μ€λ‘ νκ°μμ΄ λ€μ΄κ°κ² λ©λλ€.
Stream<String> stream = names.stream()
.filter(name -> name.contains("a"));
// [Elena, Java]
κ° μμμμ "a" λ¬Έμμ΄μ΄ λ€μ΄κ° μ΄λ¦λ§ λ€μ΄κ° μ€νΈλ¦Όμ΄ λ°νλ©λλ€.
Mapping
μ€νΈλ¦Όλ΄μ μμλ€μ νλμ© νΉμ κ°μΌλ‘ λ°νν΄μ€λλ€.
Stream<String> stream = names.stream()
.map(String::toUpperCase);
// [ERIC, ELENA, JAVA]
μλμ κ°μ΄ κ°μ²΄ μμμμ κΊΌλ΄μ¬μλ μμ΅λλ€.
Stream<Integer> stream = productList.stream()
.map(Product::getAmount);
// [23, 14, 13, 23, 13]
flatMap
μ μ€μ²©κ΅¬μ‘°λ₯Ό μ κ±°ν ν μμ
ν μ μμ΅λλ€.
List<List<String>> list = Arrays.asList(
Arrays.asList("a"),
Arrays.asList("b")
); // [[a], [b]]
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));
Sorting
μ λ ¬λ°©λ²μ λ€λ₯Έ μ λ ¬κ³Ό λ§μ°¬κ°μ§λ‘ Comparator
μ μ΄μ©νλ€.
μΈμ μμ΄ νΈμΆν κ²½μ° μ€λ¦μ°¨μμΌλ‘ μ λ ¬ν©λλ€.
IntStream.of(14, 11, 20, 39, 23)
.sorted()
.boxed()
.collect(Collectors.toList());
// [11, 14, 20, 23, 39]
λ¬Έμμ΄ λ¦¬μ€νΈλ₯Ό μν멧 μμλλ‘ μ λ ¬νλ μ½λμμλ λ€μκ³Ό κ°λ€.
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]
λ¬Έμμ΄ κΈΈμ΄λ₯Ό κΈ°μ€μΌλ‘ μ λ ¬νλ μ½λμμλ λ€μκ³Ό κ°λ€.
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]
Iterating
μ€νΈλ¦Όλ΄μ μμλ€μ κ°κ°μ λμμΌλ‘ νΉμ μ°μ°μ μννμ§ μλ λ©μλλ‘λ peek
μ΄ μλ€.
peek
μ κ·Έλ₯ νμΈλ§ ν΄λ³΄λ λ©μλλ‘ νΉμ‘ κ²°κ³Όλ₯Ό λ°ννμ§ μλ ν¨μν μΈν°νμ΄μ€ Consumer
λ₯Ό μΈμλ‘ λ°λλ€.
int sum = IntStream.of(1, 3, 5, 7, 9)
.peek(System.out::println)
.sum();
μ‘°ν©νκΈ°
Calculating
μ΅λ, μ΅μ, ν©κ³, νκ· λ± κΈ°λ³Έν νμ μΌλ‘ λ€μν κ²°κ³Όλ₯Ό λ§λ€μ΄λΌ μ μλ€.
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).min();
μ€νΈλ¦Όμμ ifPresent
λ©μλλ₯Ό μ΄μ©ν΄μ Optional μ μ²λ¦¬ν μ μλ€.
DoubleStream.of(1.1, 2.2, 3.3, 4.4, 5.5)
.average()
.ifPresent(System.out::println);
Reduction
μ€νΈλ¦Όμμ reduce
λ©μλλ₯Ό μ΄μ©ν μ μλ€.
reduce
λ©μλλ μ΄ μΈκ°μ§μ νλΌλ―Έν°λ₯Ό λ°μμ μλ€.
- accmulator
- κ° μμλ₯Ό μ²λ¦¬νλ κ³μ° λ‘μ§
- κ° μμκ° μ€κ°κ²°κ³Όλ₯Ό μ²λ¦¬νμ¬ μμ±νλ λ‘μ§
- identity
- κ³μ°μ μν΄ μ΄κΈ°κ°
- combiner
- λ³λ ¬ μ€νΈλ¦Όμμλ§ μ¬μ©κ°λ₯νλ©° κ³μ°κ²°κ³Όλ₯Ό ν©μΉλ λ‘μ§
OptionalInt reduced = IntStream.range(1, 4) // [1, 2, 3]
.reduce((a, b) -> {
return Integer.sum(a, b);
}); // 6 (1 + 2 + 3)
int reducedTwoParams = IntStream.range(1, 4) // [1, 2, 3]
.reduce(10, Integer::sum); // 16 (10 + 1 + 2 + 3)
Integer reducedParallel = Arrays.asList(1, 2, 3)
.parallelStream()
.reduce(10,
Integer::sum,
(a, b) -> {
System.out.println("combiner was called");
return a + b;
}); // 36 (10 + 1 = 11, 10 + 2 = 12, 10 + 3 = 13)
Collecting
μ€νΈλ¦Όμ μ’
λ£νλ λλ€λ₯Έ μμ
μΌλ‘ Collector
νμ
μ μΈμλ₯Ό λ°μμ μ²λ¦¬νλ€.
Collectors.toList()
μ€νΈλ¦Όμμ μμ ν κ²°κ³Όλ₯Ό 리μ€νΈλ‘ λ°ννλ€.
List<String> collectorCollection = productList.stream()
.map(Product::getName)
.collect(Collectors.toList());
// [potatoes, orange, lemon, bread, sugar]
Collectors.joining()
μ€νΈλ¦Όμ μμ ν κ²°κ³Όλ₯Ό νλμ λ¬Έμμ΄λ‘ μ΄μ΄ λΆμΈλ€.
String listToString = productList.stream()
.map(Product::getName)
.collect(Collectors.joining());
// potatoesorangelemonbreadsugar
- delimiter
- κ° μμ μ€κ°μ λ€μ΄κ° μμλ₯Ό ꡬλΆμμΌμ£Όλ ꡬλΆμ
- prefix
- κ²°κ³Ό 맨 μμ λΆλ λ¬Έμ
- suffix
- κ²°κ³Ό 맨 λ€μ λΆλ λ¬Έμ
String listToString = productList.stream()
.map(Product::getName)
.collect(Collectors.joining(", ", "<", ">"));
// <potatoes, orange, lemon, bread, sugar>
Collectors.averageingInt()
μ«μκ°μ νκ· (avg) μ λΈλ€.
Double averageAmount = productList.stream()
.collect(Collectors.averagingInt(Product::getAmount));
// 17.2
Collectors.summingInt()
μ«μκ°μ ν© (sum) μ λΈλ€.
Integer summingAmount = productList.stream()
.collect(Collectors.summingInt(Product::getAmount));
// 86
Integer summingAmount = productList.stream()
.mapToInt(Product::getAmount)
.sum(); // 86
Collectors.summarizingInt()
κ°μ, ν©κ³, νκ· , μ΅μ, μ΅λμ κ°μ μΌκ΄μ μΌλ‘ λ°ννλ€.
IntSummaryStatistics statistics = productList.stream()
.collect(Collectors.summarizingInt(Product::getAmount));
IntSummaryStatistics {count=5, sum=86, min=13, average=17.200000, max=23}
Collectors.groupingBy()
νΉμ μμλ€λ‘ κ·Έλ£Ήμ§μμ μλ€.
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'}]
}
Collectors.partitioningBy()
groupingBy
μμ ν¨μν μΈν°νμ΄μ€ Function
μ μ΄μ©ν΄μ νΉμ κ°μ κΈ°μ€μΌλ‘ μ€νΈλ¦Ό λ΄μ μμλ€μ λ¬Άμλ€λ©΄
partitioningBy
λ ν¨μν μΈν°νμ΄μ€ Predicate
λ₯Ό λ°μ μ²λ¦¬νλ€.
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'}]
}
n κΉμ§μ μμ°μλ₯Ό μμμ λΉ μμλ‘ λΆν
private boolean isPrime(int candidate) {
int candidateRoot = (int) Math.sqrt((double) candidate);
return IntStream.rangeClosed(2, candidateRoot).noneMatch(i -> candidate % i == 0);
}
public Map<Boolean, List<Integer>> partitionPrimes(int n) {
return IntStream.rangeClose(2, n).boxed().collect(Collectors.partitioningBy(candidate -> isPrime(candidate)));
}
Collectors.collectingAndThen()
νΉμ νμ
μ κ²°κ³Όλ₯Ό collect
ν μ΄νμ μΆκ°μ μΈ μμ
μ΄ νμν λ μ¬μ©κ°λ₯νλ€.
λ€μ μμ λ Collectors.toSet
μ μ΄μ©ν΄μ κ²°κ³Όλ₯Ό Set
μΌλ‘ collect
ν ν
μμ λΆκ°ν Set
μΌλ‘ λ³ννλ μμ
μ μΆκ°λ‘ μ€ννλ μ½λμ΄λ€.
Set<Product> unmodifiableSet = productList
.stream()
.collect(Collectors.collectingAndThen(Collectors.toSet(), Collections::unmodifiableSet));
Collectors.of()
reduce
λ©μλλ₯Ό λμ μΌλ‘ μ¬μ©νμ¬ collect
νλ μ½λμ΄λ€.
Collector<Product, ?, LinkedList<Product>> toLinkedList = Collector.of(LinkedList::new,
LinkedList::add,
(first, second) -> {
first.addAll(second);
return first;
});
LinkedList<Product> linkedListOfPersons = productList.stream()
.collect(toLinkedList);
Matching
쑰건μ λ§μ‘±νλ κ²°κ³Όλ₯Ό 체ν¬νμ¬ λ°νν©λλ€.
List<String> names = Arrays.asList("Eric", "Elena", "Java");
boolean anyMatch = names
.stream()
.anyMatch(name -> name.contains("a"));
boolean allMatch = names
.stream()
.allMatch(name -> name.length() > 3);
boolean noneMatch = names
.stream()
.noneMatch(name -> name.endsWith("s"));
Iterating
foreach
λ μμλ₯Ό λλ©΄μ μ€νλλ μ΅μ’
μμ
μ
λλ€.
names.stream().forEach(System.out::println);
μ°Έκ³ μλ£
https://futurecreator.github.io/2018/08/26/java-8-streams/
http://iloveulhj.github.io/posts/java/java-stream-api.html
https://homoefficio.github.io/2016/06/26/for-loop-λ₯Ό-Stream-forEach-λ‘-λ°κΎΈμ§-λ§μμΌ-ν -3κ°μ§-μ΄μ /
https://yongho1037.tistory.com/704
https://www.mkyong.com/java8/java-8-collectors-groupingby-and-mapping-example/
β Method Reference Optional β