Post

[Java] Lambda, Stream

[Java] Lambda, Stream

πŸ“Œ Lambda Function

Lambda ν•¨μˆ˜λŠ” ν•¨μˆ˜λ₯Ό ν•˜λ‚˜μ˜ μ‹μ²˜λŸΌ ν‘œν˜„ν•˜λŠ” 방법이닀. ν•¨μˆ˜λͺ…이 μ—†κΈ° λ•Œλ¬Έμ— β€˜μ΅λͺ… ν•¨μˆ˜β€™λΌκ³  λΆ€λ₯΄κΈ°λ„ ν•œλ‹€. 읡λͺ… ν•¨μˆ˜λŠ” ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€μ˜ μΈμŠ€ν„΄μŠ€λ‘œ μ·¨κΈ‰λ˜λ―€λ‘œ β€˜μΌκΈ‰ 객체’이닀. 일급 κ°μ²΄λž€ λ‹€λ₯Έ κ°μ²΄λ“€μ—κ²Œ 일반적으둜 적용 κ°€λŠ₯ν•œ 연산을 λͺ¨λ‘ μ§€μ›ν•˜λŠ” 객체둜, 읡λͺ… ν•¨μˆ˜ λ˜ν•œ μ •μˆ˜λ‚˜ λ¬Έμžμ—΄κ³Ό λ™μΌν•œ λ°©λ²•μœΌλ‘œ λ‹€λ£° 수 μžˆμŒμ„ μ˜λ―Έν•œλ‹€.

1
(λ§€κ°œλ³€μˆ˜ λͺ©λ‘) -> { ν•¨μˆ˜ λ‚΄λΆ€ }

μœ„μ™€ 같은 ν˜•νƒœλ₯Ό λžŒλ‹€ ν•¨μˆ˜λΌκ³  ν•œλ‹€.

1
2
3
4
5
6
7
8
// κΈ°μ‘΄ ν•¨μˆ˜
public int add(int x, int y) {
    return x + y;
}

// λžŒλ‹€ ν•¨μˆ˜
(int x, int y) -> x + y

λ‹€μŒκ³Ό 같이 λžŒλ‹€ ν•¨μˆ˜λ‘œ λ³€ν™˜ν•  수 μžˆλ‹€.

1
(x, y) -> x + y

μ»΄νŒŒμΌλŸ¬κ°€ λ§€κ°œλ³€μˆ˜ νƒ€μž…μ„ μΆ”λ‘ ν•  수 μžˆλŠ” 경우 μƒλž΅ν•  수 μžˆλ‹€.

1
x -> x * 2

λ§€κ°œλ³€μˆ˜κ°€ ν•˜λ‚˜λΌλ©΄ κ΄„ν˜Έλ₯Ό μƒλž΅ν•  수 μžˆλ‹€. 단, λ§€κ°œλ³€μˆ˜κ°€ μ—†λ‹€λ©΄ 빈 κ΄„ν˜Έλ₯Ό μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.

1
(x, y) -> x + y

ν•¨μˆ˜ λ‚΄λΆ€κ°€ ν•œ 쀄이라면 μ€‘κ΄„ν˜Έ 및 return 을 μƒλž΅ν•΄μ•Ό ν•œλ‹€.

πŸ“Œ Functional Interface

ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λŠ” 였직 ν•˜λ‚˜μ˜ 좔상 λ©”μ„œλ“œλ§Œμ„ κ°–λŠ” μΈν„°νŽ˜μ΄μŠ€μ΄λ‹€. @FunctionalInterface μ–΄λ…Έν…Œμ΄μ…˜μ„ μ‚¬μš©ν•˜μ—¬ μ„ μ–Έν•œλ‹€. 이 μ–΄λ…Έν…Œμ΄μ…˜μ„ μ‚¬μš©ν•˜λ©΄ μ»΄νŒŒμΌλŸ¬κ°€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€κ°€ 되기 μœ„ν•œ 쑰건을 λ§Œμ‘±ν•˜λŠ”μ§€ ν™•μΈν•œλ‹€.

ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€κ°€ 되기 μœ„ν•œ 쑰건은 λ‹€μŒκ³Ό κ°™λ‹€.

  1. 단 ν•˜λ‚˜μ˜ 좔상 λ©”μ„œλ“œλ₯Ό κ°€μ Έμ•Ό ν•œλ‹€.
  2. default , static λ©”μ„œλ“œλŠ” μ—¬λŸ¬ 개 μ‘΄μž¬ν•΄λ„ λ¬΄λ°©ν•˜λ‹€.

Consumer

Consumer μΈν„°νŽ˜μ΄μŠ€λŠ” ν•˜λ‚˜μ˜ 인자λ₯Ό λ°›μ•„ λ‘œμ§μ„ μˆ˜ν–‰ν•˜κ³  아무것도 λ¦¬ν„΄ν•˜μ§€ μ•ŠλŠ” 연산을 μ •μ˜ν•  λ•Œ μ‚¬μš©ν•œλ‹€.

1
2
Consumer<String> printer = s -> System.out.println(s);
printer.accept("Hello, Lambda!");

accept λŠ” Consumer μΈν„°νŽ˜μ΄μŠ€μ˜ 좔상 λ©”μ„œλ“œλ‘œ, T νƒ€μž…μ˜ 인자λ₯Ό λ°›μ•„ λ‘œμ§μ„ μˆ˜ν–‰ν•œλ‹€. 리턴 νƒ€μž…μ€ void 이닀.

Supplier

Supplier μΈν„°νŽ˜μ΄μŠ€λŠ” 인자λ₯Ό λ°›μ§€ μ•Šκ³  였직 값을 λ¦¬ν„΄ν•˜λŠ” 연산을 μ •μ˜ν•  λ•Œ μ‚¬μš©ν•œλ‹€.

1
2
Supplier<User> userSupplier = () -> new User("κΈ°λ³Έμ‚¬μš©μž", 0);
User defaultUser = userSupplier.get();

get 은 Supplier 의 좔상 λ©”μ„œλ“œλ‘œ, T νƒ€μž…μ˜ 객체λ₯Ό λ¦¬ν„΄ν•œλ‹€.

Function<T, R>

Function μΈν„°νŽ˜μ΄μŠ€λŠ” ν•˜λ‚˜μ˜ 인자λ₯Ό λ°›μ•„ νŠΉμ • νƒ€μž…μ˜ κ²°κ³Όλ₯Ό λ¦¬ν„΄ν•˜λŠ” μž‘μ—…μ„ μ •μ˜ν•  λ•Œ μ‚¬μš©ν•œλ‹€.

1
2
Function<Integer, Integer> square = x -> x * x;
System.out.println(square.apply(4));

apply λŠ” Function 의 좔상 λ©”μ„œλ“œλ‘œ, T νƒ€μž…μ˜ 인자λ₯Ό λ°›μ•„ R νƒ€μž…μ˜ κ²°κ³Όλ₯Ό λ¦¬ν„΄ν•œλ‹€.

Predicate

Predicate μΈν„°νŽ˜μ΄μŠ€λŠ” ν•˜λ‚˜μ˜ 인자λ₯Ό λ°›μ•„ boolean 값을 λ¦¬ν„΄ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€μ΄λ‹€.

1
2
Predicate<String> isEmpty = s -> s.isEmpty();
System.out.println(isEmpty.test(""));  

test λŠ” Predicate 의 μΈν„°νŽ˜μ΄μŠ€λ‘œ 쑰건에 맞으면 true, κ·Έλ ‡μ§€ μ•ŠμœΌλ©΄ false λ₯Ό λ¦¬ν„΄ν•œλ‹€.

πŸ“Œ Stream

Stream APIλŠ” Java 8λΆ€ν„° λ„μž…λ˜μ—ˆμœΌλ©°, μ»¬λ ‰μ…˜, λ°°μ—΄ λ“±κ³Ό 같은 데이터λ₯Ό ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ° μŠ€νƒ€μΌλ‘œ λ‹€λ£° 수 μžˆλ„λ‘ ν•˜λŠ” 도ꡬ이닀.

슀트림 νŒŒμ΄ν”„λΌμΈμ€ 크게 생성, 쀑간 μ—°μ‚°, μ΅œμ’… μ—°μ‚°μœΌλ‘œ λ‚˜λ‰˜λŠ”λ°, 쀑간 연산은 μ΅œμ’… 연산이 호좜될 λ•ŒκΉŒμ§€ μ‹€μ œλ‘œ μ—°μ‚°λ˜μ§€ μ•ŠλŠ”λ‹€. 이λ₯Ό Lazy Evaluation 이라고 ν•œλ‹€.

λ˜ν•œ μŠ€νŠΈλ¦Όμ€ λ©€ν‹° μ“°λ ˆλ“œλ₯Ό ν†΅ν•œ 병렬 연산이 κ°€λŠ₯ν•˜λ‹€. 연산을 μˆ˜ν–‰ν•˜μ—¬λ„ 원본은 λ³€κ²½λ˜μ§€ μ•ŠμœΌλ©° μƒˆλ‘œμš΄ 슀트림 λ˜λŠ” κ²°κ³Όκ°€ λ¦¬ν„΄λœλ‹€. ν•œ 번 μ΅œμ’… 연산을 μˆ˜ν–‰ν•œ μŠ€νŠΈλ¦Όμ€ λ‹€μ‹œ μ‚¬μš©ν•  수 μ—†λ‹€.

1
2
3
4
5
6
7
8
9
// λžŒλ‹€ ν•¨μˆ˜
List<String> upper1 = names.stream()
    .map(s -> s.toUpperCase())
    .collect(Collectors.toList());

// λ©”μ„œλ“œ μ°Έμ‘°
List<String> upper2 = names.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList());

μŠ€νŠΈλ¦Όμ— λžŒλ‹€ ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•  수 μžˆμ§€λ§Œ, λ©”μ„œλ“œ μ°Έμ‘°λ₯Ό μ‚¬μš©ν•˜λ©΄ 더 κ°„κ²°ν•΄μ§€κ³  가독성이 μ’‹μ•„μ§€λŠ” κ²½μš°κ°€ μžˆλ‹€.

πŸ“Œ Stream 생성

1
2
3
4
List<String> list = List.of("A", "B", "C");

Stream<String> sequential = list.stream();
Stream<String> parallel = list.parallelStream();

Collection 을 μƒμ†ν•œ ν΄λž˜μŠ€λŠ” stream κ³Ό parallelStream λ©”μ„œλ“œλ₯Ό 톡해 슀트림 λ˜λŠ” 병렬 μŠ€νŠΈλ¦Όμ„ 생성할 수 μžˆλ‹€.

1
2
String[] array = {"X", "Y", "Z"};
Stream<String> stream1 = Arrays.stream(array);

λ°°μ—΄ λ˜ν•œ stream λ©”μ„œλ“œλ₯Ό 톡해 μŠ€νŠΈλ¦Όμ„ 생성할 수 μžˆλ‹€.

1
2
Stream<String> s1 = Stream.of("A", "B", "C");
Stream<Integer> s2 = Stream.of(1, 2, 3, 4);

Stream.of λ©”μ„œλ“œλ₯Ό 톡해 μ›μ†Œλ₯Ό 직접 μ§€μ •ν•˜μ—¬ μŠ€νŠΈλ¦Όμ„ 생성할 수 μžˆλ‹€.

1
2
3
4
5
Stream<String> builtStream = Stream.<String>builder()
    .add("A")
    .add("B")
    .add("C")
    .build();

Stream.builder λ©”μ„œλ“œλ₯Ό 톡해 λΉŒλ” νŒ¨ν„΄μ„ μ‚¬μš©ν•˜μ—¬ μŠ€νŠΈλ¦Όμ„ μ‚¬μš©ν•  수 μžˆλ‹€.

1
2
3
4
5
6
7
8
Stream.iterate(0, n -> n + 1)
      .limit(10)
      .forEach(System.out::println);
      
Stream.iterate(0,           
               n -> n < 10, // μ’…λ£Œ 쑰건
               n -> n + 1)
      .forEach(System.out::println);

iterate λ©”μ„œλ“œλ₯Ό 톡해 μŠ€νŠΈλ¦Όμ„ 생성할 수 μžˆλ‹€. iterate λŠ” μ΄ˆκΈ°κ°’κ³Ό μ—°μ‚° ν•¨μˆ˜λ₯Ό μž…λ ₯λ°›μ•„ λ¬΄ν•œ μŠ€νŠΈλ¦Όμ„ μƒμ„±ν•œλ‹€.

Java 9λΆ€ν„° μ’…λ£Œ 쑰건을 ν•¨κ»˜ λ„˜κ²¨ μœ ν•œ μŠ€νŠΈλ¦Όμ„ 생성할 수 μžˆλ‹€.

πŸ“Œ 쀑간 μ—°μ‚°

filter

1
Stream<T> filter(Predicate<? super T> predicate)

filter λ©”μ„œλ“œλŠ” 슀트림 λ‚΄ μ›μ†Œλ₯Ό μ£Όμ–΄μ§„ 쑰건에 λ§žλŠ” μ›μ†Œλ§Œ ν•„ν„°λ§ν•˜μ—¬ μƒˆλ‘œμš΄ 슀트림으둜 λ¦¬ν„΄ν•œλ‹€. Predicate<T> λ₯Ό 인자둜 λ°›μ•„ test λ©”μ„œλ“œλ₯Ό 톡해 각 μ›μ†Œκ°€ 쑰건을 λ§Œμ‘±ν•˜λŠ”μ§€ ν™•μΈν•œλ‹€.

filter λ₯Ό μ—¬λŸ¬ 번 μ‚¬μš©ν•˜λŠ” 경우 filter(a).filter(b) λŒ€μ‹  filter(a.and(b)) 와 같이 μ‚¬μš©ν•˜λŠ” 것이 μ„±λŠ₯적으둜 도움이 λœλ‹€. ν•„ν„°λ§μ˜ νšŸμˆ˜κ°€ 쀄어듀기 λ•Œλ¬Έμ΄λ‹€.

1
2
3
4
5
6
List<String> names = List.of("Alice", "Bob", "Charlie", "David");

List<String> filteredNames= names.stream()
    .filter(name -> name.startsWith("C"))
    .collect(Collectors.toList());
// ["Charlie"]

map

1
<R> Stream<R> map(Function<? super T, ? extends R> mapper)

map λ©”μ„œλ“œλŠ” 슀트림의 각 μ›μ†Œλ₯Ό λ‹€λ₯Έ ν˜•νƒœλ‘œ λ³€ν™˜ν•˜μ—¬ μƒˆλ‘œμš΄ μŠ€νŠΈλ¦Όμ„ μƒμ„±ν•˜λŠ” 연산이닀. Function<T, R> λ₯Ό 인자둜 λ°›μœΌλ©°, T λŠ” 원본 슀트림 μ›μ†Œμ˜ νƒ€μž…, R 은 λ¦¬ν„΄λœ 슀트림의 μ›μ†Œ νƒ€μž…μ΄λ‹€.

κΈ°μ‘΄ 값을 직접 λ³€κ²½ν•˜λŠ” 것이 μ•„λ‹ˆλΌ μƒˆλ‘œμš΄ 값을 μƒμ„±ν•˜λŠ” 것에 μ£Όμ˜ν•΄μ•Ό ν•œλ‹€.

1
2
3
4
5
6
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

List<Integer> squaredNumbers = numbers.stream()
    .map(n -> n * n)
    .collect(Collectors.toList());
// [1, 4, 9, 16, 25]

flatMap

1
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)

map λ©”μ„œλ“œλŠ” 각 μ›μ†Œλ₯Ό μΌλŒ€μΌ λ§€ν•‘ν•˜μ—¬ κ²°κ³Όκ°€ Stream<Stream<R>> 이 λ˜μ§€λ§Œ, flatMap λ©”μ„œλ“œλŠ” 각 μ›μ†Œλ₯Ό Stream으둜 λ§€ν•‘ν•œ ν›„ ν‰νƒ„ν™”ν•˜μ—¬ Stream<R> ν˜•νƒœλ‘œ λ§Œλ“ λ‹€. λ”°λΌμ„œ flatMap λ‚΄λΆ€μ—μ„œ λ°˜λ“œμ‹œ Stream 을 λ¦¬ν„΄ν•˜λ„λ‘ ν•΄μ•Ό ν•œλ‹€.

1
2
3
4
5
6
7
8
9
10
11
12
List<List<String>> characters = List.of(
    List.of("A","B"),
    List.of("C","D")
);

characters.stream()
      .flatMap(list -> list.stream())
      .forEach(System.out::println);
// A
// B
// C
// D

sorted

1
2
3
4
Stream<T> sorted();

// μ»€μŠ€ν…€ μ •λ ¬
Stream<T> sorted(Comparator<? super T> comparator);

sorted λ©”μ„œλ“œλŠ” 슀트림 λ‚΄ μ›μ†Œλ₯Ό μ •λ ¬ν•˜μ—¬ μƒˆλ‘œμš΄ μŠ€νŠΈλ¦Όμ„ λ¦¬ν„΄ν•œλ‹€. 슀트림의 μ›μ†Œκ°€ Comparable<T> λ₯Ό κ΅¬ν˜„ν•΄μ•Ό ν•œλ‹€. λ˜λŠ” Comparable<T> λ₯Ό 인자둜 λ„˜κ²¨ μ»€μŠ€ν…€ 정렬을 μˆ˜ν–‰ν•  수 μžˆλ‹€.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class User {
    String name;
    int age;
}

List<User> users = List.of(
    new User("Alice", 30),
    new User("Bob", 25),
    new User("Charlie", 28)
);

List<User> byAge = users.stream()
    .sorted(Comparator.comparing(User::getAge))
    .collect(Collectors.toList());
// Bob(25), Charlie(28), Alice(30)

distinct

1
Stream<T> distinct();

distinct λ©”μ„œλ“œλŠ” 슀트림의 μ›μ†Œ 쀑 λ™μΌν•œ 값이 μ—¬λŸ¬ 번 λ“±μž₯ν•˜λ©΄ 첫 번째 μ›μ†Œλ§Œ 남기고 λ‚˜λ¨Έμ§€λŠ” ν•„ν„°λ§ν•˜μ—¬ μƒμ„±λœ μƒˆλ‘œμš΄ μŠ€νŠΈλ¦Όμ„ λ¦¬ν„΄ν•œλ‹€. λ‚΄λΆ€μ μœΌλ‘œ equals λ˜λŠ” hashCode λ©”μ„œλ“œλ₯Ό 톡해 쀑볡을 νŒλ‹¨ν•œλ‹€. λ”°λΌμ„œ μ‚¬μš©μž μ •μ˜ κ°μ²΄μ—μ„œ distinct λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜κ³  μ‹Άλ‹€λ©΄ equals λ‚˜ hashCode λ©”μ„œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ“œν•΄μ•Ό ν•œλ‹€.

1
2
3
4
5
6
List<Integer> list = List.of(1, 2, 2, 3, 1, 4);

List<Integer> distinctList = list.stream()
    .distinct()
    .collect(Collectors.toList());
// [1, 2, 3, 4]

limit

1
Stream<T> limit(long maxSize)

limit λ©”μ„œλ“œλŠ” 슀트림의 μ›μ†Œμ˜ 개수λ₯Ό μ΅œλŒ€ maxSize 둜 μ œν•œν•˜μ—¬ μ²˜μŒλΆ€ν„° μ§€μ •λœ 개수만큼 이루어진 μŠ€νŠΈλ¦Όμ„ λ¦¬ν„΄ν•œλ‹€.

1
2
3
4
5
List<Integer> evens = Stream.iterate(0, n -> n + 2)
    .limit(10)
    .collect(Collectors.toList());
// [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

skip

1
Stream<T> skip(long n)

skip λ©”μ„œλ“œλŠ” 처음 n개의 μ›μ†Œλ₯Ό κ±΄λ„ˆλ›°κ³  λ‚˜λ¨Έμ§€ μ›μ†Œλ§ŒμœΌλ‘œ 이루어진 μŠ€νŠΈλ¦Όμ„ λ¦¬ν„΄ν•œλ‹€. λ§Œμ•½ 슀트림 μ›μ†Œμ˜ μˆ˜κ°€ n보닀 μž‘μœΌλ©΄ 빈 μŠ€νŠΈλ¦Όμ„ λ¦¬ν„΄ν•œλ‹€.

1
2
3
4
5
6
List<String> items = List.of("A", "B", "C", "D", "E", "F");

List<String> result = items.stream()
    .skip(2)
    .collect(Collectors.toList());
// [C, D, E, F]

peak

1
Stream<T> peek(Consumer<? super T> action)

peak λ©”μ„œλ“œλŠ” νŠΉμ • μ‹œμ μ˜ μ›μ†Œλ₯Ό μ‚΄νŽ΄λ³΄κ³  좔가적인 λ™μž‘μ„ μˆ˜ν–‰ν•œλ‹€. 주둜 νŒŒμ΄ν”„λΌμΈ μ€‘κ°„μ˜ 값을 λ‘œκΉ…ν•  λ•Œ μ‚¬μš©ν•œλ‹€.

1
2
3
4
Stream.of(1, 2, 3, 4, 5)
    .peek(i -> log.debug("κ°’: {}", i))
    .filter(i -> i % 2 == 0)
    .forEach(System.out::println);

πŸ“Œ μ΅œμ’… μ—°μ‚°

forEach

1
void forEach(Consumer<? super T> action)

forEach λ©”μ„œλ“œλŠ” 슀트림의 μ›μ†Œμ— λŒ€ν•΄ μ§€μ •λœ λ™μž‘μ„ μˆ˜ν–‰ν•˜κ³  리턴값 없이 μ’…λ£Œν•˜λŠ” 역할을 ν•œλ‹€.

병렬 μŠ€νŠΈλ¦Όμ„ μ‚¬μš©ν•˜λŠ” 경우 원본 λ°μ΄ν„°μ˜ μˆœμ„œλ₯Ό 보μž₯ν•˜μ§€ μ•ŠλŠ”λ‹€. 병렬 μŠ€νŠΈλ¦Όμ—μ„œ μˆœμ„œ λ˜ν•œ 보μž₯ν•˜κΈ° μœ„ν•΄μ„œ forEachOrdered λ₯Ό μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.

1
2
3
4
List<Integer> numbers = List.of(1, 2, 3, 4, 5);

numbers.stream()
       .forEach(System.out::println);

collect

1
<R, A> R collect(Collector<? super T, A, R> collector)

collect λ©”μ„œλ“œλŠ” 슀트림 μ›μ†Œλ“€μ„ μ»¨ν…Œμ΄λ„ˆλ‘œ ν•©μΉ˜λŠ” 역할을 ν•œλ‹€. T λŠ” 슀트림의 μ›μ†Œ νƒ€μž…, A λŠ” 슀트림 λ‚΄λΆ€μ—μ„œ 값을 μž μ‹œ μ €μž₯ν•˜λŠ” μž„μ‹œ μ»¨ν…Œμ΄λ„ˆ, R 은 μ΅œμ’… 결과의 νƒ€μž…μ΄λ‹€. 보톡 Collectors μ—μ„œ 미리 μ •μ˜λœ νŒ©ν† λ¦¬ λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•œλ‹€.

A λŠ” Accumulator (λˆ„μ‚°κΈ°)라고 λΆ€λ₯Έλ‹€.

1
2
3
4
Map<Character, List<String>> grouped =
    Stream.of("apple", "banana", "avocado")
          .collect(Collectors.groupingBy(s -> s.charAt(0)));
// {'a': ["apple","avocado"], 'b': ["banana"]}

reduce

reduce λ©”μ„œλ“œλŠ” 슀트림의 μ›μ†Œλ“€μ„ ν•˜λ‚˜μ˜ 결과둜 κ²°ν•©ν•  λ•Œ μ‚¬μš©ν•œλ‹€. 주둜 집계 연산에 μ‚¬μš©λœλ‹€.

reduce λŠ” 총 μ„Έ κ°€μ§€μ˜ μ˜€λ²„λ‘œλ”©λœ λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•œλ‹€.

1
Optional<T> reduce(BinaryOperator<T> accumulator)

슀트림의 μ›μ†Œ 쀑 첫 번째λ₯Ό accumulator의 μ΄ˆκΈ°κ°’μœΌλ‘œ μ‚¬μš©ν•˜μ—¬ 남은 μ›μ†Œλ₯Ό μ°¨λ‘€λ‘œ κ²°ν•©ν•œλ‹€.

1
2
3
4
List<Integer> nums = List.of(1, 2, 3, 4);
Optional<Integer> sum = nums.stream()
    .reduce((a, b) -> a + b);
// 10

1
2
T reduce(T identity,
         BinaryOperator<T> accumulator)

μ΄ˆκΈ°κ°’ identity λ₯Ό λͺ…μ‹œν•˜κ³  κ·Έ 이후 μ›μ†Œλ“€μ„ μ°¨λ‘€λŒ€λ‘œ κ²°ν•©ν•œλ‹€. μ΄μ „μ—λŠ” Optional 둜 리턴값을 λ°›μ•˜λŠ”λ°, μ΄λ²ˆμ—λŠ” 빈 슀트림 λ˜ν•œ identity λ₯Ό λ¦¬ν„΄ν•˜κΈ° λ•Œλ¬Έμ— T νƒ€μž…μœΌλ‘œ 리턴값을 λ°›λŠ”λ‹€.

1
2
3
4
List<Integer> nums = List.of(1, 2, 3, 4);
int product = nums.stream()
    .reduce(1, (a, b) -> a * b);
// 24

1
2
3
<U> U reduce(U identity,
             BiFunction<U, ? super T, U> accumulator,
             BinaryOperator<U> combiner)

μœ„ λ©”μ„œλ“œλŠ” 병렬 μŠ€νŠΈλ¦Όμ—μ„œ 자주 μ‚¬μš©ν•˜λ©°, U νƒ€μž…μœΌλ‘œ accumulator λ₯Ό λ§Œλ“€κ³ , 각 μ›μ†Œλ₯Ό λˆ„μ ν•˜μ—¬ 쀑간 κ²°κ³Όλ₯Ό μƒμ„±ν•˜κ³ , μ΅œμ’…μ μœΌλ‘œ 이λ₯Ό combiner 둜 합쳐 λ¦¬ν„΄ν•œλ‹€.

1
2
3
4
5
6
7
8
List<String> words = List.of("Java", "Stream", "API");
int totalLength = words.parallelStream()
    .reduce(
        0,
        (sum, w) -> sum + w.length(),
        Integer::sum
    );
// 13

stream의 집계 ν•¨μˆ˜

1
long count()

count λ©”μ„œλ“œλŠ” μŠ€νŠΈλ¦Όμ— ν¬ν•¨λœ μš”μ†Œμ˜ 총 개수λ₯Ό long νƒ€μž…μœΌλ‘œ λ¦¬ν„΄ν•œλ‹€. λ‚΄λΆ€μ μœΌλ‘œ λͺ¨λ“  μ›μ†Œλ₯Ό μˆœνšŒν•˜μ—¬ 개수λ₯Ό μ„Έκ³  κ²°κ³Όλ₯Ό λ¦¬ν„΄ν•œλ‹€. 빈 슀트림의 경우 0을 λ¦¬ν„΄ν•˜λ©°, λ¬΄ν•œ 슀트림인 경우 count λ©”μ„œλ“œλŠ” μ’…λ£Œλ˜μ§€ μ•ŠλŠ”λ‹€.

1
2
3
List<String> fruits = List.of("apple", "banana", "cherry", "durian");
long count = fruits.stream().count();
// 4

1
int sum() // IntStream의 경우

sum λ©”μ„œλ“œλŠ” 슀트림의 λͺ¨λ“  μ›μ†Œμ˜ 합을 λ¦¬ν„΄ν•œλ‹€. 슀트림의 λͺ¨λ“  μ›μ†Œλ₯Ό λ‚΄λΆ€ 버퍼에 μ €μž₯ν•  ν›„ 0을 μ΄ˆκΈ°κ°’μœΌλ‘œ ν•˜μ—¬ μ›μ†Œλ₯Ό μ°¨λ‘€λ‘œ λ”ν•œλ‹€.

1
2
3
int total = IntStream.of(10, 20, 30, 40, 50)
                     .sum();
// 150

1
Optional<T> max(Comparator<? super T> comparator)

max λ©”μ„œλ“œλŠ” 슀트림 μ›μ†Œ 쀑 κ°€μž₯ 큰 값을 λ¦¬ν„΄ν•œλ‹€. 객체 슀트림인 경우 Comparator λ₯Ό 인자둜 λ„˜κ²¨μ£Όμ–΄μ•Ό ν•œλ‹€.

1
2
3
4
List<String> names = List.of("Alice", "Bob", "Charlie");

Optional<String> opt = names.stream()
    .max(Comparator.naturalOrder());

find 계열

1
Optional<T> findFirst()

findFirst λ©”μ„œλ“œλŠ” 슀트림의 첫 번째 μ›μ†Œλ₯Ό νƒμƒ‰ν•˜μ—¬ λ¦¬ν„΄ν•œλ‹€. μ΄λŠ” 병렬 μŠ€νŠΈλ¦Όμ—μ„œλ„ λ§ˆμ°¬κ°€μ§€μ΄λ‹€. 즉, findFirst λ©”μ„œλ“œλŠ” λ‚΄λΆ€μ μœΌλ‘œ κ°€μž₯ μž‘μ€ 인덱슀λ₯Ό κ°€μ§„ μ›μ†Œλ₯Ό λ¦¬ν„΄ν•œλ‹€.

1
2
3
4
5
6
List<String> list = List.of("A1", "A2", "A3", "A4");
Optional<String> firstParallel = list.parallelStream()
    .filter(s -> s.startsWith("A"))
    .findFirst();
System.out.println(firstParallel.get());
// A1

1
Optional<T> findAny()

findAny λ©”μ„œλ“œλŠ” μŠ€νŠΈλ¦Όμ—μ„œ μž„μ˜μ˜ μ›μ†Œλ₯Ό μ°Ύμ•„ Optional<T> 둜 λ¦¬ν„΄ν•œλ‹€. 순차 슀트림인 경우 findFirst λ©”μ„œλ“œμ™€ λ™μΌν•˜κ²Œ λ™μž‘ν•˜λ©°, 병렬 슀트림인 경우 각 μ“°λ ˆλ“œ 의 슀트림 쀑 κ°€μž₯ λ¨Όμ € 처리된 μ›μ†Œλ₯Ό λ¦¬ν„΄ν•œλ‹€.

toArray

1
Object[] toArray();

toArray λ©”μ„œλ“œλŠ” 슀트림의 μ›μ†Œλ“€μ„ λ°°μ—΄λ‘œ λ³€ν™˜ν•œλ‹€.

슀트림의 μ΅œμ’… 크기λ₯Ό 미리 μ•Œ 수 μ—†κΈ° λ•Œλ¬Έμ— 슀트림의 μ›μ†Œλ“€μ„ ArrayList 와 같이 크기λ₯Ό μ‘°μ •ν•  수 μžˆλŠ” μ»¨ν…Œμ΄λ„ˆμ— λ‹΄κ³ , 개수λ₯Ό κ³„μ‚°ν•˜μ—¬ 배열을 μƒμ„±ν•˜κ³  μ»¨ν…Œμ΄λ„ˆμ˜ μ›μ†Œλ“€μ„ μƒˆλ‘œ μƒμ„±λœ 배열에 λ³΅μ‚¬ν•˜λŠ” 과정을 μ§„ν–‰ν•œλ‹€.

1
2
3
Stream<String> stream = Stream.of("apple", "banana", "cherry");
Object[] array = stream.toArray();
// [apple, banana, cherry]

match 계열

1
boolean allMatch(Predicate<? super T> predicate);

allMatch λ©”μ„œλ“œλŠ” 슀트림의 λͺ¨λ“  μ›μ†Œκ°€ μ£Όμ–΄μ§„ 쑰건 predicate λ₯Ό λ§Œμ‘±ν•˜λŠ”μ§€ κ²€μ‚¬ν•˜λŠ” 연산이닀.

쑰건을 λ§Œμ‘±ν•˜μ§€ μ•ŠλŠ” μ›μ†Œλ₯Ό λ°œκ²¬ν•˜λŠ” μˆœκ°„ μ¦‰μ‹œ false λ₯Ό λ¦¬ν„΄ν•˜κ³  더 이상 μ›μ†Œλ₯Ό μ²˜λ¦¬ν•˜μ§€ μ•ŠλŠ”λ‹€. λ§Œμ•½ 빈 μŠ€νŠΈλ¦Όμ„ κ²€μ‚¬ν•˜λŠ” 경우 항상 true λ₯Ό λ¦¬ν„΄ν•œλ‹€.

단, λ¬΄ν•œ 슀트림의 경우 쑰건을 λ§Œμ‘±ν•˜λŠ” μ›μ†Œκ°€ μ—†λ‹€λ©΄ μ˜μ›νžˆ 싀행될 수 μžˆλ‹€.

1
2
3
List<Integer> numbers = Arrays.asList(2, 4, 6, 8, 10);
boolean allEven = numbers.stream()
    .allMatch(num -> num % 2 == 0) // true

anyMatch λŠ” ν•˜λ‚˜λΌλ„ 쑰건을 λ§Œμ‘±ν•˜λ©΄ true λ₯Ό λ¦¬ν„΄ν•œλ‹€. 쑰건을 λ§Œμ‘±ν•˜λŠ” μ›μ†Œλ₯Ό λ°œκ²¬ν•˜λŠ” μ¦‰μ‹œ 검사λ₯Ό μ’…λ£Œν•˜κ³  true λ₯Ό λ¦¬ν„΄ν•œλ‹€.

noneMatch λŠ” λͺ¨λ“  μ›μ†Œκ°€ 쑰건을 λ§Œμ‘±ν•˜μ§€ μ•ŠμœΌλ©΄ true λ₯Ό λ¦¬ν„΄ν•œλ‹€. 쑰건을 λ§Œμ‘±ν•˜λŠ” μ›μ†Œλ₯Ό λ°œκ²¬ν•˜λŠ” μ¦‰μ‹œ 검사λ₯Ό μ’…λ£Œν•˜κ³  false λ₯Ό λ¦¬ν„΄ν•œλ‹€.

This post is licensed under CC BY 4.0 by the author.