뭉
노재능 록리형 개발자
뭉
전체 방문자
오늘
어제
  • 분류 전체보기 (27)
    • Java (18)
      • Grammer (14)
      • Problem Solving (4)
    • JavaScript (0)
      • Grammer (0)
      • jQuery (0)
    • Spring (0)
    • DB (9)
      • SQL (6)
      • JPA (3)
    • Storage (0)
    • ETC (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
뭉

노재능 록리형 개발자

Stream API 정리
Java/Grammer

Stream API 정리

2022. 6. 4. 17:06

오늘은 간단하게  기초적인 예제와 함께 Stream API를 정리해보도록 하겠습니다. 사실 개념 설명이라기보단 단순 정리에 가까우니 참고용으로 봐주시길 바랍니다.

 

스트림 생성

//배열 스트림 생성
String[] arr = {"a", "b", "c"};
Stream<String> arrStream = Stream.of(arr);

//리스트 스트림 생성
List<String> list = Arrays.asList(arr);
Stream<String> listStream = list.stream();

스트림을 만드는 방법은 다양하고 배열이나 컬렉션이냐에 따라 생성하는 방법도 다릅니다.

여기선 가장 많이 사용되는 방법으로 스트림을 생성해보았습니다.

 

스트림의 중간 연산

⦁ 자르기 (skip, limip) 

String[] ex1 = {"a", "b", "c", "d", "e"};

Stream.of(ex1)
    .skip(1).limit(3)              //1개는 건너뛰고 3개 출력	
    .forEach(System.out::print);   //b, c, d

⦁ 필터링 (distinct, filter)

String[] ex2 = {"a", "a", "moong", "뭉", "a"};

Stream.of(ex2)
    .distinct()                     //중복제거
    .filter(s -> s.matches("\\W"))  //영어나 숫자는 제외
    .forEach(System.out::print);    //뭉

⦁ 정렬과 출력 (sorted, peek)

String[] ex3 = {"b", "a", "c", "e", "d"};

Stream.of(ex3)
    .sorted()                           //Comparable로 정렬
    .peek(System.out::print)            //abcde
    .sorted(Comparator.reverseOrder())  //역순정렬
    .forEach(System.out::print);        //edcba

⦁ 변환 (map, flatMap)

String[][] ex5 = {{"jax", "shen", "darius"}, {"lulu", "nami"}};

Stream.of(ex5)
    .flatMap(Arrays::stream)      //2뎁스를 1뎁스로
    .map(String::toUpperCase)     //대문자로 변경
    .forEach(System.out::print);  //JAX, SHEN, DARIUS, LULU, NAMI

 

스트림의 최종 연산

⦁ 갯수 (count)

String[] ex7 = {"a", "b", "c", "", " "};
		
Long count = Stream.of(ex7)
        .filter(s -> !s.isBlank())   //빈 문자열 거르기
        .count();   //갯수 구하기

System.out.println(count);  //3

⦁ 조건 판별 (allMatch, anyMatch, noneMatch)

tring[] ex8 = {"가", "a", "A", "1"};
		
boolean isAllTrue = Stream.of(ex8)
        .allMatch(s -> (s.length() == 1));  //모두 길이가 1

boolean isAnyTrue = Stream.of(ex8)
        .anyMatch(s -> s.matches("\\d"));   //적어도 하나는 숫자

boolean isNoneTrue = Stream.of(ex8)
        .noneMatch(s -> s.matches("\\s"));  //모두 길이가 1

System.out.println(isAllTrue);   //true
System.out.println(isAnyTrue);   //true
System.out.println(isNoneTrue);  //true

⦁ 누산 (reduce)

Integer[] ex9 = {1, 2, 3};

int multiple = Stream.of(ex9)
        .reduce(1, (r, a) -> r * a);   //초기값 1에 * 1 * 2 * 3

System.out.println(multiple);   //6

⦁ 자료형 담기 (collect, toArray)

String[] data = {"one", "two", "three"};
		
List<String> list = Stream.of(data)
        .collect(Collectors.toList());   //리스트에 담기

String[] arr = list.stream()
        .toArray(String[]::new);   //배열에 담기

Map<String, Integer> map = Stream.of(data)
        .collect(Collectors.toMap(s -> s, String::length));   //맵에 담기

String text = Stream.of(data)
        .collect(Collectors.joining(", "));   //문자열에 담기

System.out.println(list);                  //[one, two, three]
System.out.println(Arrays.toString(arr));  //[one, two, three]
System.out.println(map);                   //{one=3, two=3, three=5}
System.out.println(text);                  //one=3, two=3, three=5

⦁ 그룹핑 - 2분할 [collect(Collectors.partitioningBy)]

String[] names = {"junho", "jinho", "hoya", "lee", "lulu", "darius", "jax"};
		
Map<Boolean, List<String>> nameByStart = Stream.of(names)
        .collect(Collectors.partitioningBy(s -> s.startsWith("j")));   //2분할(j로 시작인지)

System.out.println(nameByStart.get(true));    //[junho, jinho, jax]
System.out.println(nameByStart.get(false));   //[hoya, lee, lulu, darius]

위 소스는 사람들의 이름이 저장된 배열을 j로 시작하는 이름과 j로 시작하지 않는 이름 둘로 나눈 것입니다.

즉 partitioningBy는 매개변수로 Predicate 타입의 함수를 매개변수로 받습니다.

Predicate 조건에 따라 스트림이 2분할로 나누어 지고 해당 데이터들은 리스트안에 저장됩니다.  그리고 해당 리스트는 Map안에 저장되어 Boolean 타입의 값들로 꺼낼 수 있습니다. 

 

⦁ 그룹핑 - 재가공하여 자료형 담기 [collect(Collectors.mapping)]

String[] names = {"junho", "jinho", "hoya", "lee", "lulu", "darius", "jax"};
		
Map<Boolean, Set<String>> nameByStartMap = Stream.of(names)
        .collect(Collectors.partitioningBy(
            s -> s.startsWith("j"),   //2분할(j로 시작하는지)
            Collectors.mapping(String::toUpperCase, Collectors.toSet())   //재가공
        ));   

System.out.println(nameByStartMap.get(true));    //[JUNHO, JAX, JINHO]
System.out.println(nameByStartMap.get(false));   //[LULU, DARIUS, LEE, HOYA]

이전에 봤던 2분할과 같은 예시입니다만 partitioningBy 안에 매개변수가 하나 더 추가되었습니다.

위와 같이 mapping을 사용하면 자료를 재가공하여 원하는 자료형에 담을 수 있습니다. 분할된 이름들을 대문자로 재가공한 후 Set에 담아 마찬가지로 Map 안에 저장됩니다.

 

⦁ 그룹핑 - n분할 [collect(Collectors.groupingBy)]

String[] names = {"junho", "jinho", "hoya", "lee", "lulu", "darius", "jax"};
		
Map<Integer, List<String>> nameByLength = Stream.of(names)
        .collect(Collectors.groupingBy(String::length));   //n분할(길이에 따른 분할)

System.out.println("3글자 이름 리스트 : " + nameByLength.get(3));   //[lee, jax]
System.out.println("4글자 이름 리스트 : " + nameByLength.get(4));   //[hoya, lulu]
System.out.println("5글자 이름 리스트 : " + nameByLength.get(5));   //[junho, jinho]

groupingBy는 n분할하여 데이터를 저장합니다. partitioningBy와 차이점은 매개변수가 Predicate가 아닌 Function 타입의 함수를 받으며 위 소스는 길이에 따라 n분할하여 마찬가지로 맵과 리스트에 저장됩니다. 

이때 키는 Boolean 타입이 아닌 매개변수 Function의 리턴타입이 키로 지정됩니다.

 

⦁ 그룹핑 - 통계값 얻기 (Collectors.counting, Collectors.maxBy, Collectors.averagingInt)

분할한 뒤에 요소들의 갯수를 얻거나 최대값/최소값 그리고 평균같은 통계값을 얻을 수도 있습니다. 밑의 예제들은 자연수와 자연수가 아닌 수로 분류하고 해당 값들을 각각 구하는 예제입니다. 

//요소 갯수 구하기
Integer[] numbers = {3, -25, 54, 1, -6, 20, -90, -1, 1000, 50};

Map<Boolean, Long> byNaturalCount = Stream.of(numbers)
        .collect(Collectors.partitioningBy(
            n -> n > 0,            //2분할(자연수인지 아닌지)
            Collectors.counting()  //분할된 원소 갯수
        ));   

System.out.println("자연수 갯수 : " + byNaturalCount.get(true));   //6
System.out.println("자연수가 아닌 수의 갯수 : " + byNaturalCount.get(false));  //4

반환값이 Long이므로 Map을 선언하실 때 값의 타입을 Long으로 설정해주셔야 합니다.

 

//최대값 구하기
Integer[] numbers = {3, -25, 54, 1, -6, 20, -90, -1, 1000, 50};

Map<Boolean, Optional<Integer>> byNaturalMax = Stream.of(numbers)
        .collect(Collectors.partitioningBy(
            n -> n > 0,                                  //2분할(자연수인지 아닌지)
            Collectors.maxBy(Comparator.naturalOrder())  //최대값...naturalOrder 생략 불가능
        ));   

System.out.println("50 미만인 숫자 중 최대값 : " + byNaturalMax.get(true).orElse(0));   //6
System.out.println("50 이상인 숫자 중 최소값 : " + byNaturalMax.get(false).orElse(0));  //4

최대값/최소값을 구할 때 MaxBy는 매개변수로 Comparator 함수를 받으며 이때 naturalOrder는 생략 불가능합니다.

또한 리턴타입이 Optional<T>인 것을 주의하셔야 합니다. 

 

//평균 구하기
Integer[] numbers = {3, -25, 54, 1, -6, 20, -90, -1, 1000, 50};
		
Map<Boolean, Double> byNaturalAvg = Stream.of(numbers)
        .collect(Collectors.partitioningBy(
            n -> n > 0,                      //2분할(자연수인지 아닌지)
            Collectors.averagingInt(n -> n)  //평균값
        ));   

System.out.println("50 미만인 숫자 평균 : " + byNaturalAvg.get(true));   //188.0
System.out.println("50 이상인 숫자 평균 : " + byNaturalAvg.get(false));  //-30.5

averagingInt는 매개변수로 ToIntFunction 함수를 받으며 리턴 타입이 Double인 것에 주의하셔야 합니다.

'Java > Grammer' 카테고리의 다른 글

Optional의 옳바른 사용법  (0) 2022.06.12
입출력 스트림 (2) - File I/O  (0) 2022.05.24
입출력 스트림 (1) - System.out.println와 I/O  (0) 2022.05.22
ShallowCopy와 DeepCopy 완전 정복  (0) 2022.05.07
객체의 중복/정렬 그리고 Collection  (0) 2022.04.24
    'Java/Grammer' 카테고리의 다른 글
    • Optional의 옳바른 사용법
    • 입출력 스트림 (2) - File I/O
    • 입출력 스트림 (1) - System.out.println와 I/O
    • ShallowCopy와 DeepCopy 완전 정복
    뭉
    뭉
    노재능 록리형 개발자

    티스토리툴바