> 在日常工作中戳晌,我們會(huì)經(jīng)常比較數(shù)據(jù)的大小,今天我們來看一下java中的比較器的底層原理是什么痴柔。沦偎。
我們知道基本數(shù)據(jù)類型的大小比較是直接通過兩個(gè)數(shù)相減的得到兩個(gè)數(shù)大小信息的,那如果是引用信息該如何人比較呢咳蔚?
首先來看基本數(shù)據(jù)類型的包裝類---他們都實(shí)現(xiàn)了Comparebal接口
整型--以Integer為例豪嚎,Short,Long谈火, Byte原理一樣(都是Number的子類侈询,只不過能比較的范圍有所不同)
```java
public class Test {
? ? public static void main(String[] args) {? ? ? ?
? ? ? ? Integer a = 11;
? ? ? ? Integer b = 15;
? ? ? ? System.out.println(a.compareTo(b));
? ? }
}
```
compareTo方法的實(shí)現(xiàn)---調(diào)用了compare方法
> public int compareTo(Integer anotherInteger) {
>? ? ? ? return compare(this.value, anotherInteger.value);
>? ? }
value為int類型的,自動(dòng)拆箱糯耍;
往compare方法里面?zhèn)魅雰蓚€(gè)參數(shù)扔字,
> public static int compare(int x, int y) {
>? ? ? ? return (x < y) ? -1 : ((x == y) ? 0 : 1);
>? ? }
>? ?
***x<y返回-1,x=y返回0温技,否則返回1***
再來看浮點(diǎn)型的---以Double為例啦租,F(xiàn)loat原理一樣;
```java
package com.Date.Test;
public class Test {
? ? public static void main(String[] args) {
? ? ? ? Double c = 12.5;
? ? ? ? Double d = 15.5;
? ? ? ? System.out.println(c.compareTo(d));
? ? }
}
```
compareTo方法的源碼--
> public int compareTo(Double anotherDouble) {
>? ? ? ? return Double.compare(value, anotherDouble.value);
>? ? }
也是調(diào)用compare方法
```java
public static int compare(double d1, double d2) {
? ? ? ? if (d1 < d2)
? ? ? ? ? ? return -1;? ? ? ? ? // Neither val is NaN, thisVal is smaller
? ? ? ? if (d1 > d2)
? ? ? ? ? ? return 1;? ? ? ? ? ? // Neither val is NaN, thisVal is larger
? ? ? ? // Cannot use doubleToRawLongBits because of possibility of NaNs.
? ? ? ? long thisBits? ? = Double.doubleToLongBits(d1);
? ? ? ? long anotherBits = Double.doubleToLongBits(d2);
? ? ? ? return (thisBits == anotherBits ?? 0 : // Values are equal
? ? ? ? ? ? ? ? (thisBits < anotherBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN)
? ? ? ? ? ? ? ? 1));? ? ? ? ? ? ? ? ? ? ? ? ? // (0.0, -0.0) or (NaN, !NaN)
? ? }
```
浮點(diǎn)型數(shù)據(jù)多了下面的方法
***doubleToLongBits方法返回個(gè)long類型的數(shù)***
public static long doubleToLongBits(double value) {
? ? ? ? if (!isNaN(value)) {
? ? ? ? ? ? return doubleToRawLongBits(value);
? ? ? ? }
? ? ? ? return 0x7ff8000000000000L;
? ? }
public static boolean isNaN(double v) {
? ? ? ? return (v != v);
? ? }
? ? public static native long doubleToRawLongBits(double value);
doubleToRawLongBits()方法是一個(gè)本地方法(native修飾)該函數(shù)根據(jù)保留Not-a-Number(NaN)值的IEEE 754浮點(diǎn)“double format”位布局返回指定浮點(diǎn)值的表示形式荒揣。
doubleToLongBits是將浮點(diǎn)型數(shù)據(jù)在內(nèi)存中拿過來轉(zhuǎn)為long型數(shù)據(jù)然后進(jìn)行比較篷角,即如果
當(dāng)isNaN(value)為false的時(shí)候即這個(gè)數(shù)是一個(gè)正常的數(shù),返回value系任,否則返回0x7ff8000000000000L;恳蹲,所以我們?cè)谟胏ompare比較兩個(gè)不是正常數(shù)的數(shù)據(jù)的時(shí)候因?yàn)閐oubleToLongBits的返回值一樣虐块,所以compare返回值為0
小結(jié),如果Double兩個(gè)數(shù)為正常的數(shù)嘉蕾,他們相等時(shí)調(diào)用compareTo時(shí)返回0贺奠,如果兩個(gè)為非正常的數(shù)--即不是數(shù),返回的結(jié)果也是0错忱,(非正常的數(shù)通過轉(zhuǎn)換為long來進(jìn)行比較)
驗(yàn)證代碼如下--
```java
Double qq=Math.sqrt(-2);
? ? ? ? Double qw=Math.sqrt(-4);
? ? ? ? System.out.println(qq.compareTo(qw));//返回0
```
isNaN方法很有意思
來看一下吧---
```java
public class Test {
? ? public static void main(String[] args) {
? ? ? ? double sqrt = Math.sqrt(-1d);
? ? ? ? System.out.println(sqrt==sqrt);
? ? ? ? System.out.println("-----------------");
? ? ? ? Double sqt=Math.sqrt(-1d);
? ? ? ? boolean naN = sqt.isNaN();
? ? ? ? System.out.println("isNaN()-------"+naN);
? ? ? ? System.out.println(sqt==sqt);
? ? ? ? System.out.println("--------------------");
? ? ? ? Double? sq=Math.sqrt(-1d);
? ? ? ? System.out.println("equals方法-------"+sqt.equals(sq));
? ? ? ? System.out.println(sqt.compareTo(sq));
? ? }
}
```
我們會(huì)發(fā)現(xiàn)當(dāng)一個(gè)數(shù)或者經(jīng)過運(yùn)算后不是數(shù)的時(shí)候的時(shí)候儡率,
基本數(shù)據(jù)類型? ? V!=V? 結(jié)果為true
包裝類? ? ? ? ? V!=V? 結(jié)果為 false
所以當(dāng)Double類型為為NaN時(shí)以清,返回
再來看Boolean類儿普,它的父類是Object,
```java
package com.Date.Test;
public class Test {
? ? public static void main(String[] args) {? ? ?
? ? ? ? Boolean s = true;
? ? ? ? Boolean r = false;
? ? ? ? System.out.println(s.compareTo(r));
? ? }
}
```
public int compareTo(Boolean b) {
? ? ? ? return compare(this.value, b.value);
? ? }
public static int compare(boolean x, boolean y) {
? ? ? ? return (x == y) ? 0 : (x ? 1 : -1);
? ? }
比較方式跟整型一樣不過只有兩個(gè)值--true與false
***String的比較器-----***
```java
public class Test {
? ? public static void main(String[] args) {
? ? ? String str ="0aaaa";
? ? ? String str0 ="1AAAA";
? ? ? ? System.out.println(str.compareTo(str0));
? ? ? ? String q="aaaaa";
? ? ? ? String w="AAA";
? ? ? ? System.out.println(q.compareTo(w));
? ? }
}
```
![在這里插入圖片描述](https://img-blog.csdnimg.cn/img_convert/f60e536bca3e3d4866ccd52addc81756.png)
public int compareTo(String anotherString) {
? ? ? ? byte v1[] = value;
? ? ? ? byte v2[] = anotherString.value;
? ? ? ? byte coder = coder();
? ? ? ? if (coder == anotherString.coder()) {
? ? ? ? ? ? return coder == LATIN1 ? StringLatin1.compareTo(v1, v2)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? : StringUTF16.compareTo(v1, v2);
? ? ? ? }
? ? ? ? return coder == LATIN1 ? StringLatin1.compareToUTF16(v1, v2)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? : StringUTF16.compareToLatin1(v1, v2);
? ? }
先將字符串轉(zhuǎn)換為byte數(shù)組掷倔,然后判斷編碼格式眉孩,根據(jù)編碼格式在做比較
以UTF16編碼為例---在 StringUTF16類中
```java
public static int compareToUTF16(byte[] value, byte[] other) {
? ? ? ? int len1 = length(value);
? ? ? ? int len2 = StringUTF16.length(other);
? ? ? ? return compareToUTF16Values(value, other, len1, len2);
? ? }
```
獲取字符串長度,
然后比較入果長度不一樣勒葱,返回兩個(gè)字符串長度的差浪汪,長度一樣的話比較值---一個(gè)一個(gè)比較,當(dāng)比較到第一個(gè)不同的值時(shí)凛虽,返回這兩個(gè)元素的ASCII碼差值死遭;
```java
private static int compareToUTF16Values(byte[] value, byte[] other, int len1, int len2) {
? ? ? ? int lim = Math.min(len1, len2);
? ? ? ? for (int k = 0; k < lim; k++) {
? ? ? ? ? ? char c1 = getChar(value, k);
? ? ? ? ? ? char c2 = StringUTF16.getChar(other, k);
? ? ? ? ? ? if (c1 != c2) {
? ? ? ? ? ? ? ? return c1 - c2;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return len1 - len2;
? ? }
```
> 以上就是比較器的方法比較,同時(shí)也要回覆寫比較器? 記住實(shí)現(xiàn)Compareable接口凯旋,返回值? 0呀潭,-1,1然后具體比較要看具體需要;
自定義比較器---
***內(nèi)部比較器***
```java
/**
* @Auther: GavinLim
* @Date: 2021/7/10 - 07 - 10 - 10:29
* @Description: com.Date.Test
* @version: 1.0
*/
//內(nèi)部比較器
class Students implements Comparable<Students> {
? ? private int SchoolId;
? ? private String name;
? ? private int age;
? ? public Students(int schoolId, String name, int age) {
? ? ? ? SchoolId = schoolId;
? ? ? ? this.name = name;
? ? ? ? this.age = age;
? ? }
? ? public int getSchoolId() {
? ? ? ? return SchoolId;
? ? }
? ? public void setSchoolId(int schoolId) {
? ? ? ? SchoolId = schoolId;
? ? }
? ? public String getName() {
? ? ? ? return name;
? ? }
? ? public void setName(String name) {
? ? ? ? this.name = name;
? ? }
? ? public int getAge() {
? ? ? ? return age;
? ? }
? ? public void setAge(int age) {
? ? ? ? this.age = age;
? ? }
//內(nèi)部比較器
? ? @Override
? ? public int compareTo(Students o) {
? ? ? ? return this.getAge() - o.getAge();
? ? }
//? ? @Override
//? ? public int compareTo(Students o) {
//? ? ? ? return this.getName().compareTo(o.getName());
//? ? }
}
public class Test {
? ? public static void main(String[] args) {
Students stu= new Students(1001,"小明",10);
Students stu1= new Students(1002,"小花",11);
? ? ? ? System.out.println(stu.compareTo(stu1));//-1
? ? }
}
```
***外部比較器***
```java
import java.util.Comparator;
class Students? {
? ? private int SchoolId;
? ? private String name;
? ? private int age;
? ? public Students(int schoolId, String name, int age) {
? ? ? ? SchoolId = schoolId;
? ? ? ? this.name = name;
? ? ? ? this.age = age;
? ? }
? ? public int getSchoolId() {
? ? ? ? return SchoolId;
? ? }
? ? public void setSchoolId(int schoolId) {
? ? ? ? SchoolId = schoolId;
? ? }
? ? public String getName() {
? ? ? ? return name;
? ? }
? ? public void setName(String name) {
? ? ? ? this.name = name;
? ? }
? ? public int getAge() {
? ? ? ? return age;
? ? }
? ? public void setAge(int age) {
? ? ? ? this.age = age;
? ? }
}
class CompareAge implements Comparator<Students> {
? ? @Override
? ? public int compare(Students o1, Students o2) {
? ? ? ? return o1.getAge()-o2.getAge();
? ? }
}
public class Test {
? ? public static void main(String[] args) {
Students stu= new Students(1001,"小明",18);
Students stu1= new Students(1002,"小花",11);
CompareAge compareAge= new CompareAge();
? ? ? ? System.out.println(compareAge.compare(stu,stu1));
? ? }
}
```
```java
@FunctionalInterface public
interface Comparator<T>? ? //功能性接口
```
```java
public interface Comparable<T> //普通接口
```
> 外部比較器實(shí)現(xiàn)了 Comparator接口瓦阐,內(nèi)部比較實(shí)現(xiàn)了Compareable接口
外部比較器與內(nèi)部比較器的比較--
外部比較器容易擴(kuò)展(繼承蜗侈、實(shí)現(xiàn)等),而且容易維護(hù)睡蟋,
而內(nèi)部比較器因?yàn)樵陬愔械膶?shí)現(xiàn)了方法踏幻,如果要繼承和實(shí)現(xiàn)以完成其他功能就得在類中添加其他方法;
接下來是TreeSet集合中的比較器---
```java
public class Test {
? ? public static void main(String[] args) {
? ? ? TreeSet<String>treeSet= new TreeSet<>();
? ? ? treeSet.add("ayayou");
? ? ? treeSet.add("eyayou");
? ? ? treeSet.add("hyayou");
? ? ? treeSet.add("cyayou");
? ? ? treeSet.add("kyayou");
? ? ? treeSet.add("eyayou");
? ? ? ? System.out.println(treeSet.size());
? ? ? ? ? ? ? ? for (String s : treeSet) {
? ? ? ? ? ? System.out.println(s);
? ? ? ? }
? ? }
}
```
![在這里插入圖片描述](https://img-blog.csdnimg.cn/img_convert/0158da09bae625dc1caf231a61706334.png)
TreeSet添加數(shù)據(jù)是按照升序排列的,
TreeSet構(gòu)造中添加了new TreeMap<>(),TreeMap的構(gòu)造中添加了一個(gè)比較器,是一個(gè)內(nèi)部比較器
>? public TreeSet() {
>? ? ? ? this(new TreeMap<>());
>? ? }
>
>? ? ? public TreeMap() {
>? ? ? ? comparator = null;
>? ? }
自定義類的比較器再TreeSet中實(shí)現(xiàn)排序--
```java
package setTest;
import java.util.*;
/**
* @author : Gavin
* @date: 2021/7/19 - 07 - 19 - 18:45
* @Description: setTest
* @version: 1.0
*/
import java.util.Comparator;
class Students? {
? ? private int SchoolId;
? ? private String name;
? ? private int age;
? ? public Students(int schoolId, String name, int age) {
? ? ? ? SchoolId = schoolId;
? ? ? ? this.name = name;
? ? ? ? this.age = age;
? ? }
? ? public int getSchoolId() {
? ? ? ? return SchoolId;
? ? }
? ? public void setSchoolId(int schoolId) {
? ? ? ? SchoolId = schoolId;
? ? }
? ? public String getName() {
? ? ? ? return name;
? ? }
? ? public void setName(String name) {
? ? ? ? this.name = name;
? ? }
? ? public int getAge() {
? ? ? ? return age;
? ? }
? ? public void setAge(int age) {
? ? ? ? this.age = age;
? ? }
? ? @Override
? ? public String toString() {
? ? ? ? return "Students{" +
? ? ? ? ? ? ? ? "SchoolId=" + SchoolId +
? ? ? ? ? ? ? ? ", name='" + name + '\'' +
? ? ? ? ? ? ? ? ", age=" + age +
? ? ? ? ? ? ? ? '}';
? ? }
}
class CompareAge implements Comparator<Students> {
? ? @Override
? ? public int compare(Students o1, Students o2) {
? ? ? ? return o1.getAge()-o2.getAge();
? ? }
}
public class Test {
? ? public static void main(String[] args) {
? ? ? ? Students stu= new Students(1001,"小明",18);
? ? ? ? Students stu1= new Students(1002,"小花",11);
? ? ? ? Students stu2= new Students(1003,"小花1",12);
? ? ? ? Students stu3= new Students(1004,"小花2",13);
? ? ? ? Students stu4= new Students(1005,"小花3",11);
? ? ? ? Students stu5= new Students(1006,"小花4",10);
? ? ? ? Students stu6= new Students(1007,"小花4",12);
? ? ? ? Students stu7= new Students(1008,"小花5",19);
? ? ? ? Students stu8= new Students(1009,"小花6",17);
? ? ? ? CompareAge compareAge= new CompareAge();
? ? ? TreeSet<Students>treeSet= new TreeSet<>(compareAge);//指定自定義比較器
? ? ? treeSet.add(stu);
? ? ? treeSet.add(stu1);
? ? ? treeSet.add(stu2);
? ? ? treeSet.add(stu3);
? ? ? treeSet.add(stu4);
? ? ? treeSet.add(stu5);
? ? ? treeSet.add(stu6);
? ? ? treeSet.add(stu7);
? ? ? treeSet.add(stu8);
? ? ? ? System.out.println(treeSet.size());
? ? ? ? for (Students s : treeSet) {
? ? ? ? ? ? System.out.println(s);
? ? ? ? }
? ? }
}
```
如果是內(nèi)部比較器的話,Students要實(shí)現(xiàn)Comparable接口
```java
import java.util.*;
/**
* @author : Gavin
* @date: 2021/7/19 - 07 - 19 - 18:45
* @Description: setTest
* @version: 1.0
*/
import java.util.Comparator;
class Students implements Comparable<Students> {
? ? private int SchoolId;
? ? private String name;
? ? private int age;
? ? public Students(int schoolId, String name, int age) {
? ? ? ? SchoolId = schoolId;
? ? ? ? this.name = name;
? ? ? ? this.age = age;
? ? }
? ? public int getSchoolId() {
? ? ? ? return SchoolId;
? ? }
? ? public void setSchoolId(int schoolId) {
? ? ? ? SchoolId = schoolId;
? ? }
? ? public String getName() {
? ? ? ? return name;
? ? }
? ? public void setName(String name) {
? ? ? ? this.name = name;
? ? }
? ? public int getAge() {
? ? ? ? return age;
? ? }
? ? public void setAge(int age) {
? ? ? ? this.age = age;
? ? }
? ? @Override
? ? public String toString() {
? ? ? ? return "Students{" +
? ? ? ? ? ? ? ? "SchoolId=" + SchoolId +
? ? ? ? ? ? ? ? ", name='" + name + '\'' +
? ? ? ? ? ? ? ? ", age=" + age +
? ? ? ? ? ? ? ? '}';
? ? }
? ? @Override
? ? public int compareTo(Students o) {
? ? ? ? if(this.getAge()==o.getAge()){
? ? ? ? ? ? return 0;
? ? ? ? }
? ? ? ? else if(this.getAge()>o.getAge()){
? ? ? ? ? ? return -1;
? ? ? ? }else {
? ? ? ? ? ? return? 1;
? ? ? ? }
? ? }
}
//class CompareAge implements Comparator<Students> {
//? ? @Override
//? ? public int compare(Students o1, Students o2) {
//
//? ? ? ? return o1.getAge()-o2.getAge();
//? ? }
//}
public class Test {
? ? public static void main(String[] args) {
? ? ? ? Students stu= new Students(1001,"小明",18);
? ? ? ? Students stu1= new Students(1002,"小花",11);
? ? ? ? Students stu2= new Students(1003,"小花1",12);
? ? ? ? Students stu3= new Students(1004,"小花2",13);
? ? ? ? Students stu4= new Students(1005,"小花3",11);
? ? ? ? Students stu5= new Students(1006,"小花4",10);
? ? ? ? Students stu6= new Students(1007,"小花4",12);
? ? ? ? Students stu7= new Students(1008,"小花5",19);
? ? ? ? Students stu8= new Students(1009,"小花6",17);
//? ? ? ? CompareAge compareAge= new CompareAge();
? ? ? TreeSet<Students>treeSet= new TreeSet<>();//指定自定義比較器
? ? ? treeSet.add(stu);
? ? ? treeSet.add(stu1);
? ? ? treeSet.add(stu2);
? ? ? treeSet.add(stu3);
? ? ? treeSet.add(stu4);
? ? ? treeSet.add(stu5);
? ? ? treeSet.add(stu6);
? ? ? treeSet.add(stu7);
? ? ? treeSet.add(stu8);
? ? ? ? System.out.println(treeSet.size());
? ? ? ? for (Students s : treeSet) {
? ? ? ? ? ? System.out.println(s);
? ? ? ? }
? ? }
}
```
按照年齡降序排列,同時(shí)剔除了年兩相同的學(xué)生
![在這里插入圖片描述](https://img-blog.csdnimg.cn/img_convert/38ecf68cee2c1c2ee97cb89ed030a528.png)
可以嘗試下匿名內(nèi)部類或者定義功能接口,lambda表達(dá)式去完成;