1년뒤의나는다르겠지

[JAVA] Stream API의 함수 1 본문

프로그래밍/JAVA

[JAVA] Stream API의 함수 1

Lirodek 2023. 8. 18. 12:46

Stream API는 JAVA8에 추가된 API입니다 함수형 프로그래밍을위한 다양한 함수들을 지원합니다.

for문, if문보다 성능이 안좋아도 Stream을 사용하는 이유

  • 가독성과 표현력 : Stream API는 선언적인 스타일을 강조합니다. 코드를 작성할 때 데이터 처리 흐름을 더 명확하게 표현할 수 있습니다. 루프를 사용하는 것보다 더 간결하고 가독성 있는 코드를 작성할 수 있습니다.

  • 함수형 프로그래밍 지원 : Stream API는 함수형 프로그래밍 스타일을 지원하며, 이는 코드의 재사용성을 증가시키고 버그 가능성을 줄여줍니다. 불변성과 부작용 없는 함수를 강조하여 프로그램의 예측 가능성을 높일 수 있습니다.

  • 병렬 처리 지원 : Stream API는 내부적으로 병렬 처리를 지원합니다. 이는 멀티코어 프로세서에서 데이터 처리 속도를 향상시킬 수 있습니다. 대용량 데이터 처리 시 병렬 스트림을 사용하여 성능을 향상시킬 수 있습니다.

  • 유연한 연산 체인 : Stream API를 사용하면 여러 연산을 체인 형태로 연결할 수 있습니다. 이로써 여러 단계의 변환과 필터링을 간결하게 표현할 수 있으며, 이로 인해 코드의 유연성과 모듈성이 향상됩니다.

  • 최적화된 내부 구현 : Java의 Stream API는 내부적으로 최적화된 구현을 제공하여 성능을 향상시킬 수 있습니다. 따라서 Stream API를 사용하면 개발자가 최적화에 신경쓰지 않아도 높은 수준의 성능을 얻을 수 있습니다.

  • 문제 해결에 집중 : Stream API를 사용하면 루프 및 조건문과 같은 세부적인 구현에 신경 쓰지 않고, 데이터 처리 문제에 집중할 수 있습니다. 이로 인해 생산성이 향상될 수 있습니다.
  • 요약 : 요약하면, Stream API는 코드의 가독성, 유연성, 모듈성을 향상시키고 병렬 처리 지원 등으로 인해 개발자들에게 사랑받는 것입니다. 성능이 조금 낮더라도 개발자의 생산성과 유지보수성을 크게 향상시켜주는 장점이 있기 때문입니다.

Stream을 반환

1.map(Function<T,R>)
Java Stream API의 map은 함수형 프로그래밍의 개념을 활용하여 컬렉션의 요소를 변환하는 데 사용되는 중요한 연산입니다. 이를 통해 컬렉션의 각 요소를 다른 형태로 변환하거나 특정 연산을 수행할 수 있습니다.
map은 다양한 형태의 변환이 가능하며, 예를들어 객체의 특정 필드를 추출하거나 문자열을 대문자로 변환하는 등 다양한 작업에 활용할 수 있습니다.

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.stream()
    .map(item->String.valueOf(item))
    .forEach(item-> System.out.println(item instanceof String));
console -------
true
true
...

2.filter(Predicate<T>)
선택된 stream의 요소가 true이면 통과하고 그렇지못하면 걸러내주는 작업을합니다
주어진 스트림의 각 요소에 대해 조건(즉, 람다 표현식이나 메서드 레퍼런스 등)을 평가합니다.
조건을 만족하는 요소만을 새로운 스트림에 포함시킵니다.
최종적으로 새로운 스트림을 반환합니다.

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.stream().filter(i->i>=2).forEach(System.out::println);
console ------
2
3
4

3.distinct()
중복되는 요소를 제거하고 새로운 스트림을 생성하는 중간연산자로
아래와같이 동작합니다.
- 주어진 스트림의 각 요소를 고유한 키로 매핑합니다. 이때 hashCode와 equals메서드를 사용합니다.
- 매핑된 고유한 키를 기반으로 중복된 요소를 제거합니다.
- 최종적으로 중복이 제거된 요소로 구성된 새로운 스트림을 반환합니다.

    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(1);
    list.add(3);
    list.add(3);
    list.stream()
        .distinct()
        .forEach(System.out::println);
console -----------------
1
3

4. dropWhile(Predicate<T>)
주어진 조건이 처음으로 거짓이 되는 요소까지의 요소들을 제외하고 나머지 요소로 구성된 새로운 스트림을 생성합니다
- 주어진 스트림의 요소를 순차적으로 평가하면서 지정된 조건을 만족하는 동안 요소를 건너뜁니다.
- 조건을 처음으로 만족하지 않는 요소부터 스트림에 포함시킵니다.
- 나머지 요소로 구성된 새로운 스트림을 반환합니다

    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    list.add(4);
    list.stream()
        .dropWhile(item -> item < 2)
        .forEach(System.out::println);
console ---------
2
3
4

5. flatMap(Function<? super Stream>)
스트림의 각 요소를 다른 스트림으로 매핑후, 이를 하나의 평면화된 스트림으로 만드는 역할을합니다.
- 주어진 스트림의 각 요소에 대해 매핑 함수를 적용하여 스트림의 스트림을 생성합니다.
- 생성된 각 스트림을 평면화하여 하나의 스트림으로 병합합니다.
- 최종적으로 평면화된 스트림을 반환합니다.

    List<List<Integer>> nestedList = Arrays.asList(
            Arrays.asList(1, 2, 3),
            Arrays.asList(4, 5, 6),
            Arrays.asList(7, 8, 9)
    );

    // 중첩된 리스트를 평면화하여 단일 레벨의 스트림 생성
    nestedList.stream()
            .flatMap(innerList -> innerList.stream())
            .collect(Collectors.toList())
            .forEach(System.out::println);
console -------
1
2
3
4 
...

6.peek(Consumer<T>)
각 스트림의 요소를 소비하면서 해당 요소에 대한 중간 연산을 수행하는 매서드입니다.
디버깅정보를 출력하거나, 기록할 수 있습니다.
- 주어진 스트림의 각 요소에 대해 주어진 동작(람다 표현식 또는 메서드 레퍼런스)을 수행합니다.
- 각 요소를 해당 동작에 전달하여 동작을 실행합니다.
- 동작의 실행 결과에는 영향을 주지 않고, 원본 스트림을 그대로 반환합니다.

	List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
    
    list.stream()
        .peek(num -> System.out.println("Processing: " + num))
        .map(num -> num * 2)
        .forEach(System.out::println);
        
     List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
console ----------
Processing: 1
2
Processing: 2
4
Processing: 3
6
Processing: 4
8

7. limit(long max)
스트림에서 처음부터 지정된 개수만큼의 요소만을 제한하여 새로운 스트림을 생성합니다.
이를 통해서 스트림의 크기를 제한하거나, 원하는 요소의 일부분만을 추출할 수 있습니다.
- 주어진 스트림에서 처음부터 지정된 개수만큼의 요소를 선택합니다.
- 선택된 요소들로 구성된 새로운 스트림을 반환합니다.

	List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
    
	list.stream().limit(2).forEach(System.out::println);
    
console -------------
1
2

8. skip(long n)
스트림에서 처음 시작부터 지정된 개수만큼의 요소를 건너뛴 후 나머지 요소들로 구성된 새로운 스트림을 생성합니다
limit과 반대의 효과를 가지고있습니다.
- 주어진 스트림에서 지정된 개수만큼의 요소를 건너뜁니다.
- 건너뛴 후 남은 요소들로 구성된 새로운 스트림을 반환합니다.

	List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);


	list.stream().skip(2).forEach(System.out::println);
    
console -------
3
4

9.sorted(Comparator<? super Integer>)
스트림의 요소를 정렬하여 새로운 스트림을 생성하는 중간 연산자로 스트림의 요소를 정렬된 순서로 처리할 수 있습니다.
Comparator의 return이 -1이면 내림차순 반대의경우엔 오름차순이됩니다
sorted가 비어있으면 기본적으로 오름차순이됩니다

	ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);

        list.stream().sorted((item1, item2) -> Integer.compare(item2, item1) ).forEach(System.out::println);
        

console ------------
3
2
1

10.takeWhile(Predicate<T>)
takeWhile은 주어진 조건을 만족하는 동안 스트림의 요소를추출하여 새로운 스트림을 만들어줍니다.
조건을 만족하지 않는 요소를 만나면 추출을 멈추고 이 이후의 요소는 무시됩니다.

	List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);

        list.stream().takeWhile(item -> item < 4).forEach(System.out::println);
        
console -------------------
1
2
3

더 많은 Stream이 있지만 추후에 공부해서 올리도록 하겠습니다