자바8(7)--Stream(중) 배우러 왔어요.

16220 단어
Java8( )——Stream( )에서 우리는 Stream 대상의 상용 방법과 용법을 알게 되었다.지금부터 Stream.collect() 방법의 사용을 깊이 있게 알아보겠습니다.

collect 기본 사용법


collect는 수집을 의미합니다. 이것은 Stream의 요소를 수집하고 귀납하여 새로운 집합 대상을 되돌려줍니다.먼저 간단한 예를 살펴보겠습니다.
public class CollectTest {

    @Data
    @AllArgsConstructor
    static class Goods {
        private String goodsName;
        private int price;
    }
    
    public static void main(String[] args) {
        List list = Arrays.asList(
                new Goods("iphoneX", 4000)
                , new Goods("mate30 pro", 5999)
                , new Goods("redmek20", 2999)
                );
        List nameList = list.stream()
            .map(Goods::getGoodsName)
            .collect(Collectors.toList());
    }

}

이 예에서 맵 방법을 통해 상품 이름을 되돌려주고 모든 상품 이름을 리스트 대상에 넣는다.
원본 코드를 보면collect 방법은 두 가지 재부팅 방법으로 구성되어 있습니다.
  • 방법1:
  •  R collect(Supplier supplier,
                      BiConsumer accumulator,
                      BiConsumer combiner);
    
  • 방법2:
  •  R collect(Collector super T, A, R> collector);
    

    그 중에서 가장 많이 사용하는 방법은 방법2이다. 이 방법은 방법1의 빠른 방식이라고 볼 수 있다. Collector에서 똑같이 Supplier supplier, BiConsumer accumulator, BiConsumer combiner라는 세 개의 매개 변수를 제공했기 때문에 그 밑바닥은 방법1에 대응하는 실현을 사용해야 한다고 추측하기 어렵지 않다.
    우리는 먼저 collect(Collector super T, A, R> collector)부터 시작해서 이것을 통해 방법 1의 용법을 천천히 이해할 수 있다.

    Collectors

    Stream.collect(Collector super T, A, R> collector) 방법의 매개 변수인 콜렉터 대상은 주로 Collectors류에서 제공한다.Collectors 클래스에는 다음과 같은 방법으로 Collector 객체를 반환하는 일련의 정적 방법이 포함되어 있습니다.
    메서드 이름
    묘사
    averagingXX
    평균을 구하다
    counting
    집합 중의 원소 개수를 구하다
    groupingBy
    컬렉션 그룹화
    joining
    집합 원소를 조립하다
    mapping
    그룹을 나누는 과정에서 값을 다시 매핑할 수 있습니다
    maxBy
    최대치를 구하다
    minBy
    최소값을 구하다
    partitioningBy
    요소 분할
    reducing
    귀납
    summarizingXX
    한데 모으다
    toCollection
    컬렉션 객체로 변환
    toConcurrentMap
    ConcurrentMap으로 변환
    toList
    List로 변환
    toMap
    맵으로 변환
    toSet
    셋으로 변환
    다음은 순서대로 각 방법의 용도를 설명한다.

    averagingXX


    averagingXX에는 averagingDouble, averagingInt, averagingLong이 포함됩니다.그것들은 평균치를 구하는 것을 나타낸다.
    double averagingInt = Stream.of(1, 2, 3)
            .collect(Collectors.averagingInt(val -> val));
    System.out.println("averagingInt:" + averagingInt);
    
    double averagingLong = Stream.of(10L, 21L, 30L)
            .collect(Collectors.averagingLong(val -> val));
    System.out.println("averagingLong:" + averagingLong);
    
    double averagingDouble = Stream.of(0.1, 0.2, 0.3)
            .collect(Collectors.averagingDouble(val -> val));
    System.out.println("averagingDouble:" + averagingDouble);
    

    이러한 매개 변수는 Lambda 표현식으로 작성할 수 있는 함수식 인터페이스로, 여기서 Lambda 표현식의 매개 변수는 Stream의 요소이고 평균 수치를 반환합니다.아래의 이 열은 상품의 평균치를 구하는 것이다.
    List list = Arrays.asList(
                    new Goods("iphoneX", 4000)
                    , new Goods("mate30 pro", 5999)
                    , new Goods("redmek20", 2999)
                    );
            
    double avgPrice = list.stream()
        .collect(Collectors.averagingInt(goods -> goods.getPrice()));
    System.out.println(" :" + avgPrice);
    

    summingXX


    averagingXX와 유사하게,summingXX 방법은 집합 중의 원소 값의 총계를 구하는 데 사용된다.
    double summingInt = Stream.of(1, 2, 3)
            .collect(Collectors.summingInt(val -> val));
    System.out.println("summingInt:" + summingInt);
    
    double summingLong = Stream.of(10L, 21L, 30L)
            .collect(Collectors.summingLong(val -> val));
    System.out.println("summingLong:" + summingLong);
    
    double summingDouble = Stream.of(0.1, 0.2, 0.3)
            .collect(Collectors.summingDouble(val -> val));
    System.out.println("summingDouble:" + summingDouble);
    

    인쇄:
    summingInt:6.0
    summingLong:61.0
    summingDouble:0.6
    

    counting()


    counting () 은 집합의 원소 개수를 되돌려줍니다.
    long count = Stream.of(1,2,3,4,5)
            .collect(Collectors.counting());
    System.out.println("count:" + count); // 5
    

    summarizingXX


    위에서 말했듯이averagingXX(평균을 구하라),summingXX(화합을 구하라),counting(총수를 구하라),만약 내가 이 세 개의 수를 동시에 얻으려면 어떻게 해야 합니까?summarizingXX를 사용할 수 있습니다.
    IntSummaryStatistics summarizingInt = Stream.of(1, 2, 3)
                    .collect(Collectors.summarizingInt(val -> val));
    System.out.println(" :" + summarizingInt.getAverage());
    System.out.println(" :" + summarizingInt.getCount());
    System.out.println(" :" + summarizingInt.getSum());
    System.out.println(" :" + summarizingInt.getMax());
    System.out.println(" :" + summarizingInt.getMin());
    

    인쇄:
     :2.0
     :3
     :6
     :3
     :1
    

    summarizingInt는 통계 결과를 IntSummaryStatistics 대상에 넣고 대상에서 서로 다른 통계 정보를 얻을 수 있습니다.

    groupingBy()


    groupingBy () 는 집합의 원소를 그룹으로 나누어 세 가지 재부팅 방법으로 구성한다
  • 재로드 1: groupingBy(Function)
  • 재부팅 2: groupingBy(Function, Collector)
  • 재부팅 3: groupingBy(Function, Supplier, Collector)
  • 이 중 재부팅 1은 재부팅 2를, 재부팅 2는 재부팅 3을 호출하기 때문에 최종적으로 재부팅 3까지 실행된다.
    우선 재부팅 1groupingBy(Function)의 사용법을 살펴보자. 이 방법은 기본적으로 새로운List에 그룹을 나눈다. 아래의 예는 상품 유형을 그룹으로 나누고 같은 유형의 상품을 하나의List에 놓는다.
    @Data
    @AllArgsConstructor
    static class Goods {
        private String goodsName;
        //  ,1: ,2: 
        private int type;
        @Override
        public String toString() {
            return goodsName;
        }
    }
    
    public static void main(String[] args) {
        List list = Arrays.asList(
                new Goods("iphoneX", 1)
                , new Goods("mate30 pro", 1)
                , new Goods("thinkpad T400", 2)
                , new Goods("macbook pro", 2)
                );
        
        Map> goodsListMap = list.stream()
            .collect(Collectors.groupingBy(Goods::getType));
        goodsListMap.forEach((key, value) -> {
            System.out.println(" " + key + ":" + value);
        });
    }
    

    인쇄:
     1:[iphoneX, mate30 pro]
     2:[thinkpad T400, macbook pro]
    

    위에서 말한 바와 같이 groupingBy(Function)는 실제로 호출되었다groupingBy(Function, Collector). 그 중에서 두 번째 파라미터Collector는 어디로 전환할 것인지를 결정했다. 기본값은 toList()이다. 참조groupingBy(Function)의 원본 코드:
    public static  Collector>>
        groupingBy(Function super T, ? extends K> classifier) {
            return groupingBy(classifier, toList());
        }
    

    따라서 우리는 groupingBy(Function, Collector)를 호출해서 수동으로 Collector를 지정할 수 있다. 변환된 요소를 Set에 넣으려면 이렇게 쓸 수 있다.
    Map> goodsListMap = list.stream()
            .collect(Collectors.groupingBy(Goods::getType, Collectors.toSet()));
    

    재부팅 2 메소드 소스를 살펴보면 재부팅 3이 호출된 것입니다.
    public static 
        Collector> groupingBy(Function super T, ? extends K> classifier,
                                              Collector super T, A, D> downstream) {
            return groupingBy(classifier, HashMap::new, downstream);
        }
    

    그 중에서 Goods::getType는classifier에 대응하고 Collectors.toSet()는downstream에 대응한다.중간에 있는 매개 변수HashMap::new는 의미가 뚜렷하다. 즉, 되돌아오는 맵의 구체적인 실현 클래스가 무엇인지, 링크드 해시 맵으로 바꾸려면 이렇게 쓸 수 있다.
    LinkedHashMap> goodsListMap = list.stream()
            .collect(Collectors.groupingBy(Goods::getType, LinkedHashMap::new, Collectors.toSet()));
            
    

    이것이 바로 중재 3의 사용 방식이다.
    Collectors의 groupingByConcurrent 방법은 바로 재부팅 3을 기반으로 한 것이고 중간 코드는 ConcurrentHashMap::new로 바뀌었을 뿐이다.
    public static 
        Collector>>
        groupingByConcurrent(Function super T, ? extends K> classifier) {
            return groupingByConcurrent(classifier, ConcurrentHashMap::new, toList());
        }
    

    GroupingBy 방법의 Collector 매개 변수는 toList(), toSet()만 사용할 수 있는 것이 아니라 더욱 유연한 사용법도 있다. 이전에 우리가 전환한 것은 모두 Map> 형식이고value에 저장된 것은 집합 대상이다. 그렇게 많은 속성을 원하지 않으면 대상 안의 상품 이름만 원한다. 즉, 우리가 얻고 싶은 것은 Map>이다. 그 중에서 키는 상품 유형이고value는 상품 이름 집합이다.
    이때Collectors.mapping()가 도움이 되었습니다. 우리는 groupingBy(Function, Collector) 방법을 사용했고 두 번째 매개 변수 전송Collectors.mapping()을 사용했습니다.
    Map> goodsListMap = 
    list.stream()
        .collect(
             Collectors.groupingBy(
                Goods::getType, 
                Collectors.mapping(Goods::getGoodsName, Collectors.toList())
             )
        );
    

    mapping () 방법은 두 개의 매개 변수가 있는데, 첫 번째 매개 변수는 되돌아오는 속성을 지정하고, 두 번째 매개 변수는 어떤 집합을 되돌아오는지 지정합니다.

    joining


    joining 방법은Stream의 요소를 연결할 수 있습니다.
    List list = Arrays.asList("hello", "world");
    String str = list.stream().collect(Collectors.joining());
    System.out.println(str); //  :helloworld
    

    구분자를 지정할 수도 있습니다.
    List list = Arrays.asList("hello", "world");
    String str = list.stream().collect(Collectors.joining(","));
    System.out.println(str); //  :hello,world
    

    이외에 String류는 하나의join방법을 제공했는데 기능은 같다
    String str2 = String.join(",", list);
    System.out.println(str2);
    

    maxBy&minBy

  • maxBy:Stream에서 가장 큰 원소를 찾아라
  • @Data
    @AllArgsConstructor
    static class Goods {
        private String goodsName;
        private int price;
    }
    
    public static void main(String[] args) {
        List list = Arrays.asList(
                new Goods("iphoneX", 4000)
                , new Goods("mate30 pro", 5999)
                , new Goods("redmek20", 2999)
                );
        
        Goods maxPriceGoods = list.stream()
            .collect(
                Collectors.maxBy(
                    Comparator.comparing(Goods::getPrice)
                )
            )
            .orElse(null);
        System.out.println(" :" + maxPriceGoods);
    }
    

    위의 예는 가장 비싼 상품을 찾는 것을 보여 준다. Collectors.maxBy() 방법은 비교기를 전송해야 하며 상품의 가격에 따라 비교해야 합니다.
    마찬가지로 가장 싼 상품을 찾으면 maxByminBy로 바꾸면 된다.

    partitioningBy


    파티션 By 방법은 섹션을 나타낸다. 조건에 따라 Stream의 요소를 두 부분으로 나누어 각각 하나의 맵에 넣는다. 맵의 키는 Boolean 유형이고 키는true 부분은 조건에 맞는 요소를 저장하고 키는false는 조건에 부합되지 않는 요소를 저장한다.
    {
        true ->  
        false ->  
    }
    

    partitioningBy 메서드는 두 가지 재로드 메서드로 구성됩니다.
  • 재로드 1:partitioningBy(Predicate)
  • 재부팅 2:partitioningBy(Predicate,Collector)
  • 그중 중재1은 중재2를 호출하기 때문에 최종적으로 중재2 방법을 호출했습니다. 우선 중재1 방법을 살펴보겠습니다.
    다음 예는 상품 유형에 따라 상품을 휴대전화 상품과 비휴대전화 상품으로 나눈다.
    @Data
    @AllArgsConstructor
    static class Goods {
        private String goodsName;
        //  ,1: ,2: 
        private int type;
        @Override
        public String toString() {
            return goodsName;
        }
    }
    
    public static void main(String[] args) {
        List list = Arrays.asList(
                new Goods("iphoneX", 1)
                , new Goods("mate30 pro", 1)
                , new Goods("thinkpad T400", 2)
                , new Goods("macbook pro", 2)
                );
        
        //  , 
        // true ->  
        // false ->  
        Map> goodsMap = list.stream()
            .collect(
                Collectors.partitioningBy(goods -> goods.getType() == 1)
            );
        //  
        List mobileGoods = goodsMap.get(true);
        System.out.println(mobileGoods);
    }
    

    partitioningBy(Predicate,Collector) 방법의 두 번째 인자는 집합 요소를 지정할 수 있습니다. 기본적으로 사용하는List 저장은 Set 저장을 사용하려면 다음과 같이 쓸 수 있습니다.
    Map> goodsMap = list.stream()
        .collect(
            Collectors.partitioningBy(
                goods -> goods.getType() == 1
                //  
                , Collectors.toSet())
        );
    

    toList & toSet & toCollection


    toList와 toSet은 Stream의 원소를 List, Set 집합으로 변환할 수 있는데 이것은 비교적 많이 사용하는 두 가지 방법이다.
    Stream stream = Stream.of(
            new Goods("iphoneX", 4000)
            , new Goods("mate30 pro", 5999)
            , new Goods("redmek20", 2999)
            );
    
    List list = stream.collect(Collectors.toList());
    Set set = stream.collect(Collectors.toSet());
    

    기본적으로 ToList는 Array List를 되돌려줍니다. ToSet은 HashSet을 되돌려줍니다. 링크드 List와 같은 다른 종류의 집합을 되돌려받으려면 toCollection 을 사용할 수 있습니다. 개발자가 어떤 집합이 필요한지 지정할 수 있습니다.
    LinkedList linkedList = stream.collect(Collectors.toCollection(LinkedList::new));
    

    toConcurrentMap


    toConcurrentMap 방법은 Stream을 ConcurrentMap으로 변환하는 것입니다. 세 가지 재부팅 방법으로 구성됩니다.
  • 중재1: toConcurrentMap(Function super T, ? extends K> keyMapper, Function super T, ? extends U> valueMapper)
  • 중재2: toConcurrentMap(Function super T, ? extends K> keyMapper, Function super T, ? extends U> valueMapper, BinaryOperator mergeFunction)
  • 중재3:toConcurrentMap(Function super T, ? extends K> keyMapper, Function super T, ? extends U> valueMapper, BinaryOperator mergeFunction, Supplier mapSupplier)
  • 그 중에서 재부팅 1은 재부팅 2를, 재부팅 2는 재부팅 3을 호출하면 최종적으로 재부팅 3 방법으로 실행된다.
    먼저 재부팅 1을 보고 두 개의 매개 변수를 제공했다
  • keyMapper:ConcurrentMap의 key 값 지정
  • valueMapper: 키에 해당하는value
  • 를 지정합니다.
    아래의 이 예는 상품의 명칭을 키로 하고 가격을value로 한다
    List list = Arrays.asList(
            new Goods("iphoneX", 4000)
            , new Goods("mate30 pro", 5999)
            , new Goods("redmek20", 2999)
    );
    ConcurrentMap goodsMap = list.stream()
            .collect(
                    Collectors.toConcurrentMap(Goods::getGoodsName, Goods::getPrice)
            );
    System.out.println(goodsMap);
    

    인쇄:
    {mate30 pro=5999, iphoneX=4000, redmek20=2999}
    

    주의: 이 방법은 키가 중복되지 않도록 요구합니다. 만약 중복된 키가 있다면 Illegal State Exception 이상을 던질 것입니다. 키가 중복되면 toConcurrentMap(Function, Function, BinaryOperator) 즉 다시 불러오기 2를 사용해야 합니다.
    다시 한 번 재부팅 2:toConcurrentMap(Function, Function, BinaryOperator)를 살펴보자. 이 방법은 앞의 두 파라미터가 재부팅 1과 같고 세 번째 파라미터는 키 충돌 상황을 처리하기 위해 개발자가value 값을 선택하여 되돌려준다.
    List list = Arrays.asList(
            new Goods("iphoneX", 4000)
            , new Goods("mate30 pro", 5999)
            , new Goods("mate30 pro", 6000) //  
            , new Goods("redmek20", 2999)
    );
    ConcurrentMap goodsMap = list.stream()
            .collect(
                    Collectors.toConcurrentMap(Goods::getGoodsName, Goods::getPrice, new BinaryOperator() {
                        @Override
                        public Integer apply(Integer price1, Integer price2) {
                            //  
                            return Math.max(price1, price2);
                        }
                    })
            );
    System.out.println(goodsMap);
    

    인쇄: {mate30 pro=6000, iphoneX=4000, redmek20=2999}이 예에서mate30pro는 키로 중복되었습니다. BinaryOperator 에서 우리는 가격이 높은 데이터를 선택하여 되돌려줍니다.
    마지막으로 리셋 3을 보십시오. 리셋 2보다 인자가 하나 더 있습니다. Supplier 개발자가 리셋을 지정할 수 있습니다. ConcurrentMap리셋 2, 리셋 3 호출, 기본값은 ConcurrentMap::new 입니다.
    참고: 네 번째 매개변수는 ConcurrentMap 또는 ConcurrentMap의 하위 클래스여야 합니다.

    소절


    이 글은 주로 Stream.collect의 용법과 Collectors류 중의 정적 방법의 사용을 설명했고 다음 글에서는 reduce에 관한 용법을 상세하게 설명할 것이다.
    정기적으로 기술 건조품을 공유하고, 함께 공부하고, 함께 진보하자!위챗 공식계정: 원숭이가 달을 치며 번호를 내린다

    좋은 웹페이지 즐겨찾기