Java 8 Lambda:比较器

Posted

技术标签:

【中文标题】Java 8 Lambda:比较器【英文标题】:Java 8 Lambda: Comparator 【发布时间】:2017-10-28 18:47:57 【问题描述】:

我想用 Lambda 对列表进行排序:

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

但是我得到了这个编译错误:

 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) 
     -> )

【问题讨论】:

【参考方案1】:

Comparator#compareTo 返回一个int;而getTime 显然是long

这样写会更好:

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

【讨论】:

【参考方案2】:

拉姆达

lambda 可以看作是有些繁琐的匿名类的简写:

Java8 版本:

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

Java8 之前的版本:

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

所以,每当你对如何编写正确的 lambda 感到困惑时,你可以尝试编写一个 pre-lambda 版本,看看它是怎么错的。

应用

在你的具体问题中,你可以看到compare返回int,你的getTime返回long,这是错误的来源。

您可以将任一方法用作其他答案方法,例如:

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

通知

您应该避免在Comparator 中使用-,这在某些情况下可能会导致溢出并导致程序崩溃。

【讨论】:

【参考方案3】:

Comparatorcompare() 方法必须返回 int,而您的方法似乎返回 long

你可以改成:

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

这是假设(根据您的错误消息)o1.getTime() 返回long

【讨论】:

我认为他们可以直接使用像 o1.getTime().compareTo(o1.getTime()) 这样的长比较方法。因为 Long 包装类已经实现了 Comparable 接口。【参考方案4】:

比较器

我们使用比较器接口对同质和异质元素进行排序,以实现默认的自定义排序顺序。

int compare(T o1, T o2);

排序需要两个参数。返回一个

    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 如何使用内部类对prior versions of Java 8 中的对象列表进行排序。

匿名类无法访问其封闭范围内未声明为 final 或有效 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 使用比较方法

一个 lambda 表达式就像一个方法:它提供了一个形式参数的列表和一个体 - 一个表达式或块 - 用这些参数表示。

LambdaExpression:LambdaParameters -&gt; LambdaBody

任何使用但未在 lambda 表达式中声明的局部变量、形参或异常参数都必须声明为 final 或实际上是 final,否则在尝试使用时会发生编译时错误。

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

支持 Lambda 的基本排序

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

使用 Extracted Key 和 Comparing method通过提取的 key 进行比较的比较器。使用 :: 关键字传递引用。

static <T> Comparator<T> comparingLong(ToLongFunction<? super T> keyExtractor)
ToLongFunction<Employee> keyExtracor = Employee::getCreationTime;
Comparator<Employee> byTime = Comparator.comparingLong( Employee::getCreationTime );

示例测试代码:

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


还可以查看这些帖子:

Java 8 Tutorial Java8 Lambdas vs Anonymous classes Lambda vs anonymous inner class performance

【讨论】:

【参考方案5】:

你应该改变

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

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

假设值为Comparable,它提供了自然的排序顺序。

如果您想添加更多字段,则可以将它们链接到比较器。例如先按时间排序,然后按发件人:

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

要反转任何Comparator 的顺序,请将reveresed() 方法链接到它,例如先按时间降序排序,然后按发件人:

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

另见https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html

【讨论】:

【参考方案6】:

compare() 方法必须返回 int,而您的方法似乎返回 long

你可以把它改成:

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

在下面的视频link 中很好地解释了 lambda 比较器。

【讨论】:

【参考方案7】:

lamda 比较器

In the place of Developer write your class name 

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

【讨论】:

以上是关于Java 8 Lambda:比较器的主要内容,如果未能解决你的问题,请参考以下文章

Lambda表达式用法大比较: Scala和Java 8

Java 8 为什么会引入lambda 表达式?

聊聊Java 8的Lambda表达式和函数式接口

《Java8实战》 - 读书笔记 - Lambda 表达式的组合用法

java8 lambda表达式的一些用法

比较 Java 的 lambda 表达式和 Swift 的函数类型