I want to use Java 8's stream api for the following exercise:
List<Person> persons
Person
is a simple POJO with a name (string) and age (int) propertyNames=[joe,diana,thomas]&Ages=[34,52,19]
What I can easily do with streams is the following:
String result = persons.stream()
.map(p -> "(" + p.getName() + "," + p.getAge() + ")")
.collectors(Collectors.joining("&","persons=[","]");
Which will give persons=[(joe,34)&(diana,52)&(thomas,19)]
, but I don't know how I can concatenate on the fields across mutiple person objects rather then on the properties of each person individually. It's like I need to flatmap the person to one property, and afterwards flatmap the list of persons a second time for the next property, and then combine these outputs. What would the syntax be for this?
The Person class is very simple. If possible, I would like a solution that is written in such a way that reflection is used to loop over ALL fields of a class instead of checking hardcoded only the "name" and "age" fields. Assume that all fields are simple properties like String or int (there are no nested classes or collections).
I guess having a class Person
(as you describe), you would need a custom collector for this:
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()
));
You could use
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
is just a stand-in for the missing standard Pair
type. If you have a Pair
type in your project, you can use it instead; you just have to replace the Map.Entry::getKey
and Map.Entry::getValue
method references with the right ones to retrieve the first resp. second value of the pair.
The solution above is tailored to the case of two properties. An alternative which can be easily adapted to more than two properties would be
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("&"))));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With