Your scenario sounds like a good use case for the groupingBy
collector. Normally, instead of supplying an equality function, you supply a function that extracts a qualifier. The elements are then mapped to these qualifiers in lists.
i.e.
Map<Qualifier, List<T>> map = list.stream()
.collect(Collectors.groupingBy(T::getQualifier));
Collection<List<T>> result = map.values();
In the case the identity of T
is your qualifier, you could use Function.identity()
as an argument.
But this becomes a problem when your qualifier is more than 1 field of T
. You could use a tuple type, to create an alternate identity for T
but this only goes so far, as there needs to be a separate tuple class for each number of fields.
If you want to use groupingBy
you really need to create a temperate alternate identity for T
, so you don't have to change T
's equals
and hashCode
methods.
To create a proper identity, you need to implement equals
and hashCode
(or always return 0
for a hash code, with performance downsides). There is no API class for this, that I know of, but I have made a simple implementation:
interface AlternateIdentity<T> {
public static <T> Function<T, AlternateIdentity<T>> mapper(
BiPredicate<? super T, Object> equality, ToIntFunction<? super T> hasher) {
return t -> new AlternateIdentity<T>() {
@Override
public boolean equals(Object other) {
return equality.test(t, other);
}
@Override
public int hashCode() {
return hasher.applyAsInt(t);
}
};
}
}
Which you could use like:
Collection<List<T>> result
= list.stream()
.collect(Collectors.groupingBy(
AlternateIdentity.mapper(eqF, hashF)
))
.values();
Where eqF
is your function, and hashF
is a hash code function that hashes the same fields as eqF
tests. (Again, you could also just return 0
in hashF
, but having a proper implementation would speed things up.)