How to Sort by Two Fields in Java

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:

  1. Do use java.time, the modern Java date and time API, for your date and time work. The SimpleDateFormat and Date classes are poorly designed and long outdated, the former in particular notoriously troublesome.
  2. Don’t keep your dates as strings in your objects. Keep proper date-time objects such as LocalDate or LocalDateTime.
  3. 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



Leave a reply



Submit