I have a simple class which stores an integer and a list of Strings.
As I want to use this class in a TreeSet<>
, the one must be Comparable
.
But when trying to use the Java 8 Comparator
class, I cannot compare my inner list.
I have the following error:
Bad return type in method reference: cannot convert java.util.List to U
I think there is a very simple way to do that but I could not find it out.
How to do that?
public class MyClass implements Comparable<MyClass> {
private final int someInt;
private final List<String> someStrings;
public MyClass (List<String> someStrings, int someInt) {
this.someInt = someInt;
this.someStrings = new ArrayList<>(someStrings);
}
@Override
public int compareTo(MyClass other) {
return
Comparator.comparing(MyClass::getSomeInt)
.thenComparing(MyClass::getSomeStrings) // Error here
.compare(this, other);
}
public int getSomeInt() {
return someInt;
}
public List<String> getSomeStrings() {
return someStrings;
}
}
I just want the String list to be compared in the simplest way (using implicitly String.compareTo()
).
Note that I do now want to sort my List<String>
but I want it to be Comparable
so that MyClass
is also comparable and finally, I can insert MyClass
instances into a TreeSet<MyClass>
.
A also saw in the JavaDoc the following:
java.util.Comparator<T> public Comparator<T>
thenComparing(@NotNull Comparator<? super T> other)
For example, to sort a collection of String based on the length and then case-insensitive natural ordering, the comparator can be composed using following code,
Comparator<String> cmp = Comparator.comparingInt(String::length)
.thenComparing(String.CASE_INSENSITIVE_ORDER);
It seems to be an clue but I don't know how to apply it to this simple example.
Let's say I want my List<String>
to be sorted the following way:
List.size()
(the shorter is less than the larger one);String.compareTo
method returns 1 or -1.How to do that with lambdas in a my compareTo
method?
This does not duplicates this question because I want to know how to build a comparator of a class which contains a List<String>
with Java 8 chaining Comparable
calls.
So to compare the list, first you check the length, then you compare each item with same indexes in both list one by one right?
(That is [a, b, c] < [b, a, c]
)
Make a custom comparator for list return join of your list string:
Comparator<List<String>> listComparator = (l1, l2) -> {
if (l1.size() != l2.size()) {
return l1.size() - l2.size();
}
for (int i = 0; i < l1.size(); i++) {
int strCmp = l1.get(i).compareTo(l2.get(i));
if (strCmp != 0) {
return strCmp;
}
}
return 0; // Two list equals
};
Then you can compare using that custom comparator:
@Override
public int compareTo(MyClass other) {
return Comparator.comparing(MyClass::getSomeInt)
.thenComparing(Comparator.comparing(MyClass:: getSomeStrings , listComparator))
.compare(this, other);
}
If you want [a, b, c] = [b, a, c]
, then you have to sort those list first before comparing:
public String getSomeStringsJoined() {
return getSomeStrings().stream().sort(Comparator.naturalOrder()).collect(Collectors.joining());
}