How to sort by two fields in Java?
You can use Collections.sort
as follows:
private static void order(List<Person> persons) {
Collections.sort(persons, new Comparator() {
public int compare(Object o1, Object o2) {
String x1 = ((Person) o1).getName();
String x2 = ((Person) o2).getName();
int sComp = x1.compareTo(x2);
if (sComp != 0) {
return sComp;
}
Integer x1 = ((Person) o1).getAge();
Integer x2 = ((Person) o2).getAge();
return x1.compareTo(x2);
}});
}
List<Persons>
is now sorted by name, then by age.
String.compareTo
"Compares two strings lexicographically" - from the docs.
Collections.sort
is a static method in the native Collections library. It does the actual sorting, you just need to provide a Comparator which defines how two elements in your list should be compared: this is achieved by providing your own implementation of the compare
method.
Collections.sort with multiple fields
Do you see anything wrong with the code?
Yes. Why are you adding the three fields together before you compare them?
I would probably do something like this: (assuming the fields are in the order you wish to sort them in)
@Override public int compare(final Report record1, final Report record2) {
int c;
c = record1.getReportKey().compareTo(record2.getReportKey());
if (c == 0)
c = record1.getStudentNumber().compareTo(record2.getStudentNumber());
if (c == 0)
c = record1.getSchool().compareTo(record2.getSchool());
return c;
}
How to sort a list by multiple fields in different orders (asc/desc) in Java?
Comparator.reversed() for descending order
Comparator<Element> compar = Comparator.comparing(Element::getA)
.thenComparing(Comparator.comparing(Element::getB).reversed())
.thenComparing(Element::getC);
yourArrayList.sort(compar);
In addition to the reversed
method I am exploiting the fact that thenComparing()
is overloaded: one thenComparing()
takes a Comparator
as argument, which we need for reversing, the other just takes a method reference (or lambda) as argument (a Function
in the declaration of thenComparing()
).
If either a
, b
or c
is a primitive int
, long
or double
remember to use comparingInt()
, comparingLong()
, comparingDouble()
, thenComparingInt()
, etc.
Java stream sort by 2 fields
I have found best possible way to do it, since i'm using Kotlin it can be done like this:
result.sortedWith(compareBy({ it.intervalType.orderIndex }, { it.item.amount }))
Java 8 Sort using 2 fields
You can use group comparator and parallel stream as follows:
List<Document> outList = documentList.stream()
.filter(....)
.sorted(Comparator.comparing(Document::getPdate)
.thenComparing(Document::getSubject))
.parallel();
Sorting by two fields of object. Java
Three points:
- Do use java.time, the modern Java date and time API, for your date and time work. The
SimpleDateFormat
andDate
classes are poorly designed and long outdated, the former in particular notoriously troublesome. - Don’t keep your dates as strings in your objects. Keep proper date-time objects such as
LocalDate
orLocalDateTime
. - For sorting on multiple fields since Java 8 use
Comparator.comparing()
and.thenComparing()
for much simpler and terser and first and foremost less error-prone code.
In code:
List<DocSet> mainList = Arrays.asList(
new DocSet(LocalDate.of(2019, Month.JANUARY, 1), "CCCC"),
new DocSet(LocalDate.of(2019, Month.FEBRUARY, 1), "Aaaa"),
new DocSet(LocalDate.of(2019, Month.MARCH, 1), "CCC"),
new DocSet(LocalDate.of(2019, Month.FEBRUARY, 1), "BBBB"),
new DocSet(LocalDate.of(2019, Month.MARCH, 1), "Aaaa"),
new DocSet(LocalDate.of(2019, Month.JANUARY, 1), "Aaaa"));
Collections.sort(mainList,
Comparator.comparing(DocSet::getDate).thenComparing(DocSet::getCompanyName));
mainList.forEach(System.out::println);
The output from this snippet is:
2019-01-01 Aaaa
2019-01-01 CCCC
2019-02-01 Aaaa
2019-02-01 BBBB
2019-03-01 Aaaa
2019-03-01 CCC
You notice that the objects have been sorted first by date, next by company name.
If you want case-insensitive sorting of company names, use
Collections.sort(mainList,
Comparator.comparing(DocSet::getDate)
.thenComparing(DocSet::getCompanyName, String::compareToIgnoreCase));
What went wrong in your code?
Your code works as you say you want it to. The image that you linked to in a comment documents that your objects are sorted first by date and time (190101120010 before 190103120010), next by company name (AAAAAAAAAbdc1 before Abdc1).
Links
- Oracle tutorial: Date Time explaining how to use java.time.
- Your own image documenting that your sorting is correct, again
The DocSet class I have used
For the sake of a complete example here is the DocSet
class that I have used in the above code:
public class DocSet {
LocalDate date;
String companyName;
public DocSet(LocalDate date, String companyName) {
this.date = date;
this.companyName = companyName;
}
public LocalDate getDate() {
return date;
}
public String getCompanyName() {
return companyName;
}
@Override
public String toString() {
return "" + date + ' ' + companyName;
}
}
Is it possible to sort objects by two fields both in reversed natural order using a Comparator.comparingInt chain
just remove reversed()
from comparingInt
and only call reversed
upon thenComparingLong
:
Comparator<Test> comparator =
Comparator.comparingInt((Test t) -> t.height) // <--- removed reverse from this comparator
.thenComparingLong((Test t ) -> t.width).reversed();
Further, given that width
is an int
I'd use thenComparingInt
instead of thenComparingLong
.
In addition, in regard to your stream pipeline, I'd suggest using forEachOrdered
since you care about the order in which the elements are printed.
forEach
is documented as:
The behavior of this operation is explicitly nondeterministic. For
parallel stream pipelines, this operation does not guarantee to
respect the encounter order of the stream, as doing so would sacrifice
the benefit of parallelism.
Therefore:
list.stream()
.sorted(comparator)
.forEachOrdered(e -> System.out.println(e.height + "/" + e.width));
How to sort an arraylist by 2 fields?
List<YourClass> list = new ArrayList<>();
The following works like this.
Comparator.comparing(YourClass::getLength).reversed()
sorts on the length field in desending order.- if the above results in equal lengths, then
.thenComparing(YourClass::getPosition)
sorts on the position in lexical order.
list.sort(Comparator.comparing(YourClass::getLength).reversed()
.thenComparing(YourClass::getPosition));
// or with no getters
list.sort(Comparator.comparing((YourClass cl) -> cl.length).reversed()
.thenComparing((YourClass cl) -> cl.position));
A possible structure of your coordinate class.
class YourClass {
int length;
public String position;
int coordX;
int coordY;
public int getLength() {
return length;
}
public String getPosition() {
return position;
}
public int getCoordX() {
return coordX;
}
public int getCoordY() {
return coordY;
}
}
How to sort by two fields in Java and specify sort direction?
As simple as adding a reversed
...
list.sort(Comparator.comparing(ClassA::getVar1)
.thenComparing(Comparator.comparing(ClassA::getVar2).reversed()));
Related Topics
How to Read from a Inputstream With a Timeout
Swing Animation Running Extremely Slow
Replace a Character At a Specific Index in a String
Best Way to Format a Double Value to 2 Decimal Places
How to Sanity Check a Date in Java
Dates With No Time or Timezone Component in Java/MySQL
How to Get Rid of Accents and Convert a Whole String to Regular Letters
How to Determine Day of Week by Passing Specific Date
Difference Between a Static Method and a Non-Static Method
Changing the Current Working Directory in Java
How to Use Key Bindings Instead of Key Listeners
Array or List in Java. Which Is Faster