How to compare objects by multiple fields
You can implement a Comparator
which compares two Person
objects, and you can examine as many of the fields as you like. You can put in a variable in your comparator that tells it which field to compare to, although it would probably be simpler to just write multiple comparators.
Java 8 - Compare multiple fields in different order using Comparator
Use Comparator.reverseOrder()
:
.thenComparing(Person::getAge, Comparator.reverseOrder())
If you want to avoid autoboxing, you can do
.thenComparing((p1, p2) -> Integer.compare(p2.getAge(), p1.getAge()))
Or
.thenComparing(Comparator.comparingInt(Person::getAge).reversed())
Comparing objects based on the values of multiple fields using Comparator
1) return 1000000 + getWeight(u1.getUsername());
and return -1000000 + getWeight(u2.getUsername());
are not required. return 1
and return -1
is clearer and produces the same result if you refer to the CompareTo()
javadoc :
Compares this object with the specified object for order. Returns a
negative integer, zero, or a positive integer as this object is less
than, equal to, or greater than the specified object
2) You don't chain the field comparisons but you have 3 ways of sorting according to the state of the compared objects. So the fact that the code be a bit verbose to define each case is finally normal.
You could all the same reduce it with an extract method as you duplicate a lot of user.getLastName().isEmpty()
invocations.
For example :
public static Comparator<UserConfigurationDto> BY_LASTNAME = (u1, u2) -> {
// first case
if( u1.isLastAndFirstNameEmpty() && u2.isLastAndFirstNameEmpty()){
return u1.getUsername().compareToIgnoreCase(u2.getUsername());
}
// second case
if(u1.isLastAndFirstNameEmpty()){
return 1;
}
else if(u2.isLastAndFirstNameEmpty()){
return -1;
}
// third case
String s1 = u1.getLastName().isEmpty() ? u1.getFirstName() : u1.getLastName();
String s2 = u2.getLastName().isEmpty() ? u2.getFirstName() : u2.getLastName();
return s1.compareToIgnoreCase(s2);
};
Compare by multiple methods in java compareTo?
You can create a Comparator for your Student class this way:
Comparator<Student> comparator = Comparator
.comparing(Student::getLastName)
.thenComparing(Student::getFirstName)
.thenComparing(Student::getSection);
And then use this comparator (instead of implementing Comparable interface) to sort a list with Student objects, or to create a TreeMap with these objects:
Collections.sort(listOfStudents, comparator);
TreeMap<Student> mapOfStudents = new TreeMap<>(comparator);
Java Comparator for Objects with multiple fields
Your method might be correct, but it is inefficient (unnecessarily calls equals) and difficult to read. It could be rewritten something like this:
public int compare(Collection c1, Collection c2)
{
int n;
n = c1.id.compareTo(c2.id);
if (n != 0) return n;
n = c1.entityType.compareTo(c2.entityType);
if (n != 0) return n;
n = c1.brandId.compareTo(c2.brandId);
if (n != 0) return n;
return c1.productId.compareTo(c2.productId);
}
Even better is to use a library method which abstracts all this logic away so you don't have to think about it. E.g. using apache.commons.lang CompareToBuilder
public int compare(Collection c1, Collection c2)
{
return new CompareToBuilder()
.append(c1.id, c2.id)
.append(c1.entityType, c2.entityType)
.append(c1.brandId, c2.brandId)
.append(c1.productId, c2.productId)
.toComparison();
}
How to use Comparator with a multiple field object?
It seems your question is in regards to simplifying your comparison, but I think you'd rather implement Comparable<Passenger>
rather than Comparator
, and use the #compareTo
method. As for cleanup, that's a bit easy too if you simply abstract the actual boolean logic:
public int compareTo(Passenger other) {
if (this.isDisability() ^ other.isDisability()) { //use an XOR
return this.isDisability() ? 1 : -1; //1 for us, -1 for other
}
//compare #getClase
int clase = -Integer.compare(this.getClase(), other.getClase()); //invert
if (clase == 0) {
//compare arrival times if clase is equal
//normalize to -1, 1 (0 excluded in OP)
return this.getArrivalTime() < other.getArrivalTime() ? 1 : -1;
}
return clase > 0 ? 1 : -1; //normalize to -1, 0, 1
}
This allows you to define a natural ordering to a Passenger
, and is encapsulated/internal to your class implementation (doesn't require as much exposure).
This also makes operations like sorting much easier:
List<Passenger> passengers = /* some list */;
Collections.sort(passengers);
If you want to provide a Comparator which can accomplish alternative sorts, you can also do that inside your class:
public class Passenger {
//...
public static class ArrivalComparator implements Comparator<Passenger> {
public int compare(Passenger one, Passenger two) {
return Integer.compare(one.getArrivalTime(), two.getArrivalTime());
}
}
//...
}
Using our previous example, this would let you sort all the passengers based on arrival time:
Collections.sort(passengers, new Passenger.ArrivalComparator());
Additionally, this can just be inlined using Java 8:
//Sort by arrival time
Collections.sort(passengers, (one, two) -> Integer.compare(one.getArrivalTime(), two.getArrivalTime());
But overall, keep in mind a Comparator is mostly for defining a specific sort, while Comparable
defines a general/natural ordering.
How does Java decide to sort a list of objects with multiple fields, when the chosen field to compare the object is equal?
Quoting from the Java API for Stream.sorted()
:
For ordered streams, the sort is stable. For unordered streams, no stability guarantees are made.
A stream over a List
is ordered, which means a stable sort algorithm is used. A stable sort guarantees that equal elements will not be swapped. Elements that compare equal are left in the same relative order as in the starting list.
All of the sorting methods in the standard library have similar guarantees:
Collections.sort()
Arrays.sort()
Arrays.parallelSort()
This sort is guaranteed to be stable: equal elements will not be reordered as a result of the sort.
...
The documentation for the methods contained in this class includes briefs description of the implementations. Such descriptions should be regarded as implementation notes, rather than parts of the specification. Implementors should feel free to substitute other algorithms, so long as the specification itself is adhered to. (For example, the algorithm used by
sort(Object[])
does not have to be a MergeSort, but it does have to be stable.)List.sort()
Implementation Note:
This implementation is a stable, adaptive, iterative mergesort...
Best way to compare multiple fields of objects in java?
Here is a method to do it with Reflection:
public class Test {
public static void main(String[] args) {
Person person1 = new Person();
person1.name = "Jack";
person1.age = 18;
person1.favoriteSuperhero = "Superman";
Person person2 = new Person();
person2.name = "Jack";
person2.age = 20;
person2.favoriteSuperhero = "Superman";
Map<String, List<Object>> uncommonTraits = getUncommonTraits(person1, person2);
for(Entry<String, List<Object>> entry : uncommonTraits.entrySet()){
System.out.println(entry.getKey() + ":\t" + entry.getValue());
}
}
private static Map<String, List<Object>> getUncommonTraits(Person p1, Person p2) {
Map<String, List<Object>> result = new HashMap<>();
for(Field field : Person.class.getDeclaredFields()){
try {
if(!(field.get(p1).equals(field.get(p2)))){
result.put(field.getName(), new ArrayList<Object>(Arrays.asList(field.get(p1), field.get(p2))));
}
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
return result;
}
}
class Person {
public String name;
public int age;
public String favoriteSuperhero;
}
Output:
age: [18, 20]
This is very resistant to changes: if you add a field to your Person
class, you won't have to change anything. It is also a bazillion times less redundant than manually checking each field.
I have used public
fields for ease of use.
Comparing multiple fields of 2 list of different objects
You can use this:
boolean isMatchs = list1.stream()
.allMatch(el1 -> list2.stream().anyMatch(el2 -> equals(el1, el2)));
with static method equals like this:
static boolean equals(Object1 a, Object2 b) {
return Objects.equals(a.getField1(), b.getField1());// && ...
}
Related Topics
How to Return Multiple Objects from a Java Method
Initialization of an Arraylist in One Line
Does a Lambda Expression Create an Object on the Heap Every Time It's Executed
Java Serializable Object to Byte Array
How to Establish a Connection Pool in Jdbc
How to Set the Proxy to Be Used by the Jvm
MySQL Jdbc Driver 5.1.33 - Time Zone Issue
How to Use a Servlet Filter in Java to Change an Incoming Servlet Request Url
Close a Scanner Linked to System.In
What Are the Effects of Exceptions on Performance in Java
Questions About Java'S String Pool
Difference Between Inheritance and Composition
How to Calculate Someone'S Age in Java
How to Sort by Two Fields in Java