【问题标题】:Stream groupingBy: reducing to first element of list [duplicate]Stream groupingBy:减少到列表的第一个元素[重复]
【发布时间】:2016-04-02 10:31:09
【问题描述】:

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

[ { 代码=欧元,描述=欧元,比率=1 },{ 代码=美元, 描述=美元,比率=1.1 } ]

我想将其转换为 Map<String, Valuta>,如下所示:

{ EUR={ codice=EUR, description=Euro, ratio=1 }, USD={ codice=USD, 描述=美元,比率=1.1 }}

我写了这个单行:

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

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

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

【问题讨论】:

    标签: java java-8 grouping java-stream collectors


    【解决方案1】:

    其实这里需要使用Collectors.toMap,而不是Collectors.groupingBy

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

    groupingBy 用于根据分组函数对 Stream 的元素进行分组。 2 与分组功能结果相同的Stream元素,默认会被收集到List中。

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

    【讨论】:

    • 如何对groupingBy做同样的事情?我不能使用toMap,因为我必须期待来自键映射器的重复键。我看到groupingBy 有下游收集器的签名作为第二个参数,但还不能让它工作。
    • 假设我想要 Map 而不是 Map,我需要 Valuta.getDescription() ,基本上,这可能吗?
    • 请注意Collectors.toMap 会抛出异常,如果你有重复的键!它们并不完全等价。
    • 发现了这一点 - 您可以使用 .toMapmergeFunction 来简单地删除或替换任何重复项并避免额外的对象分配:stackoverflow.com/a/32313069/97964
    【解决方案2】:

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

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

    【讨论】:

    • 这就是我要找的东西
    • 这是需要的,以防您确实有重复但不想使用它
    • 您可以使用.toMapmergeFunction 来简单地删除任何重复项并避免额外的对象分配:stackoverflow.com/a/32313069/97964
    • 为什么要在流中创建列表?在这里,您的解决方案在不创建列表的情况下进行了调整:getValute().stream().collect(groupingBy(Valuta::getCodice, reducing(null, identity(), (first, last) -&gt; last)))
    【解决方案3】:

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

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

    如果您觉得v-&gt;v 更易读,您可以将v-&gt;v 替换为Function.identity()。注意toMap,默认情况下,如果遇到重复,会抛出异常。

    【讨论】:

      【解决方案4】:

      toMap 版本选择在碰撞时选择第一个值而不是抛出异常是:

      Collectors.toMap(keyMapper, valueMapper, mergeFunction) 前:

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

      【讨论】:

        【解决方案5】:

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

        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;
        }
        

        【讨论】:

          【解决方案6】:

          这里有 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
          
          }
          

          【讨论】:

          • 这似乎是执行默认groupingBy 行为的三种等效方式。这很有趣,但我不认为 OP 想要什么(这是一个单一的 k -> v)。 :)
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-11-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-09-21
          • 1970-01-01
          相关资源
          最近更新 更多