我想使用 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 (没有嵌套类或集合)。
我想有一个类
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()
));
你可以使用
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("&"))));
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 个参数,在这段代码中专门进行了剪裁: