Java 8 Lambda: Comparator

JavaLambdaJava 8Comparator

Java Problem Overview


I want to sort a list with Lambda:

List<Message> messagesByDeviceType = new ArrayList<Message>();  	
messagesByDeviceType.sort((Message o1, Message o2)->o1.getTime()-o2.getTime());

But I got this compilation error:

 Multiple markers at this line
	- Type mismatch: cannot convert from long to int
	- The method sort(Comparator<? super Message>) in the type List<Message> is not applicable for the arguments ((Message o1, Message o2) 
	 -> {})

Java Solutions


Solution 1 - Java

Comparator#compareTo returns an int; while getTime is obviously long.

It would be nicer written like this:

.sort(Comparator.comparingLong(Message::getTime))

Solution 2 - Java

Lambda

The lambda can be seen as the shorthand of somewhat cumbersome anonymous class:

Java8 version:

Collections.sort(list, (o1, o2) -> o1.getTime() - o2.getTime());

Pre-Java8 version:

    Collections.sort(list, new Comparator<Message>() {
        @Override
        public int compare(Message o1, Message o2) {
            return o1.getTime() - o2.getTime();
        }
    }); 

So, every time you are confused how to write a right lambda, you may try to write a pre-lambda version, and see how it is wrong.

Application

In your specific problem, you can see the compare returns int, where your getTime returns long, which is the source of error.

You may use either method as other answer method, like:

Long.compare(o1.getTime(),o2.getTime())
Notice
  • You should avoid using - in Comparator, which may causes overflow, in some cases, and crash your program.

Solution 3 - Java

The Comparator's compare() method must return an int, and it seems yours is returning a long.

You can change it to:

(Message o1, Message o2)->Long.compare(o1.getTime(),o2.getTime())

This is assuming (based on your error message) that o1.getTime() returns a long.

Solution 4 - Java

Comparator

We use comparator interface to sort homogeneous and heterogeneous elements for default, customized sorting order.

> int compare(T o1, T o2); > it takes two arguments for ordering. Returns a > negative integer(-1) « if first argument is less than the other zero (0) « if both are equal positive integer (1) « if first greater than the second. >

Anonymous Classes how to sort a list of objects in prior versions of Java 8 using inner Classes.

An anonymous class cannot access local variables in its enclosing scope that are not declared as final or effectively final.

Comparator<Employee> timeCompare = new Comparator<Employee>() {
	@Override public int compare(Employee e1, Employee e2) {
		return e1.getCreationTime().compareTo( e2.getCreationTime() );
	}
};

Java 8 Lambda Expressions uing compare method

> A lambda expression is like a method: it provides a list of formal parameters and a body - an expression or block - expressed in terms of those parameters. > > LambdaExpression: LambdaParameters -> LambdaBody > > Any local variable, formal parameter, or exception parameter used but not declared in a lambda expression must either be declared final or be effectively final, or a compile-time error occurs where the use is attempted.

Comparator<Employee> functional_semantics = (e1, e2) -> {
   return e1.getCreationTime().compareTo( e2.getCreationTime() );
};

Basic Sort with Lambda Support

Comparator<Employee> timeCompareLambda = (o1, o2) -> (int) ( o1.getCreationTime() - o2.getCreationTime());
Collections.sort(java8, timeCompareLambda );

Using Extracted Key and Comparing method: A comparator that compares by an extracted key. Pass references using :: keyword. > static Comparator comparingLong(ToLongFunction keyExtractor)

ToLongFunction<Employee> keyExtracor = Employee::getCreationTime;
Comparator<Employee> byTime = Comparator.comparingLong( Employee::getCreationTime );

Sample Test Code:

public class Lambda_Long_Comparator {
	public static void main(String[] args) {
		
		List<Employee> java7 = getEmployees();
		
		// Sort with Inner Class
		Comparator<Employee> timeCompare = new Comparator<Employee>() {
			@Override public int compare(Employee e1, Employee e2) {
				return e1.getCreationTime().compareTo( e2.getCreationTime() );
			}
		};
		
		// Collections.sort(list); // Defaults to Comparable<T> « @compareTo(o1)
		Collections.sort(java7, timeCompare); // Comparator<T> « @compare (o1,o2)
		System.out.println("Java < 8 \n"+ java7);
		
		List<Employee> java8 = getEmployees();
		Collections.sort(java8, Comparator
				.comparing( Employee::getCreationTime )
				.thenComparing( Employee::getName ));
		//java8.forEach((emp)-> System.out.println(emp));
		System.out.println("Java 8 \n"+java8);
	}
	
	static List<Employee> getEmployees() {
		Date date = Calendar.getInstance().getTime();
		List<Employee> list = new ArrayList<Employee>();
		list.add( new Employee(4, "Yash", date.getTime()+7));
		list.add( new Employee(2, "Raju", date.getTime()+1));
		list.add( new Employee(4, "Yas", date.getTime()));
		list.add( new Employee(7, "Sam", date.getTime()-4));
		list.add( new Employee(8, "John", date.getTime()));
		return list;
	}
}
class Employee implements Comparable<Employee> {
	Integer id;
	String name;
	Long creationTime;
	
	public Employee(Integer id, String name, Long creationTime) {
		this.id = id;
		this.name = name;
		this.creationTime = creationTime;
	}
	
	@Override public int compareTo(Employee e) {
		return this.id.compareTo(e.id);
	}
	
	@Override public String toString() {
		return "\n["+this.id+","+this.name+","+this.creationTime+"]";
	}
	
	// Other getter and setter methods
}

See these posts also:

Solution 5 - Java

You should change

>
messagesByDeviceType.sort( (Message o1, Message o2) -> o1.getTime() - o2.getTime() );

to

messagesByDeviceType.sort(
    Comparator.comparing((Message m) -> m.getTime())
);

That assumes that the value is Comparable, which provides a natural sort order.

If you want to add more fields then you can can chain them to the Comparator. e.g. to sort first by time, and then by sender:

messagesByDeviceType.sort(
    Comparator
        .comparing((Message m) -> m.getTime())
        .thenComparing((m)     -> m.getSender())
);

To reverse the order of any Comparator, chain the reveresed() method to it, e.g. to sort first by time descending, and then by sender:

messagesByDeviceType.sort(
    Comparator
        .comparing((Message m) -> m.getTime())
        .reversed()
        .thenComparing((m)     -> m.getSender())
);

See also https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html

Solution 6 - Java

The compare() method must return an int, and it seems yours is returning a long.

You can change it this into:

Long.compare(o1.getTime(),o2.getTime())

Well explained regarding lambda comparator in the below presented video link.

Solution 7 - Java

lamda comparator

In the place of Developer write your class name 

    Comparator<Developer> byName = 
		(Developer o1, Developer o2)->o1.getName().compareTo(o2.getName());

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionNunyet de Can Cal&#231;adaView Question on Stackoverflow
Solution 1 - JavaEugeneView Answer on Stackoverflow
Solution 2 - JavaTonyView Answer on Stackoverflow
Solution 3 - JavaEranView Answer on Stackoverflow
Solution 4 - JavaYashView Answer on Stackoverflow
Solution 5 - JavaisapirView Answer on Stackoverflow
Solution 6 - JavaRajView Answer on Stackoverflow
Solution 7 - Javabhargava krishnaView Answer on Stackoverflow