【问题标题】:Use the streaming api to loop over fields instead of over objects使用流 api 循环遍历字段而不是遍历对象
【发布时间】:2018-03-11 02:00:06
【问题描述】:

我想使用 Java 8 的流 API 进行以下练习:

  • 给定一个List<Person> persons
  • Person 是一个简单的 POJO,具有名称(字符串)和年龄(int)属性
  • 构造以下字符串Names=[joe,diana,thomas]&Ages=[34,52,19]

我可以用流轻松做以下事情:

String result = persons.stream()
    .map(p -> "(" + p.getName() + "," + p.getAge() + ")")
    .collectors(Collectors.joining("&","persons=[","]");

这将给出persons=[(joe,34)&(diana,52)&(thomas,19)],但我不知道如何在多个人员对象的字段上连接,而不是在每个人的属性上单独连接。就像我需要将人员平面映射到一个属性,然后再次将人员列表平面映射到下一个属性,然后组合这些输出。语法是什么?

Person 类非常简单。如果可能的话,我想要一个以这样一种方式编写的解决方案,即使用反射来循环一个类的​​所有字段,而不是只检查硬编码的“名称”和“年龄”字段。假设所有字段都是简单的属性,如 String 或 int(没有嵌套的类或集合)。

【问题讨论】:

  • 您可能需要编写自己的Collector。如果流的对象不是都属于同一类型,您还需要决定结果应该是什么。
  • 在两个字段(如姓名和年龄)的情况下,您可以使用 '''Collectors.toMap''' 和 '''Collectors.collectingandThen''' 编写一个简单的解决方案,但它会得到在更通用的情况下更复杂,您可能需要像@Andreas 所说的那样编写自定义收集器。
  • @alainlompo 地图只有在您绝对确定名称是唯一的情况下才能使用。由于世界上有更多的Zhang Wei(或James Smith),你不能保证,所以地图不是一个好的选择。

标签: java collections java-stream


【解决方案1】:

我猜想有一个 Person 类(正如你所描述的),你需要一个自定义收集器:

 String result = List.of(new Person(1, "david"), new Person(33, "eugene"))
            .stream()
            .parallel()
            .collect(Collector.of(
                    () -> new StringJoiner[]{new StringJoiner(",", "[", "]"), new StringJoiner(",", "[", "]")},
                    (sj, per) -> {
                        sj[0] = sj[0].add("" + per.getAge());
                        sj[1] = sj[1].add(per.getName());
                    },
                    (left, right) -> {
                        left[0] = left[0].merge(right[0]);
                        left[1] = left[1].merge(right[1]);
                        return left;
                    },

                    x -> "Names = " + x[1].toString() + "&Ages = " + x[0].toString()
            ));

【讨论】:

    【解决方案2】:

    你可以使用

    String result = persons.stream()
        .flatMap(p -> Stream.of(new AbstractMap.SimpleEntry<>(true, p.getName()),
                                new AbstractMap.SimpleEntry<>(false, ""+p.getAge())))
        .collect(Collectors.collectingAndThen(
            Collectors.partitioningBy(Map.Entry::getKey,
                Collectors.mapping(Map.Entry::getValue, Collectors.joining(",", "[", "]"))),
            m -> "Names="+m.get(true)+"&Ages="+m.get(false)));
    

    AbstractMap.SimpleEntry 只是缺少的标准 Pair 类型的替代品。如果你的项目中有Pair 类型,你可以使用它来代替;您只需将 Map.Entry::getKeyMap.Entry::getValue 方法引用替换为正确的引用即可检索第一个响应。对的第二个值。

    上面的解决方案是针对两个属性的情况量身定制的。可以轻松适应两个以上属性的替代方法是

    String result = persons.stream()
        .flatMap(p -> Stream.of(new AbstractMap.SimpleEntry<>("Names", p.getName()),
                                new AbstractMap.SimpleEntry<>("Ages", ""+p.getAge())))
        .collect(Collectors.collectingAndThen(
            Collectors.groupingBy(Map.Entry::getKey,
                Collectors.mapping(Map.Entry::getValue, Collectors.joining(",", "[", "]"))),
            m -> m.entrySet().stream()
                  .map(e -> e.getKey()+"="+e.getValue()).collect(Collectors.joining("&"))));
    

    【讨论】:

      猜你喜欢
      • 2020-06-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-30
      • 2017-06-12
      • 2015-10-31
      • 2014-11-30
      • 1970-01-01
      相关资源
      最近更新 更多