[Java] Java Stream

Java Stream

지난번에 이야기한 Java8의 핵심 기능, 자바 Optional에 이어서, 오늘은 Stream에 대해서 정리합니다.


Java Stream 정의

Java Stream은 배열이나 컬렉션의 저장 요소를 하나씩 참조해서 람다식으로 처리할 수 있게 해주는 반복자입니다. 이전에는 Java Stream이 없었을 때는 Iterator이라는 반복자를 사용하였습니다.

  • Java Iterator
ArrayList<Integer> list = new ArrayList<>(Array.asList(1, 2, 3));
Iterator<Integer> it = list.iterator();

while(it.hasNext()){
        int num = it.next();
        System.out.print(num + " ");
}
// output : 1, 2, 3
  • Java Stream
ArrayList<Integer> list = new ArrayList<>(Array.asList(1, 2, 3));
Stream<Integer> stream = list.stream();

stream.forEach(num -> System.out.print(num + " "));
// output : 1, 2, 3

Java Stream 특징

  • 배열 또는 컬렉션 인스턴스에 함수 여러 개를 조합해서 원하는 결과를 필터링할 수 있으며 가공된 결과를 얻을 수 있습니다.
  • 람다를 통해 코드를 간결하게 표현할 수 있습니다.
  • 배열과 컬렉션을 통해 함수형으로 처리할 수 있습니다.
  • 병렬처리가 가능합니다.

Java Stream 구조 및 사용법

스트림은 다음과 같은 절차로 사용할 수 있습니다.

  • 스트림 생성 : 스트림 인스턴스 생성
  • 중개 연산 : 필터링(filtering) 및 맵핑(mapping) 등으로 원하는 결과를 만들어가는 중간 과정
  • 최종 연산 : 최종적으로 결과를 만드는 작업

스트림 생성

  • 배열 스트림 : Arrays.stream() 을 사용합니다.
int[] array = {1, 2, 3};

IntStream intStream = Arrays.stream(array);
  • 컬렉션 스트림 : 인터페이스의 default method를 사용합니다.
List<Integer> list = Arrays.asList(1, 2, 3);

Stream<Integer> stream = list.streams();
Stream<Integer> parallelStream = list.parallelStream();

중개 연산

  • Filter: 스트림 내 요소 중 조건에 맞는 것을 고릅니다.
List<String> arrary = Arrays.asList("apple", "banana", "melon");
Stream<String> stream = arrary.stream().filter(x -> x.contains("a"));
// output : ['apple', 'banana']
  • Map : 스트림 내 요소들이 특정 로직 수행 후 새로운 스트림을 반환합니다.
List<Integer> list = Arrays.asList(1, 2, 3);
Stream<Integer> stream = list.stream().map(x -> x + 1);
// output : [2, 3, 4]
  • flatMap : 여러 스트림을 하나의 스트림으로 합칩니다.
String[][] array = new String[][]{{"a1", "a2"}, {"b1", "b2"}, {"c1", "c2"}};
Stream<String> stream = Arrays.stream(arr).flatMap(s -> Arrays.stream(s));
// output : ["a1", "a2", "b1", "b2", "c1", "c2"]
  • sorted : 정렬합니다.
List<Integer> list = Arrays.asList(1, 4, 3);
List<Integer> sortedList = list.stream().sorted().collect(Collectors.toList()); // 오름차순
// output : [1, 3, 4]

List<Integer> sortedList = list.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList()); // 내림차순
// output : [4, 3, 1]

최종 연산

  • count, min, max, sum : 갯수, 최소, 최대, 합
  • forEach : 요소를 돌면서 실행합니다.
  • collect : 스트림의 값들을 모을 수 있습니다. ex) toMap(), toSet(), toList()
List<Integer> arr = Arrays.asList(1, 2, 3, 3, 4, 5, 5);

Set<Integer> set = arr.stream().collect(Collectors.toSet());
set.forEach(x -> System.out.print(x + " " ));
// output : 1 2 3 4 5
  • reduce : 누적된 값을 계산합니다.
OptionalInt reduced =
    IntStream.range(1, 4) // [1, 2, 3]
    .reduce((a, b) -> {
        return Integer.sum(a, b);
    });
    // output : 6 (1 + 2 + 3)

    int reducedTwoParams =
    IntStream.range(1, 4) // [1, 2, 3]
    .reduce(10, Integer::sum);
    // output : 16 (10 + 1 + 2 + 3)
}

마무리.

오늘은 간단하게 stream에 대해 알아보고 이를 사용한 예시를 사용하였습니다. 현업에서 stream과 optional, lambda식을 잘 쓰는 것은 클린 코드를 만드는데 좋은 스킬 중 하나임을 개발하면서 자주 느낍니다. 오늘 사용한 예제말고도 다른 예제는 찾아보면서 좋은 개발할 수 있으면 좋겠습니다.

감사합니다.


출처