Java Collection - stream API

my_mon·2023년 3월 20일
0
post-thumbnail

Stream() 이란?

Java 8버전부터 Collection 인터페이스에 추가된 API
stream은 Collection 객체를 스트림으로 변환하여 데이터를 처리하는 다양한 연산을 수행하도록 도와주며 컬렉션, 배열 등의 데이터 소스로부터 요소를 추출하고 처리하는 기능을 제공한다.
Stream에서 연산은 파이프라인을 통해 처리된다. 이는 여러개의 중간 연산과 최종연산이 순차적으로 연결된 것을 의미하는데, 스트림 파이프라인을 통해 코드를 간결하게 유지할 수 있다.

중간연산

중간연산은 여러번 사용될 수 있는데, 이 단계에서 자주 사용하는 메소드는 다음과 같다.

  • filter() : 조건에 따라 요소를 필터링하여 만족하는 요소만 Stream으로 반환된다. 스트림에서 짝수만 반환하는 예시는 다음과 같다.
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = list.stream();
Stream<Integer> evenStream = stream.filter(x -> x % 2 == 0);

위 코드에서 filter() 메소드는 각 요소 x에 대해 x % 2 == 0의 조건을 검사한 후, 만족하는 요소만을 선택하여 evenStream에 반환한다.


  • map() : 스트림의 각 요소를 변환하는데, 입력값을 받아서 변환된 출력값을 반환한다.
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = list.stream();
Stream<Integer> squaredStream = stream.map(x -> x * x);

// 출력값 : 1, 4, 9, 16, 25

위 코드에서 스트림의 map 메소드를 사용하여 각 요소를 제곱한 결과를 새로운 Stream으로 반환한다. 이 때 람다식 x -> x * x는 각 요소를 제곱하는 함수이다.


  • sorted() : 스트림의 요소를 정렬하여 새로운 스트림을 반환한다.
    기본적으로는 Comparable 인터페이스를 구현한 객체에 대해서 오름차순으로 정렬한다. Comparator를 인자로 받는 오버로딩된 메소드를 사용하여 정렬방법으 지정할 수도 있다.
List<String> list = Arrays.asList("apple", "banana", "cherry", "date");
Stream<String> stream = list.stream();

Stream<String> sortedStream = stream.sorted();
sortedStream.forEach(System.out::println);

// 출력 결과: apple, banana, cherry, date

위 코드에서는 String 타입의 요소를 가진 리스트에서 스트림을 생성하고 Sorted() 메소드를 호출하여 알파벳순으로 정렬된 스트림을 반환하고 출력한다.


  • distinct() : 스트림의 요소 중에서 중복된 요소를 제거하여 새로운 Stream을 반환하며 중복을 판단할 때 equals() 메소드를 사용한다.
List<Integer> list = Arrays.asList(1, 2, 3, 2, 4, 3, 5);
Stream<Integer> stream = list.stream();

Stream<Integer> distinctStream = stream.distinct();
distinctStream.forEach(System.out::println);

// 출력 결과: 1, 2, 3, 4, 5

위 코드에서는 Integer타입의 요소를 가진 리스트에서 스트림을 생성하고, distinct() 메소드를 호출하여 중복된 요소를 제거한 Stream을 반환하고 출력한다.


  • limit() : 스트림 요소 중에서 처음부터 지정한 개수만큼 요소를 가져와 새로운 스트림을 반환한다.
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = list.stream();

Stream<Integer> limitedStream = stream.limit(3);
limitedStream.forEach(System.out::println);

// 출력 결과: 1, 2, 3

위 코드에서는 limit() 메소드를 호출하여 처음 3개의 요소만을 가진 스트림을 반환하고 출력한다.


  • flatMap() : 스트림의 각 요소들을 다른 스트림으로 변환한 후, 모든 스트림을 하나의 스트림으로 연결하여 반환한다.
List<String> list1 = Arrays.asList("java", "python");
List<String> list2 = Arrays.asList("stream", "lambda");
Stream<List<String>> streamOfList = Stream.of(list1, list2);

Stream<String> flatMapStream = streamOfList.flatMap(list -> list.stream());
flatMapStream.forEach(System.out::println);

// 출력 결과: java, python, stream, lambda

위 코드에서는 각각 두개의 문자열 리스트를 가진 스트림을 생성하고 flatMap() 메소드를 호출하여 각 리스트의 문자열 요소들을 하나의 스트림으로 연결 후 반환하고 출력한다.

최종연산

최종연산은 스트림의 요소들을 처리하여 최종 결과를 반환하는 연산인데, 최종 연산을 수행한 후에는 더이상 스트림을 다룰 수 없다.

1. 요소의 출력 : forEach()
스트림의 각 요소를 소비하여 출력한다.

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
list.stream()
    .forEach(x -> System.out.print(x + " ")); 
    
// 출력 결과: 1 2 3 4 5

2. 요소의 소모 : reduce()
스트림의 요소를 하나씩 처리하여 최종적으로 하나의 결과값을 반환한다.

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> sum = list.stream().reduce((x, y) -> x + y);
System.out.println(sum.get()); // 출력 결과: 15

3. 요소의 검색 : findFirst(), findAny()
findFisrt(), findAny()는 스트림에서 요소를 검색하는 용도로 사용된다.

  • findFirst() 메소드는 스트림에서 첫 번째 요소를 반환하거나, 스트림이 비어있다면 Optional.empty()를 반환하며, 일반적으로 정렬된 스트림에서 첫번째 요소를 찾는 데 사용된다.
List<String> strings = Arrays.asList("apple", "banana", "avocado");
Optional<String> firstA = strings.stream().filter(s -> s.startsWith("a")).findFirst();
System.out.println(firstA.orElse("No strings found"));

//출력 결과 : apple

위 코드는 filter() 메소드를 사용하여 문자열 리스트에서 'a'로 시작하는 첫 번째 문자열을 찾기 위해 스트림을 필터링한 후, findFirst() 메소드를 통해 결과를 반환한다.


  • findAny() 메소드는 스트림에서 임의의 요소를 반환한다. 마찬가지로 스트림이 비어있다면 Optional.empty()를 반환하며 병렬처리된 스트림에서 사용되어 스트림의 모든 요소 중 하나를 찾을 때 유용하다.
List<String> strings = Arrays.asList("apple", "banana", "avocado");
Optional<String> anyA = strings.parallelStream().filter(s -> s.contains("a")).findAny();
System.out.println(anyA.orElse("No strings found"));

//출력 결과 : banana

위 코드에서 출력되는 결과는 'banana' 또는 'avocado' 문자열 중 하나가 출력된다. findAll() 메소드는 임의의 요소를 반환하기 때문이다.

4. 요소의 검사 : anyMatch(), allMatch(), noneMatch()
anyMatch(), allMatch(), noneMatch() 메소드는 모두 스트림의 요소를 조건식과 비교하여 boolean 값을 반환한다.

  • anyMatch() 메소드는 만족하는 스트림의 요소가 하나라도 있는지 검사한다. 조건식을 만족하는 요소가 적어도 하나가 있다면 true를 반환하고 그렇지 않으면 false를 반환한다.
List<String> strings = Arrays.asList("apple", "banana", "avocado");
boolean anyContainsA = strings.stream().anyMatch(s -> s.contains("a"));
System.out.println("Any string contains 'a': " + anyContainsA);

//출력 결과 : Any string contains 'a': true

위 코드는 문자열 리스트에서 'a'를 포함하는 요소가 하나 이상이 있는지 확인하는 예시다.


  • allMatch() 메소드는 조건식을 모든 스트림 요소에 대해 검사한다. 모든 요소가 조건식을 만족한다면 true를, 그렇지 않으면 false를 반환한다.
List<String> strings = Arrays.asList("apple", "banana", "avocado", "cherry");
boolean allStartsWithA = strings.stream().allMatch(s -> s.startsWith("a"));
System.out.println("All strings start with 'a': " + allStartsWithA)

//출력 결과 : Any string contains 'a': false

위 코드에서는 각 요소 모두가 a를 포함하고 있지 않기 때문에 false를 반환한다.


  • noneMatch() 메소드는 모든 스트림 요소가 조건식을 만족하지 않는지 검사한다. 모든 요소가 조건식을 만족하지 않으면 true, 하나라도 만족한다면 false를 반환한다.
List<String> strings = Arrays.asList("apple", "banana", "avocado", "cherry");
boolean noneContainsZ = strings.stream().noneMatch(s -> s.contains("z"));
System.out.println("No string contains 'z': " + noneContainsZ);

//출력 결과 : Any string contains 'z': true  

각 요소 모두가 'z'라는 문자열을 포함하고 있지 않으므로 반환되는 결과는 true 이다.

5. 요소의 통계 : count(), min(), max()

  • count() 메소드는 스트림의 요소 수를 반환한다.
List<String> strings = Arrays.asList("apple", "banana", "avocado", "cherry");
long count = strings.stream().count();
System.out.println("Number of strings: " + count);

// 출력 결과 : Number of strings: 4

위 코드에서 각 요소의 갯수는 4개 이므로 반환되는 값은 4다.


  • min() 메소드는 스트림의 최소값을 반환한다.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> min = numbers.stream().min(Integer::compareTo);
System.out.println("Minimum number: " + min.get());

//출력 결과 : Minimum number: 1

  • max() 메소드는 스트림의 최대값을 반환한다.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> max = numbers.stream().max(Integer::compareTo);
System.out.println("Maximum number: " + max.get());

//출력 결과 : Maximum number: 5

6. 요소의 연산 : sum(), average()

  • sum() 메소드는 스트림 요소들의 합을 반환한다. 요소들은 반드시 숫자형 데이터여야 한다.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream().mapToInt(Integer::intValue).sum();
System.out.println("Sum of numbers: " + sum);

//출력 결과 : Sum of numbers: 15

  • average() 메소드는 스트림 요소들의 평균값을 반환한다. 역시 모두 숫자형 데이터여야 한다.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
OptionalDouble avg = numbers.stream().mapToInt(Integer::intValue).average();
System.out.println("Average of numbers: " + avg.getAsDouble());

//출력 결과 : Average of numbers: 3.0

7. 요소의 수집 : collect()
스트림의 요소를 수집하는 용도로 사용된다. 스트림의 요소를 수집해서 다른 컬렉션에 담거나 문자열로 변환하는 등의 작업을 할 수 있다.

List<String> strings = Arrays.asList("apple", "banana", "avocado", "cherry");
List<String> longStrings = strings.stream()
                                  .filter(s -> s.length() >= 5)
                                  .collect(Collectors.toList());
System.out.println("Long strings: " + longStrings);

//출력 결과 : [apple, banana, avocado, cherry]

위 코드는 collect() 메소드를 사용해서 문자열 리스트에서 길이가 5 이상인 요소들만 골라서 새로운 리스트를 수집하는 예시다.

스트림 요소를 문자열로 결합할 수도 있다.

List<String> strings = Arrays.asList("apple", "banana", "avocado", "cherry");
String joinedString = strings.stream()
                             .collect(Collectors.joining(", "));
System.out.println("Joined string: " + joinedString);

//출력 결과 : oined string: apple, banana, avocado, cherry

위 코드는 문자열 리스트의 모든 요소를 쉼표로 구분한 문자열로 결합하는 예시다.

이처럼 collect() 메소드는 스트림의 요소를 수집하는 다양한 방법을 제공한다. 위의 예시 말고도 Collectors 클래스에서 제공하는 메소드들을 이용하여 원하는 수집 방식을 선택하여 사용할 수 있다.

profile
기록하는 사람

0개의 댓글