使用流API循环字段而不是对象

问题描述 投票:0回答:3

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

  • 给予
    List<Person> persons
  • Person
    是一个简单的POJO,具有名称(字符串)和年龄(整数)属性
  • 构造以下字符串
    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 (没有嵌套类或集合)。

java collections java-stream
3个回答
4
投票

我想有一个类

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()
            ));

3
投票

你可以使用

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::getKey
Map.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("&"))));

0
投票

或者,这可以通过

Collectors.teeing
来完成,但它从 Java 12 开始可用:

String res =
    Stream.of(new Person(34, "joe"), new Person(52, "diana"), new Person(19, "thomas"))
        .collect(
            Collectors.teeing(
                Collectors.mapping(Person::getName, Collectors.joining(",")),
                Collectors.mapping(p -> "" + p.getAge(), Collectors.joining(",")),
                (names, ages) -> String.format("Names=[%s]&Ages=[%s]", names, ages)));

System.out.println(res); // Names=[joe,diana,thomas]&Ages=[34,52,19]

Collectors.teeing
需要 3 个参数,在这段代码中专门进行了剪裁:

  • 两个 Collector 对象,用于收集列表中人员的姓名和年龄。
  • 一个 BiFunction,用于将两个收集器的结果组合成一个字符串。
© www.soinside.com 2019 - 2024. All rights reserved.