JDK8之Comparator
Comparator
在Java8之前的版本我们应该也使用过关于Comparator吧!Comparator就是一个比较器,比较两个元素的大小。我们在对集合进行排序的时候,就需要一个比较器来对集合中的元素进行比较,才能进行排序。
* @since 1.2*/
@FunctionalInterface
public interface Comparator<T> {
}
通过这段源代码就可以发现,Comparator比较器是从Java2就有啦。但是自从JDK8开始,该接口就变为一个函数式接口,并且在JKD8对Comparator进行了增强(增加了一些默认方法和静态方法),既然Comparator成为一个函数式接口,那么该接口中的唯一一个抽象方法是什么呢?
int compare(T o1, T o2);
该方法就是Comparator比较器的核心:比较。该方法接收两个参数,并返回一个整形。如果返回的数据大于0就证明o1比o2大,返回0就证明o1和o2相等,返回小于0就证明o1比o2小。
- 例子1:创建一个字符串集合,并对集合中的元素进行排序。
public class ComparatorTest {public static void main(String[] args) {List<String> list = Arrays.asList("hello","world","nihao","wohao","welcome");list.sort((item1,item2)->item2.length()-item1.length());System.out.println(list);}
}
通过这个示例代码可以知道,我们是对集合中的元素按元素的长度进行降序排列。我们通过Comparator进行改造:
public class ComparatorTest {public static void main(String[] args) {List<String> list = Arrays.asList("hello","world","nihao","wohao","welcome");list.sort(Comparator.comparingInt(String::length).reversed());System.out.println(list);}
}
改成该种方式的代码也能完成相同的功能。那么我们将上面的代码中的方法引用改成lambda表达式,看能出现什么情况呢?
public class ComparatorTest {public static void main(String[] args) {List<String> list = Arrays.asList("hello","world","nihao","wohao","welcome");list.sort(Comparator.comparingInt(item->item.length()).reversed());System.out.println(list);}
}
这行代码在编辑器进行编译的时候,会发现编译出错Cannot resolve method 'length()'
,也就是说item并没有length方法,那么为什么没有该方法呢?通过查看item类型发现,编辑器把item当成了Object类型的啦。那么为什么Java的类型推断会把item推断为Object类型呢?
public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor) {Objects.requireNonNull(keyExtractor);return (Comparator<T> & Serializable)(c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2));
}
通过我们查看Comparator接口中的comparingInt静态方法,发现该方法是一个泛型方法,T
就是需要比较的元素类型,然而我们可以发现ToIntFunction<? super T>
接收的参数类型也带有一个泛型,该泛型就是输入元素的类型,可以发现输入元素的类型定义了一个下界,证明输入元素类型的下界是T
类型。那么为什么类型推断不能通过上下文推断出ToIntFunction<? super T>
输入参数是String类型,而将输入元素向上转换为Object类型呢?其实是这样的,我们在调用comparingInt方法后,还调用了reversed
方法:
default Comparator<T> reversed() {return Collections.reverseOrder(this);
}
通过reversed
方法可以发现,该默认方法也是一个泛型方法,当中的泛型就是T
类型,也就是集合中的待比较元素的类型。就可以知道reversed
方法返回的Comparator<T>
类型相对上下文会比ToIntFunction<? super T>
相对较远,所以,编辑器不能判断出确定的类型。就会把T类型向上转型为Object最大的类型。 因为,编辑器不能推断出item的类型,我们在编程的时候,可以指定出item的类型:
public class ComparatorTest {public static void main(String[] args) {List<String> list = Arrays.asList("hello","world","nihao","wohao","welcome");list.sort(Comparator.comparingInt((String item)->item.length()).reversed());System.out.println(list);}
}
那么问题来了,为什么comparingInt
方法为什么会把待比较的元素类型设置为一个下界呢?其实JDK的设计思路是这样的,有些时候,可能我们比较的时候,不一定会按照当前类的特性进行比较,有可能,我们会按照待比较元素类型的父类或者接口中的一些特性进行比较。
default void sort(Comparator<? super E> c) {Object[] a = this.toArray();Arrays.sort(a, (Comparator) c);ListIterator<E> i = this.listIterator();for (Object e : a) {i.next();i.set((E) e);}
}
通过这段代码,我们可以发现,JDK的设计者是向开发者可以使用比较器的时候,可以使用比较元素的父类或者接口进行比较,比较完了过后,把比较后的元素给强制的转换为比较元素的类型。
- 例子2:首先对集合中的元素按照元素的长度进行升序排列,如果相同,就按照元素的ASCI码进行比较。
public class ComparatorTest {public static void main(String[] args) {List<String> list = Arrays.asList("hello","world","nihao","wohao","welcome");list.sort(Comparator.comparingInt(String::length).thenComparing(String.CASE_INSENSITIVE_ORDER));System.out.println(list);}
}
这里给出一个注意点:String类给我们直接提供了一个不区分大小写的比较实现CASE_INSENSITIVE_ORDER
public static final Comparator<String> CASE_INSENSITIVE_ORDER= new CaseInsensitiveComparator();private static class CaseInsensitiveComparatorimplements Comparator<String>, java.io.Serializable {// use serialVersionUID from JDK 1.2.2 for interoperabilityprivate static final long serialVersionUID = 8575799808933029326L;public int compare(String s1, String s2) {int n1 = s1.length();int n2 = s2.length();int min = Math.min(n1, n2);for (int i = 0; i < min; i++) {char c1 = s1.charAt(i);char c2 = s2.charAt(i);if (c1 != c2) {c1 = Character.toUpperCase(c1);c2 = Character.toUpperCase(c2);if (c1 != c2) {c1 = Character.toLowerCase(c1);c2 = Character.toLowerCase(c2);if (c1 != c2) {// No overflow because of numeric promotionreturn c1 - c2;}}}}return n1 - n2;}/** Replaces the de-serialized object. */private Object readResolve() { return CASE_INSENSITIVE_ORDER; }}
这段代码就是String提供的一个对String类型不区分大小写进行比较的一个私有的比较器。
在这个例子中,我们使用了thenComparing
方法,该方法,就是如果前面的比较结果不为0,就直接返回前面比较的结果,如果比较结果为0,就调用该方法传入的比较器进行二次比较。
default Comparator<T> thenComparing(Comparator<? super T> other) {Objects.requireNonNull(other);return (Comparator<T> & Serializable) (c1, c2) -> {int res = compare(c1, c2);return (res != 0) ? res : other.compare(c1, c2);};
}
在上面的例子中,我们使用String提供的一个不区分大小写的私有比较器CASE_INSENSITIVE_ORDER
,那么我们不使用String提供的,该怎么写呢?
public class ComparatorTest {public static void main(String[] args) {List<String> list = Arrays.asList("hello","world","nihao","wohao","welcome");list.sort(Comparator.comparingInt(String::length).thenComparing((item1,item2)->item1.toLowerCase().compareTo(item2.toLowerCase())));System.out.println(list);}
}
也可是这样:
public class ComparatorTest {public static void main(String[] args) {List<String> list = Arrays.asList("hello","world","nihao","wohao","welcome");list.sort(Comparator.comparingInt(String::length).thenComparing(Comparator.comparing(String::toLowerCase)));System.out.println(list);}
}
JDK8之Comparator相关推荐
- 【java8新特性】——方法引用(四)
一.简介 方法引用是java8的新特性之一, 可以直接引用已有Java类或对象的方法或构造器.方法引用与lambda表达式结合使用,可以进一步简化代码. 来看一段简单代码: public static ...
- JDK8:Lambda表达式操作List集合
JDK8的流对list的处理提供了很大的方便,特别是做报表的时候才能真正体现出来这个功能的强大:结合日常使用过程,有两个体会:一个是减少了数据库连接,最忌讳在循环中进行数据查询,特别是嵌套多层循环的时 ...
- JDK8 Stream操作 collectingAndThen:根据对象的属性去重
来源:blog.csdn.net/qq_35634181/article/details/108867857 ExportTemperatureDto实体对象: @Getter @Setter @To ...
- jdk8新特性 lambda表达式详解
本文主要讲到的内容有: 一- 前言 二- 背景 三- lambda表达式的语法 四- Lambda程序例子 4-1 Runnable Lambda 4-2 Comparator Lambda 4-3 ...
- java可比较的和比较器的区别_Java中Compareable和Comparator两种比较器的区别
对于JDK8而言,有三种实现对象比较的方法: 1.在需要比较的对象类中覆写Object类的equals()方法: 2.需要比较的类继承Comparable接口,然后在其类内部实现compareTo() ...
- Lambda使用——JDK8新特性
文章目录 Lambda 简介 Lambda 表达式 Lambda 常用测试 Optional 常用方法测试 Lambda 简介 Lambda表达式是JDK8的新特性,可以取代大部分的匿名内部类,写出更 ...
- Java15-day06【Set、HashSet、LinkedHashSet、TreeSet、Comparable、Comparator、泛型类、可变参数的使用】
视频+资料(工程源码.笔记)[链接:https://pan.baidu.com/s/1MdFNUADVSFf-lVw3SJRvtg 提取码:zjxs] Java基础--学习笔记(零起点打开java ...
- java8 大到小排序,屌炸天,JDK8的排序大法!!
今天总结了下JDK中排序的方法,包括JDK8中强大的lambda表达式及函数式接口运用,不废话,请看下面示例. public class Test { public static void main( ...
- sort()排序(Comparable、Comparator)
在收集对象之后,对对象进行排序是常用的动作.不用亲自操作排序算法Java.util. Collections提供有sort()方法.由于必须有索引才能进行排序,因此 Collections的sort( ...
最新文章
- Windows环境下IOCP和SELECT模型性能比较
- Oracle 12C 多种方式创建PDB
- C# Chart控件,chart、Series、ChartArea曲线图绘制的重要属性
- .Net Core3.1下使用Swagger搭建web api项目
- 编程体系结构(04):JavaIO流文件管理
- 多态 java 1614787331
- html设置设置字母间的距离,css如何设置字母间距?字母间距的设置方法
- L3-007. 天梯地图-PAT团体程序设计天梯赛GPLT
- C++11 —— 基于区间(range)的 for 循环
- hbase1.1.1 连接集群_HBase-1.2.1集群搭建
- python open 文件操作
- studio one 3 机架声道设置_雅马哈Yamaha AG03/AG06声卡直播机架跳线教程
- jed后缀是什么文件?什么作用呢?
- qt中实现多语言功能
- dcp1608 linux驱动下载,兄弟激光 DCP-1608驱动
- LaTeX技巧014:实现圆圈形状的脚注
- 为地图marker 设置网络图片
- Integer.MAX_VALUE是什么意思
- Python-修改图片分辨率
- 如何实现廣州南方学院校园网WiFi连接的高效性