so that only unique lists remain
Judging by your example, the following lists ["abc", "cba"]
and ["cba", "abc"]
are considered to be duplicates (which obviously isn't aligned with how equality is defined by the List
interface).
I infer your definition of equality as: two lists are equal if every element from the first list appear the same number of times in the second list, regardless of the order.
We can eliminate duplicates in a linear time O(n) by making use of standard hash-based mechanisms like method Stream.distinct()
.
For that, we need to create a wrapper-class which would implement equals/hashCode
contract as described above.
The following class exposes a static
method of()
which internally generates a map of frequencies for every element in the given list (which would is needed for equality checks) and hash code of the map of frequencies (which would be reused instead of being recalculated) and return an instance of wrapper class.
class ListWrapper<T> {
private List<T> list;
private Map<T, Integer> frequencies;
private final int hash; // hash code of the Map of frequencies generated from the List would be computed only once and reused
private ListWrapper(List<T> list, Map<T, Integer> frequencies, int hash) { // should not be invoked from outside
this.list = list;
this.frequencies = frequencies;
this.hash = hash;
}
public static <T> ListWrapper<T> of(List<T> list) {
Map<T, Integer> freq = getFrequencies(list);
return new ListWrapper<>(list, freq, freq.hashCode());
}
public static <T> Map<T, Integer> getFrequencies(List<T> list) {
return list.stream()
.collect(Collectors.toMap(
Function.identity(),
item -> 1,
Integer::sum
));
}
public List<T> getList() {
return list;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o instanceof ListWrapper<?> other) {
return Objects.equals(this.frequencies, other.frequencies);
}
return false;
}
@Override
public int hashCode() {
return hash;
}
}
And that's how we can use it.
public static void main(String args[]) {
List<List<String>> originalList = List.of(
List.of("abc", "cba"),
List.of("cba", "abc"),
List.of("123", "321")
);
List<List<String>> resultList = originalList.stream()
.map(ListWrapper::of) // wrapping a list
.distinct() // eliminating duplicates
.map(ListWrapper::getList) // unwrapping a list
.toList(); // or .collect(Collectors.toList()) for Java 15 and earlier
resultList.forEach(System.out::println);
}
Output:
[abc, cba]
[123, 321]
A link to Online Demo