본문 바로가기
JAVA

JAVA 스트림(Stream)

by khds 2021. 7. 1.

팀 프로젝트를 하면서 list를 다룰 때 종종 쓰던 것이 있는데 그것이 Stream이었다.   

이 글은 다른 블로그의 내용을 참고로 내가 찾기 쉽고 보기 편하게 핵심적인 설명 및 예시만 을 정리한 글이다.

아래는 참고한 블로그 이다.  내가 적은 거보다 훨씬 잘 설명해주고 있으니 확인해보기 바란다.

 

https://futurecreator.github.io/2018/08/26/java-8-streams/

 

Java 스트림 Stream (1) 총정리

이번 포스트에서는 Java 8의 스트림(Stream)을 살펴봅니다. 총 두 개의 포스트로, 기본적인 내용을 총정리하는 이번 포스트와 좀 더 고급 내용을 다루는 다음 포스트로 나뉘어져 있습니다. Java 스트

futurecreator.github.io

 

스트림을 사용하는 이유는 배열, 컬렉션에 함수(예를들면 map) 여러 개를 조합해서 원하는 결과를 필터링하고 가공할 수 있으며 람다를 이용해서 코드의 양을 줄이고 쉽게 표현할 수 있다. 즉, 배열과 컬렉션을 함수형으로 처리할 수 있다는 말이다. 또한, 스레드를 이용해 하나의 작업을 나누는 병렬 처리를 가능하게 하여 더 빠르게 처리가 가능하다.

 

스트림은 크게 3가지로 나눌 수 있다. 

  1. 생성하기 : 스트림 인스턴스 생성.
  2. 가공하기 : 필터링(filtering) 및 맵핑(mapping) 등 원하는 결과를 만들어가는 중간 작업(intermediate operations).
  3. 결과 만들기 : 최종적으로 결과를 만들어내는 작업(terminal operations).

1. 생성하기

▶ 배열 스트림

String[] arr = new String[]{"a", "b", "c"};
Stream<String> stream1 = Arrays.stream(arr);
Stream<String> stream2 = Arrays.stream(arr,1,3); // 1~3의 요소

 컬렉션 스트림

List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
Stream<String> parallelStream = list.parallelStream(); // 병렬 처리 스트림

 빈스트림

Stream<String> stream = Stream.empty();

 Stream.of()

Stream<String> stream1 = Stream.of("one", "two", "three");

 Stream.builder()

Stream<String> builderStream =
    Stream.<String>builder()
       .add("one").add("two").add("three")
       .build(); //타입은 Straem 타입

 Stream.generate()

List<Integer> generatedStream =
    Stream.generate(() -> {int x=0; return x+1;}).limit(5).collect(Collectors.toList());
    //collect는 아래에서 설명 예정, generatedStream 안의 값은 [1,1,1,1,1]

 Stream.iterate()

Stream<Integer> iteratedStream = 
    Stream.iterate(30, n -> n + 2).limit(5);   //초기값 30, 2씩증가하며 5번 진행

 기본 타입형 스트림

IntStream intStream = IntStream.range(1, 5);
LongStream longStream = LongStream.rangeClosed(1, 5);
DoubleStream doubles = new Random().doubles(3);

 문자열 스트링

String test= "hello my name is khds";
IntStream charsStream = test.chars();    // 각 문자에 해당되는 아스키코드값이 들어감.

 스트림 연결하기

Stream<String> stream1 = Stream.of("one", "two", "three");
Stream<String> stream2 = Stream.of("four", "five", "six");
Stream<String> concat = Stream.concat(stream1, stream2);

2. 가공하기

List<String> numbers = Arrays.asList("one", "two", "three");  // test list

 Filtering

List<String> stream =
    numbers.stream()
        .filter(number -> number.length()==3 && number.contains("t")).collect(Collectors.toList()); 
        // 길이가 3이고 t가 포함됨 문자열을 return
        //collect 는 3. 결과만들기에서 설명함

▶ Mapping

Stream<String> stream =
    numbers.stream()
        .map(number-> { return number+" ggg";});
      //.map(String::toUpperCase);
        
commentRepository.findAllByArticleIdOrderByCreateDateDesc(articleId).stream()
    .map(this::toCommentResponse)
    .collect(Collectors.toList());

▶ Sorting(Comparator을 이용 가능))

Stream<String> stream =
    numbers.stream()
        .map(number-> { return number+" ggg";});
        .sorted(); // 기본은 오름차순 
        
        
List<Value> sortedValues = values.stream()
            .sorted(Comparator.comparing(Value::getPercent).reversed())
            .collect(Collectors.toList());

Comparator에 대해서는 여기를 클릭(업데이트 예정!!!!!)

▶ Iterating

Stream<String> stream =
    numbers.stream()
        .map(number-> { return number+" ggg";});
        .sorted(); // 기본은 오름차순 
        .peek(System.out::println); //peak안에 있는 것은 결과에 영향 x

3. 결과 만들기

▶ 수학 연산

Long count = IntStream.of(1, 3, 5, 7, 9).count();
Long sum = LongStream.of(1, 3, 5, 7, 9).sum(); 
//이밖에 평균 ,최대 최소, 비교 등 있음

Collecting

.collect(Collectors.toList());  // Stream 타입을 List 타입으로 로 반환 
.collect(Collectors.joining(", ", "[", "]")); // 하나의 String 로 반환 1,2,3 번째 인자는 각각 1.요소의 중간에 들어가서 요소를 구분시켜준다, 2. prefix, 3. suffix 이다.

Collectors.groupingBy

public static void main(String[] args) {
    List<Integer> numbers = Arrays.asList(1, 2, 2, 2, 3, 3, 4, 5, 6, 6);
    Map<Integer, List<Integer>> map = numbers
            .stream()
            .collect(Collectors.groupingBy(n -> n));
    System.out.println(map);
    //{1=[1], 2=[2, 2, 2], 3=[3, 3], 4=[4], 5=[5], 6=[6, 6]}
}
Map<Integer, List<Product>> collectorMapOfLists =
 productList.stream()
  .collect(Collectors.groupingBy(Product::getAmount));

 

 String[] persons = {"a", "b", "a", "b", "b", "b"};
    Map<String, Long> countsByPerson = Arrays.stream(persons)
            .collect(Collectors.groupingBy(person -> person, HashMap::new, Collectors.counting()));
    System.out.println(countsByPerson);
    //{a=2, b=4}

 

추가적으로 한가지 더 말하자면 만약 stream을 통해서 값을 얻으려 할때 값이 존재하지 않는 다면 예외처리를 진행할 수 있다. 아래는 예시코드이다.

 UserGroup userGroup = userGroups.stream()
            .filter(userGroup1 -> userGroup1.getUser().getId() == userId)
            .findFirst()
            .orElseThrow(() -> new IllegalArgumentException("해당 그룹이 없거나, "
                + "ID=" + userId + "인 사용자가 ID=" + groupId + "인 그룹을 삭제할 권한이 없습니다."));

 

filter을 통해 특정한 userGroup를 찾는 코드이다. findFirst는 리턴타입을 Optional 로 설정하여 예외처리를 진행할 수 있게 한다.

 

참고

https://futurecreator.github.io/2018/08/26/java-8-streams/

https://xlffm3.github.io/java/map_using_collectors/