java Stream groupingBy:减少到列表的第一个元素

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34495816/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 22:58:30  来源:igfitidea点击:

Stream groupingBy: reducing to first element of list

javajava-8groupingjava-streamcollectors

提问by Fabio B.

I have a List<Valuta>which can be represented (simplified) JSON-style:

我有一个List<Valuta>可以表示(简化)JSON 样式的:

[ { codice=EUR, description=Euro, ratio=1 }, { codice=USD, description=Dollars, ratio=1.1 } ]

[ { codice=EUR, description=Euro, ratio=1 }, { codice=USD, description=Dollars, ratio=1.1 } ]

I want to transform that in a Map<String, Valuta>like this:

我想把它转换成Map<String, Valuta>这样:

{ EUR={ codice=EUR, description=Euro, ratio=1 }, USD={ codice=USD, description=Dollars, ratio=1.1 }}

{ EUR={ codice=EUR, description=Euro, ratio=1 }, USD={ codice=USD, description=Dollars, ratio=1.1 }}

I wrote this one-liner:

我写了这个单行:

getValute().stream().collect(Collectors.groupingBy(Valuta::getCodice));

but this returns a Map<String, List<Valuta>>instead of what I need.

但这返回一个Map<String, List<Valuta>>而不是我需要的。

I suppose mapping()function would work for me, but don't know how.

我想mapping()函数对我有用,但不知道如何。

回答by Tunaki

Actually, you need to use Collectors.toMaphere instead of Collectors.groupingBy:

实际上,您需要在Collectors.toMap此处使用而不是Collectors.groupingBy

Map<String, Valuta> map = 
    getValute().stream()
               .collect(Collectors.toMap(Valuta::getCodice, Function.identity()));

groupingByis used to group elements of a Stream based on a grouping function. 2 Stream elements that will have the same result with the grouping function will be collected into a Listby default.

groupingBy用于根据分组功能对流的元素进行分组。2 与分组功能有相同结果的流元素将List默认收集到a中。

toMapwill collect the elements into a Mapwhere the key is the result of applying a given key mapper and the value is the result of applying a value mapper. Note that toMap, by default, will throw an exception if a duplicate is encountered.

toMap将元素收集到 a 中Map,其中键是应用给定键映射器的结果,值是应用值映射器的结果。请注意toMap,默认情况下,如果遇到重复项,将抛出异常。

回答by Philippe

It's a bit late in the game, but try this:

游戏有点晚了,但试试这个:

Map<String, Valuta> map = 
    getValute().stream()
               .collect(Collectors.groupingBy(Valuta::getCodice,
                            Collectors.collectingAndThen(
                                Collectors.toList(), 
                                values -> values.get(0))));

回答by Pshemo

You could use Collectors.toMap(keyMappingFunction, valueMappingFunction)

你可以用 Collectors.toMap(keyMappingFunction, valueMappingFunction)

Map<String, Valuta> map = list
        .stream()
        .collect(Collectors.toMap(Valuta::getCodice, v -> v));

You can replace v->vwith Function.identity()if you find it more readable.

如果您发现它更具可读性,您可以替换v->vFunction.identity()

回答by 3ygun

The toMapversion which opts to choose the 1st value on collisions instead of throwing an exception is:

toMap其中选择只选择在碰撞第一个值,而不是抛出一个异常的版本:

Collectors.toMap(keyMapper, valueMapper, mergeFunction)

Collectors.toMap(keyMapper, valueMapper, mergeFunction)

Map<String, Valuta> map = list
        .stream()
        .collect(Collectors.toMap(Valuta::getCodice, v -> v, (v1, v2) -> v1));

回答by Soudipta Dutta

Here are 3 methods.

这里有3种方法。

public class Test1 {
  static class Foo {
    public int id, targetCost, actualCost;
    public String ref;

    public Foo(int id, String ref, int actualCost, int targetCost) {

      this.id = id;
      this.targetCost = targetCost;
      this.actualCost = actualCost;
      this.ref = ref;
    }

    public int getId() {
      return id;
    }

    public void setId(int id) {
      this.id = id;
    }

    public int getTargetCost() {
      return targetCost;
    }

    public void setTargetCost(int targetCost) {
      this.targetCost = targetCost;
    }

    public int getActualCost() {
      return actualCost;
    }

    public void setActualCost(int actualCost) {
      this.actualCost = actualCost;
    }

    public String getRef() {
      return ref;
    }

    public void setRef(String ref) {
      this.ref = ref;
    }

    @Override
    public String toString() {
      return " [id=" + id + ", targetCost="
    + targetCost + ", " + "actualCost=" 
          + actualCost + ", ref=" + ref
          + "]";
    }

  }// foo

  public static void main(String[] args) {

    List<Foo> list = Arrays.asList(

        new Foo(1, "P1", 300, 400), new Foo(2, "P2", 600, 400), new Foo(3, "P3", 30, 20),
        new Foo(3, "P3", 70, 20), new Foo(1, "P1", 360, 40), new Foo(4, "P4", 320, 200),
        new Foo(4, "P4", 500, 900)

    );
    // Method 1  : 
    Map<Integer, List<Foo>> collect = list.stream()
        .collect(
            Collectors.groupingBy(
                Foo::getId, 
                Collectors.collectingAndThen(

            Collectors.toList(),

            Function.identity()

        )// andthen

        )// gr

        );
    System.out.println(collect);
    /*
    {
        1=[ [id=1, targetCost=400, actualCost=300, ref=P1],
        id=1, targetCost=40, actualCost=360, ref=P1]],

         2=[ [id=2, targetCost=400, actualCost=600, ref=P2]],

          3=[ [id=3, targetCost=20, actualCost=30, ref=P3], 
           [id=3, targetCost=20, actualCost=70, ref=P3]], 

           4=[ [id=4, targetCost=200, actualCost=320, ref=P4], 
            [id=4, targetCost=900, actualCost=500, ref=P4]]

          }
  */

    // Method 2

    Map<Integer, List<Foo>> collect2 = list.stream().collect(
        Collectors.groupingBy(
            Foo::getId, 
            Collectors.mapping(
                Function.identity(), 
                Collectors.toList())));

    System.out.println(collect2);
    /*
     {
  1=[ [id=1, targetCost=400, actualCost=300, ref=P1], 
   [id=1, targetCost=40, actualCost=360, ref=P1]],

    2=[ [id=2, targetCost=400, actualCost=600, ref=P2]],

     3=[ [id=3, targetCost=20, actualCost=30, ref=P3],
       [id=3, targetCost=20, actualCost=70, ref=P3]],

        4=[ [id=4, targetCost=200, actualCost=320, ref=P4], 
         [id=4, targetCost=900, actualCost=500, ref=P4]]

       }

*/

    // Method 3 

    // If you need to compare something the you can use Compare.comparing

     Map<Integer, List<Foo>> collect3 = list
     .stream()
     .sorted( Comparator.comparing(Foo::getId)
         .thenComparing(Foo::getActualCost)
         .thenComparing(Foo::getTargetCost)    )

     .collect(                 

    Collectors.groupingBy(ch -> ch.id)     



         );

     System.out.println(collect3);


/*
{
  1=[ [id=1, targetCost=400, actualCost=300, ref=P1],  
  [id=1, targetCost=40, actualCost=360, ref=P1]],

   2=[ [id=2, targetCost=400, actualCost=600, ref=P2]],

    3=[ [id=3, targetCost=20, actualCost=30, ref=P3], 
     [id=3, targetCost=20, actualCost=70, ref=P3]],

      4=[ [id=4, targetCost=200, actualCost=320, ref=P4], 
       [id=4, targetCost=900, actualCost=500, ref=P4]]

     }
*/


  }// main

}

回答by Kedar Gokhale

Not completely related to this question but quiet similar case .If you want to group list by multiple params and after that need return object of different type as value then you can try this solution . Hope helps to someone!

与此问题不完全相关,但类似的情况很安静。如果您想按多个参数对列表进行分组,然后需要将不同类型的对象作为值返回,那么您可以尝试此解决方案。希望对某人有帮助!

public Map<Integer, Map<String, ObjectTypeB>> setObjectTypeB(List<ObjectTypeA> typeAList) {

   Map<Integer, Map<String, ObjectTypeB>> map = typeAList.stream()
        .collect(groupingBy(ObjectTypeA::getId,
         groupingBy(ObjectTypeA::getDate, 
         collectingAndThen(mapping(this::getObjectTypeB,toList()),values -> values.get(0)))));    
    return map;
}

    public ObjectTypeB getObjectTypeB(ObjectTypeA typeA) {
       ObjectTypeB typeB = new ObjectTypeB();
       typeB.setUnits(typeA.getUnits());
       return typeB;
}