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相关推荐

  1. 【java8新特性】——方法引用(四)

    一.简介 方法引用是java8的新特性之一, 可以直接引用已有Java类或对象的方法或构造器.方法引用与lambda表达式结合使用,可以进一步简化代码. 来看一段简单代码: public static ...

  2. JDK8:Lambda表达式操作List集合

    JDK8的流对list的处理提供了很大的方便,特别是做报表的时候才能真正体现出来这个功能的强大:结合日常使用过程,有两个体会:一个是减少了数据库连接,最忌讳在循环中进行数据查询,特别是嵌套多层循环的时 ...

  3. JDK8 Stream操作 collectingAndThen:根据对象的属性去重

    来源:blog.csdn.net/qq_35634181/article/details/108867857 ExportTemperatureDto实体对象: @Getter @Setter @To ...

  4. jdk8新特性 lambda表达式详解

    本文主要讲到的内容有: 一- 前言 二- 背景 三- lambda表达式的语法 四- Lambda程序例子 4-1 Runnable Lambda 4-2 Comparator Lambda 4-3 ...

  5. java可比较的和比较器的区别_Java中Compareable和Comparator两种比较器的区别

    对于JDK8而言,有三种实现对象比较的方法: 1.在需要比较的对象类中覆写Object类的equals()方法: 2.需要比较的类继承Comparable接口,然后在其类内部实现compareTo() ...

  6. Lambda使用——JDK8新特性

    文章目录 Lambda 简介 Lambda 表达式 Lambda 常用测试 Optional 常用方法测试 Lambda 简介 Lambda表达式是JDK8的新特性,可以取代大部分的匿名内部类,写出更 ...

  7. Java15-day06【Set、HashSet、LinkedHashSet、TreeSet、Comparable、Comparator、泛型类、可变参数的使用】

    视频+资料(工程源码.笔记)[链接:https://pan.baidu.com/s/1MdFNUADVSFf-lVw3SJRvtg   提取码:zjxs] Java基础--学习笔记(零起点打开java ...

  8. java8 大到小排序,屌炸天,JDK8的排序大法!!

    今天总结了下JDK中排序的方法,包括JDK8中强大的lambda表达式及函数式接口运用,不废话,请看下面示例. public class Test { public static void main( ...

  9. sort()排序(Comparable、Comparator)

    在收集对象之后,对对象进行排序是常用的动作.不用亲自操作排序算法Java.util. Collections提供有sort()方法.由于必须有索引才能进行排序,因此 Collections的sort( ...

最新文章

  1. Windows环境下IOCP和SELECT模型性能比较
  2. Oracle 12C 多种方式创建PDB
  3. C# Chart控件,chart、Series、ChartArea曲线图绘制的重要属性
  4. .Net Core3.1下使用Swagger搭建web api项目
  5. 编程体系结构(04):JavaIO流文件管理
  6. 多态 java 1614787331
  7. html设置设置字母间的距离,css如何设置字母间距?字母间距的设置方法
  8. L3-007. 天梯地图-PAT团体程序设计天梯赛GPLT
  9. C++11 —— 基于区间(range)的 for 循环
  10. hbase1.1.1 连接集群_HBase-1.2.1集群搭建
  11. python open 文件操作
  12. studio one 3 机架声道设置_雅马哈Yamaha AG03/AG06声卡直播机架跳线教程
  13. jed后缀是什么文件?什么作用呢?
  14. qt中实现多语言功能
  15. dcp1608 linux驱动下载,兄弟激光 DCP-1608驱动
  16. LaTeX技巧014:实现圆圈形状的脚注
  17. 为地图marker 设置网络图片
  18. Integer.MAX_VALUE是什么意思
  19. Python-修改图片分辨率
  20. 如何实现廣州南方学院校园网WiFi连接的高效性

热门文章

  1. 太变态!还有这样的 Hello World 鬼畜代码
  2. 使用安卓投屏软件在电脑上玩吃鸡、王者手游的方法教程!
  3. 8588亿年收入,华为生态贡献多大?
  4. 基于matlab编程实现SAR图像多视处理
  5. php同步网站内容到百家号,提升seo排名
  6. 非极大值抑制(nonMaximumSuppression)
  7. 玩儿《阴阳师》的体验
  8. AR ———正方形图片识别转换成一个标记
  9. 查看oracle的表的字段类型
  10. Android自定义控件之onMeasure详解