import java.util.*;
import java.util.stream.*;
/* Name of the class has to be "Main" only if the class is public. */
public class Main {
static List<Foo> joinedCategoriesMap(List<Foo> input) {
return input
.stream()
.collect(Collectors.groupingBy(Foo::getId))
.entrySet().stream()
.map(e -> new Foo(
e.getKey(),
e.getValue().get(0).name,
e.getValue().stream().map(Foo::getCategory).collect(Collectors.toList()))
)
.collect(Collectors.toList());
}
static List<Foo> joinedCategoriesReduce(List<Foo> input) {
return input
.stream()
.collect(Collectors.groupingBy(Foo::getId)) // Map<Integer, List<Foo>>
.entrySet().stream()
.map(e -> e.getValue().stream() // Stream<Foo>
.
reduce(new Foo
(e.
getKey(), e.
getValue().
get(0).
getName(),
(String)null), Foo
::merge
) )
.collect(Collectors.toList());
}
final Foo f1 = new Foo(1L, "a", "c1");
final Foo f2 = new Foo(1L, "a", "c2");
final Foo f3 = new Foo(2L, "a", "c1");
final List
<Foo
> li
= List.
of(f1, f2, f3
);
joinedCategoriesMap
(li
).
forEach(System.
out::println
);
System.
out.
println("Reduce: "); joinedCategoriesReduce
(li
).
forEach(System.
out::println
); }
static class Foo {
private List<String> categories;
this.id = id;
this.name = name;
this.category = cat;
}
this.id = id;
this.name = name;
this.categories = cats;
}
static Foo merge(Foo accum, Foo other) {
if (null == accum.categories) {
accum.categories = new ArrayList<>();
if (null != accum.category) {
accum.categories.add(accum.category);
accum.category = null;
}
}
accum.categories.add(other.category);
return accum;
}
public Long getId
() { return id
; } public void setId
(Long id
) { this.
id = id
; }
public String getName
() { return name
; } public void setName
(String name
) { this.
name = name
; }
public String getCategory
() { return category
; }
@Override
return String.
format("Foo { id=%d, name=%s, category=%s, categories=%s }", id, name, category, categories
); }
}
}
aW1wb3J0IGphdmEudXRpbC4qOwppbXBvcnQgamF2YS51dGlsLnN0cmVhbS4qOwoKLyogTmFtZSBvZiB0aGUgY2xhc3MgaGFzIHRvIGJlICJNYWluIiBvbmx5IGlmIHRoZSBjbGFzcyBpcyBwdWJsaWMuICovCnB1YmxpYyBjbGFzcyBNYWluIHsKCglzdGF0aWMgTGlzdDxGb28+IGpvaW5lZENhdGVnb3JpZXNNYXAoTGlzdDxGb28+IGlucHV0KSB7CgkgICAgcmV0dXJuIGlucHV0CgkgICAgICAgICAgICAuc3RyZWFtKCkKCSAgICAgICAgICAgIC5jb2xsZWN0KENvbGxlY3RvcnMuZ3JvdXBpbmdCeShGb286OmdldElkKSkKCSAgICAgICAgICAgIC5lbnRyeVNldCgpLnN0cmVhbSgpCgkgICAgICAgICAgICAubWFwKGUgLT4gbmV3IEZvbygKCSAgICAgICAgICAgICAgICAgICAgZS5nZXRLZXkoKSwKCSAgICAgICAgICAgICAgICAgICAgZS5nZXRWYWx1ZSgpLmdldCgwKS5uYW1lLAoJICAgICAgICAgICAgICAgICAgICBlLmdldFZhbHVlKCkuc3RyZWFtKCkubWFwKEZvbzo6Z2V0Q2F0ZWdvcnkpLmNvbGxlY3QoQ29sbGVjdG9ycy50b0xpc3QoKSkpCgkgICAgICAgICAgICApCgkgICAgICAgICAgICAuY29sbGVjdChDb2xsZWN0b3JzLnRvTGlzdCgpKTsKCX0KCQoJc3RhdGljIExpc3Q8Rm9vPiBqb2luZWRDYXRlZ29yaWVzUmVkdWNlKExpc3Q8Rm9vPiBpbnB1dCkgewogICAgICAgIHJldHVybiBpbnB1dAogICAgICAgICAgICAgICAgLnN0cmVhbSgpCiAgICAgICAgICAgICAgICAuY29sbGVjdChDb2xsZWN0b3JzLmdyb3VwaW5nQnkoRm9vOjpnZXRJZCkpICAvLyBNYXA8SW50ZWdlciwgTGlzdDxGb28+PgogICAgICAgICAgICAgICAgLmVudHJ5U2V0KCkuc3RyZWFtKCkKICAgICAgICAgICAgICAgIC5tYXAoZSAtPiBlLmdldFZhbHVlKCkuc3RyZWFtKCkgLy8gU3RyZWFtPEZvbz4KICAgICAgICAgICAgICAgICAgICAgICAgLnJlZHVjZShuZXcgRm9vKGUuZ2V0S2V5KCksIGUuZ2V0VmFsdWUoKS5nZXQoMCkuZ2V0TmFtZSgpLCAoU3RyaW5nKW51bGwpLCBGb286Om1lcmdlKQogICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgLmNvbGxlY3QoQ29sbGVjdG9ycy50b0xpc3QoKSk7CiAgICB9CiAgICAKCXB1YmxpYyBzdGF0aWMgdm9pZCBtYWluIChTdHJpbmdbXSBhcmdzKSB0aHJvd3MgamF2YS5sYW5nLkV4Y2VwdGlvbiB7CgkJZmluYWwgRm9vIGYxID0gbmV3IEZvbygxTCwgImEiLCAiYzEiKTsKCQlmaW5hbCBGb28gZjIgPSBuZXcgRm9vKDFMLCAiYSIsICJjMiIpOwoJCWZpbmFsIEZvbyBmMyA9IG5ldyBGb28oMkwsICJhIiwgImMxIik7CgkJCgkJZmluYWwgTGlzdDxGb28+IGxpID0gTGlzdC5vZihmMSwgZjIsIGYzKTsKCQkKCQlTeXN0ZW0ub3V0LnByaW50bG4oIk1hcDogIik7CgkJam9pbmVkQ2F0ZWdvcmllc01hcChsaSkuZm9yRWFjaChTeXN0ZW0ub3V0OjpwcmludGxuKTsKCQkKCQlTeXN0ZW0ub3V0LnByaW50bG4oIlJlZHVjZTogIik7CiAgICAgICAgam9pbmVkQ2F0ZWdvcmllc1JlZHVjZShsaSkuZm9yRWFjaChTeXN0ZW0ub3V0OjpwcmludGxuKTsKCX0KCQogICAgc3RhdGljIGNsYXNzIEZvbyB7CiAgICAgICAgcHJpdmF0ZSBMb25nIGlkOwogICAgICAgIHByaXZhdGUgU3RyaW5nIG5hbWU7CiAgICAgICAgcHJpdmF0ZSBTdHJpbmcgY2F0ZWdvcnk7CiAgICAgICAgcHJpdmF0ZSBMaXN0PFN0cmluZz4gY2F0ZWdvcmllczsKCiAgICAgICAgRm9vKExvbmcgaWQsIFN0cmluZyBuYW1lLCBTdHJpbmcgY2F0KSB7CiAgICAgICAgICAgIHRoaXMuaWQgPSBpZDsKICAgICAgICAgICAgdGhpcy5uYW1lID0gbmFtZTsKICAgICAgICAgICAgdGhpcy5jYXRlZ29yeSA9IGNhdDsKICAgICAgICB9CgogICAgICAgIEZvbyhMb25nIGlkLCBTdHJpbmcgbmFtZSwgTGlzdDxTdHJpbmc+IGNhdHMpIHsKICAgICAgICAgICAgdGhpcy5pZCA9IGlkOwogICAgICAgICAgICB0aGlzLm5hbWUgPSBuYW1lOwogICAgICAgICAgICB0aGlzLmNhdGVnb3JpZXMgPSBjYXRzOwogICAgICAgIH0KCiAgICAgICAgc3RhdGljIEZvbyBtZXJnZShGb28gYWNjdW0sIEZvbyBvdGhlcikgewogICAgICAgICAgICBpZiAobnVsbCA9PSBhY2N1bS5jYXRlZ29yaWVzKSB7CiAgICAgICAgICAgICAgICBhY2N1bS5jYXRlZ29yaWVzID0gbmV3IEFycmF5TGlzdDw+KCk7CiAgICAgICAgICAgICAgICBpZiAobnVsbCAhPSBhY2N1bS5jYXRlZ29yeSkgewogICAgICAgICAgICAgICAgICAgIGFjY3VtLmNhdGVnb3JpZXMuYWRkKGFjY3VtLmNhdGVnb3J5KTsKICAgICAgICAgICAgICAgICAgICBhY2N1bS5jYXRlZ29yeSA9IG51bGw7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgYWNjdW0uY2F0ZWdvcmllcy5hZGQob3RoZXIuY2F0ZWdvcnkpOwoKICAgICAgICAgICAgcmV0dXJuIGFjY3VtOwogICAgICAgIH0KCiAgICAgICAgcHVibGljIExvbmcgZ2V0SWQoKSB7IHJldHVybiBpZDsgfQogICAgICAgIHB1YmxpYyB2b2lkIHNldElkKExvbmcgaWQpIHsgdGhpcy5pZCA9IGlkOyB9CgogICAgICAgIHB1YmxpYyBTdHJpbmcgZ2V0TmFtZSgpIHsgcmV0dXJuIG5hbWU7IH0KICAgICAgICBwdWJsaWMgdm9pZCBzZXROYW1lKFN0cmluZyBuYW1lKSB7IHRoaXMubmFtZSA9IG5hbWU7IH0KCiAgICAgICAgcHVibGljIFN0cmluZyBnZXRDYXRlZ29yeSgpIHsgcmV0dXJuIGNhdGVnb3J5OyB9CgogICAgICAgIEBPdmVycmlkZQogICAgICAgIHB1YmxpYyBTdHJpbmcgdG9TdHJpbmcoKSB7CiAgICAgICAgICAgIHJldHVybiBTdHJpbmcuZm9ybWF0KCJGb28geyBpZD0lZCwgbmFtZT0lcywgY2F0ZWdvcnk9JXMsIGNhdGVnb3JpZXM9JXMgfSIsIGlkLCBuYW1lLCBjYXRlZ29yeSwgY2F0ZWdvcmllcyk7CiAgICAgICAgfQogICAgfQp9
Map:
Foo { id=1, name=a, category=null, categories=[c1, c2] }
Foo { id=2, name=a, category=null, categories=[c1] }
Reduce:
Foo { id=1, name=a, category=null, categories=[c1, c2] }
Foo { id=2, name=a, category=null, categories=[c1] }