Reputation: 1334
Collections sort is not giving me the result I was expecting, or am I misreading the method?
List of Row objects is to be sorted:
public class Row {
private int id;
private boolean line;
public Row(int id, boolean line) {
this.id = id;
this.line = line;
}
public boolean isLine() {
return line;
}
@Override public String toString() {
return "Row{" + "id=" + id + ", line=" + line + '}';
}
}
Starting data:
[Row{id=0, line=true}, Row{id=1, line=false}, Row{id=2, line=true}, Row{id=3, line=false}]
Sorting code:
Collections.sort(rows, new Comparator<Row>(){
@Override public int compare(Row o1, Row o2) {
if (!o1.isLine() && !o2.isLine()) return 0;
if (o1.isLine()) {
return 1;
} else {
return -1;
}
}
});
Result:
[Row{id=1, line=false}, Row{id=3, line=false}, Row{id=0, line=true}, Row{id=2, line=true}]
I was under the impresion that all objects with line=true
should be at the start of the list, not the end.
If I slightly change Comporator implementation:
Collections.sort(rows, new Comparator<Row>(){
@Override public int compare(Row o1, Row o2) {
if (!o1.isLine() && !o2.isLine()) return 0;
if (o1.isLine()) {
return -1;
} else {
return 1;
}
}
});
Result:
[Row{id=2, line=true}, Row{id=0, line=true}, Row{id=1, line=false}, Row{id=3, line=false}]
All of objects with line=true
can be found at the start of the list now, but they have switched places (id=0
should be first).
Expected sort result:
[Row{id=0, line=true}, Row{id=2, line=true}, Row{id=1, line=false}, Row{id=3, line=false}]
Upvotes: 1
Views: 196
Reputation: 969
Your comparision is not symmetric and hence broken. Use inverted comparision implemented in Boolean class (please note minus in start of lambda body):
Collections.sort(rows, (o1, o2) -> -Boolean.valueOf(o1.isLine()).compareTo(o2.isLine()));
Upvotes: 0
Reputation: 131316
I was under the impresion that all objects with line=true should be at the start of the list, not the end.
Not really as this code :
if (o1.isLine()) {
return 1;
}
means that o1
is superior to o2
.
So object with isLine=true
will happen to the end as default order is ascending.
All of objects with line=true can be found at the start of the list now, but they have switched places (id=0 should be first).
You never use the id
in the comparator implementation.
It could never be considered.
To get :
[Row{id=0, line=true}, Row{id=2, line=true}, Row{id=1, line=false}, Row{id=3, line=false}]
You should add a getter in Row
to retrieve the id
.
Then you should first sort by line=true
and by id
ASC.
Collections.sort(rows, new Comparator<Row>(){
@Override public int compare(Row o1, Row o2) {
if (!o1.isLine() && !o2.isLine()) return 0;
if (o1.isLine() && o2.isLine()) {
return o1.getId() > o2.getId();
}
if (o1.isLine()) {
return -1;
} else {
return 1;
}
}
});
A more brief way to write that would be using Java 8 Comparator :
Comparator<Row> comparatorRow = Comparator.comparing(Row::isLine).reversed()
.thenComparing(Row::getId);
As rows are already sorted by id
and the sort is guaranteed to be stable: equal elements will not be reordered as a result of the sort, you could compare only on isLine
:
Comparator<Row> comparatorRow = Comparator.comparing(Row::isLine).reversed();
Upvotes: 3
Reputation: 1334
Summarising @Dukeling:
Collections.sort(rows, new Comparator<Row>(){
@Override public int compare(Row o1, Row o2) {
return -Boolean.compare(o1.isLine(), o2.isLine());
}
});
This gives expected result.
Input:
[Row{id=0, line=true}, Row{id=1, line=false}, Row{id=2, line=true}, Row{id=3, line=false}]
Result:
[Row{id=0, line=true}, Row{id=2, line=true}, Row{id=1, line=false}, Row{id=3, line=false}]
Upvotes: 1