Java各種比較對(duì)象的方式的對(duì)比總結(jié)
一、==和!=操作符
讓我們從==和!=開(kāi)始可以分別判斷兩個(gè)Java對(duì)象是否相同的操作符。
1.1 原始類型(Primitives)
對(duì)于原始類型,相同意味著具有相等的值:
assertThat(1 == 1).isTrue();
感謝自動(dòng)拆箱,在將原語(yǔ)值與其包裝類型對(duì)應(yīng)值進(jìn)行比較時(shí),也可以這樣做:
Integer a = new Integer(1); assertThat(1 == a).isTrue();
如果兩個(gè)整數(shù)的值不同,==運(yùn)算符將返回false,而!=運(yùn)算符將返回true。
1.2 對(duì)象比較
假設(shè)我們要比較兩個(gè)具有相同值的整數(shù)包裝類型:
Integer a = new Integer(1); Integer b = new Integer(1); assertThat(a == b).isFalse();
通過(guò)比較兩個(gè)對(duì)象,這些對(duì)象的值不是1,而是它們?cè)诙褩V械膬?nèi)存地址,因?yàn)檫@兩個(gè)對(duì)象都是使用new操作符創(chuàng)建的。如果我們把a(bǔ)分配給b,我們會(huì)得到不同的結(jié)果:
Integer a = new Integer(1); Integer b = a; assertThat(a == b).isTrue();
現(xiàn)在,讓我們看看使用Integer#valueOf factory方法時(shí)會(huì)發(fā)生什么:
Integer a = Integer.valueOf(1); Integer b = Integer.valueOf(1); assertThat(a == b).isTrue();
在這種情況下,它們被認(rèn)為是相同的。這是因?yàn)関alueOf()方法將整數(shù)存儲(chǔ)在緩存中,以避免創(chuàng)建太多具有相同值的包裝器對(duì)象。因此,該方法為兩個(gè)調(diào)用返回相同的整數(shù)實(shí)例。
對(duì)字符串也是一樣:
assertThat("Hello!" == "Hello!").isTrue();
但是,如果它們是使用new操作符創(chuàng)建的,那么它們就不一樣了。最后,兩個(gè)空引用被認(rèn)為是相同的,而任何非空對(duì)象將被認(rèn)為與空對(duì)象不同:
assertThat(null == null).isTrue(); assertThat("Hello!" == null).isFalse();
當(dāng)然,相等運(yùn)算符的行為可能是有限的。如果我們想比較兩個(gè)映射到不同地址的對(duì)象,并根據(jù)它們的內(nèi)部狀態(tài)將它們視為相等,那該怎么辦?我們將在下一節(jié)中看到如何操作。
二、Object的equals方法
現(xiàn)在,讓我們用equals()方法討論一個(gè)更廣泛的相等概念。這個(gè)方法是在Object類中定義的,以便每個(gè)Java對(duì)象都繼承它。默認(rèn)情況下,它的實(shí)現(xiàn)比較對(duì)象內(nèi)存地址,因此它的工作方式與==運(yùn)算符相同。但是,我們可以重寫(xiě)這個(gè)方法來(lái)定義相等對(duì)我們的對(duì)象意味著什么。
首先,讓我們看看它對(duì)現(xiàn)有對(duì)象(如Integer)的表現(xiàn):
Integer a = new Integer(1); Integer b = new Integer(1); assertThat(a.equals(b)).isTrue();
當(dāng)兩個(gè)對(duì)象相同時(shí),該方法仍然返回true。我們應(yīng)該注意,我們可以傳遞一個(gè)空對(duì)象作為方法的參數(shù),但是當(dāng)然,不能作為調(diào)用方法的對(duì)象。我們可以對(duì)自己的對(duì)象使用equals()方法。假設(shè)我們有一個(gè)Person類:
public class Person { private String firstName; private String lastName; public Person(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } }
我們可以重寫(xiě)該類的equals()方法,以便根據(jù)兩個(gè)人的內(nèi)部詳細(xì)信息進(jìn)行比較:
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person that = (Person) o; return firstName.equals(that.firstName) && lastName.equals(that.lastName); }
三、Objects的靜態(tài)方法equals
現(xiàn)在讓我們看看Objects的equals靜態(tài)方法。我們前面提到過(guò),不能使用null作為第一個(gè)對(duì)象的值,否則會(huì)拋出NullPointerException。Objects助手類的equals()方法解決了這個(gè)問(wèn)題。它接受兩個(gè)參數(shù)并比較它們,同時(shí)處理空值。
讓我們?cè)俅伪容^Person對(duì)象:
Person joe = new Person("Joe", "Portman"); Person joeAgain = new Person("Joe", "Portman"); Person natalie = new Person("Natalie", "Portman"); assertThat(Objects.equals(joe, joeAgain)).isTrue(); assertThat(Objects.equals(joe, natalie)).isFalse();
如前所述,該方法處理空值。因此,如果兩個(gè)參數(shù)都為null,則返回true;如果只有一個(gè)參數(shù)為null,則返回false。這真的很方便。假設(shè)我們想給Person類添加一個(gè)可選的出生日期:
public Person(String firstName, String lastName, LocalDate birthDate) { this(firstName, lastName); this.birthDate = birthDate; }
然后,我們必須更新equals()方法,但是要處理Null。我們可以將此條件添加到equals()方法中:
birthDate == null ? that.birthDate == null : birthDate.equals(that.birthDate);
但是,如果我們?cè)陬愔刑砑釉S多可為null的字段,它可能會(huì)變得非?;靵y。在equals()實(shí)現(xiàn)中使用Objects#equals方法更加簡(jiǎn)潔,并且提高了可讀性:
Objects.equals(birthDate, that.birthDate);
四、Comparable接口
比較邏輯也可用于給對(duì)象排序??杀容^接口允許我們通過(guò)確定一個(gè)對(duì)象是大于、等于還是小于另一個(gè)對(duì)象來(lái)定義對(duì)象之間的順序。
Compariable接口是泛型的,只有一個(gè)方法compareTo(),該方法接受泛型類型的參數(shù)并返回int。如果當(dāng)前值小于參數(shù),則返回負(fù)值;如果它們相等,則返回0;否則返回正值。
比方說(shuō),在我們的Person類中,我們希望按Person對(duì)象的姓氏進(jìn)行比較:
public class Person implements Comparable<Person> { //... @Override public int compareTo(Person o) { return this.lastName.compareTo(o.lastName); } }
如果使用姓氏大于此的人調(diào)用compareTo()方法,則返回負(fù)int;如果姓氏相同,則返回零;否則返回正int。
五、Comparator接口
Comparator接口是泛型的,并且有一個(gè)compare方法,該方法接受該泛型類型的兩個(gè)參數(shù)并返回一個(gè)整數(shù)。我們?cè)谇懊娴目杀容^接口中已經(jīng)看到了這種模式。
比較器相似;但是,它與類的定義是分離的。因此,我們可以為一個(gè)類定義任意多個(gè)比較器,其中我們只能提供一個(gè)可比較的實(shí)現(xiàn)。
假設(shè)我們有一個(gè)在網(wǎng)頁(yè)中有一個(gè)展示人信息的表格,我們想讓用戶能夠按名字而不是姓氏對(duì)他們進(jìn)行排序。如果我們還想保持當(dāng)前的實(shí)現(xiàn),那么使用Comparable是不可能的,但是我們可以實(shí)現(xiàn)自己的比較器。
讓我們創(chuàng)建一個(gè)Person Comparator,它將只根據(jù)他們的名字進(jìn)行比較:
Comparator<Person> compareByFirstNames = Comparator.comparing(Person::getFirstName);
現(xiàn)在讓我們用這個(gè)比較器對(duì)一組人進(jìn)行排序:
Person joe = new Person("Joe", "Portman"); Person allan = new Person("Allan", "Dale"); List<Person> people = new ArrayList<>(); people.add(joe); people.add(allan); people.sort(compareByFirstNames); assertThat(people).containsExactly(allan, joe);
在compareTo()實(shí)現(xiàn)中,可以使用Comparator接口上的其他方法:
@Override public int compareTo(Person o) { return Comparator.comparing(Person::getLastName) .thenComparing(Person::getFirstName) .thenComparing(Person::getBirthDate, Comparator.nullsLast(Comparator.naturalOrder())) .compare(this, o); }
在這種情況下,我們首先比較姓,然后比較名。然后,我們比較出生日期,但是由于它們是可空的,我們必須說(shuō)明如何處理它,所以我們給出了第二個(gè)參數(shù),告訴它們應(yīng)該根據(jù)它們的自然順序進(jìn)行比較,但是空值最后才是。
六、使用Apache Commons
現(xiàn)在讓我們看看apachecommons庫(kù)。首先,讓我們導(dǎo)入Maven依賴項(xiàng):
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.11</version> </dependency>
6.1 ObjectUtils的notEqual方法
首先,我們來(lái)討論ObjectUtils#notEqual方法。它需要兩個(gè)對(duì)象參數(shù),根據(jù)它們自己的equals()方法實(shí)現(xiàn)來(lái)確定它們是否相等。它還處理空值。
讓我們重新使用我們的字符串示例:
String a = new String("Hello!"); String b = new String("Hello World!"); assertThat(ObjectUtils.notEqual(a, b)).isTrue();
應(yīng)該注意,ObjectUtils有一個(gè)equals()方法。但是,自從Java7出現(xiàn)Objects#equals之后,這種方法就被棄用了。
6.2 ObjectUtils的compare方法
現(xiàn)在,讓我們用ObjectUtils#compare方法比較對(duì)象順序。它是一個(gè)泛型方法,它接受該泛型類型的兩個(gè)可比較參數(shù)并返回一個(gè)整數(shù)。
讓我們?cè)倏纯慈绾问褂米址?/p>
String first = new String("Hello!"); String second = new String("How are you?"); assertThat(ObjectUtils.compare(first, second)).isNegative();
默認(rèn)情況下,該方法通過(guò)將空值視為更大值來(lái)處理空值。它提供了一個(gè)重載版本,它提供了一個(gè)布爾參數(shù)來(lái)反轉(zhuǎn)該行為,并將它們考慮得更小。
七、使用Guava
現(xiàn)在,我們來(lái)看看Guava。Guava 是 Google 的一個(gè)開(kāi)源項(xiàng)目,包含許多 Google 核心 Java 常用庫(kù),如:集合 [collections] 、緩存 [caching] 、原生類型支持 [primitives support] 、并發(fā)庫(kù) [concurrency libraries] 、通用注解 [common annotations] 、字符串處理 [string processing] 與 I/O 等。
首先,讓我們導(dǎo)入依賴項(xiàng):
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>29.0-jre</version> </dependency>
7.1 Objects的equal方法
與apache commons庫(kù)類似,Google為我們提供了一種方法來(lái)確定兩個(gè)對(duì)象是否相等,objects#equal。盡管它們有不同的實(shí)現(xiàn),但它們返回相同的結(jié)果:
String a = new String("Hello!"); String b = new String("Hello!"); assertThat(Objects.equal(a, b)).isTrue();
盡管它沒(méi)有被標(biāo)記為deprecated,但是這個(gè)方法的JavaDoc說(shuō)它應(yīng)該被視為deprecated,因?yàn)閖ava7提供了Objects#equals方法。
7.2 Comparison方法
現(xiàn)在,Guava庫(kù)沒(méi)有提供一個(gè)方法來(lái)比較兩個(gè)對(duì)象(我們將在下一節(jié)中看到如何實(shí)現(xiàn)這一點(diǎn)),但它確實(shí)提供了比較原始值的方法。讓我們看看Ints helper類的compare()方法是如何工作的:
assertThat(Ints.compare(1, 2)).isNegative();
通常,如果第一個(gè)參數(shù)小于、等于或大于第二個(gè)參數(shù),則返回一個(gè)整數(shù),該整數(shù)可能為負(fù)、零或正。除了bytes之外,所有基元類型都有類似的方法。
7.3 ComparisonChain類
最后,Guava庫(kù)提供了ComparisonChain類,它允許我們通過(guò)一系列比較來(lái)比較兩個(gè)對(duì)象。我們可以很容易地比較兩個(gè)人的名字和姓氏:
Person natalie = new Person("Natalie", "Portman"); Person joe = new Person("Joe", "Portman"); int comparisonResult = ComparisonChain.start() .compare(natalie.getLastName(), joe.getLastName()) .compare(natalie.getFirstName(), joe.getFirstName()) .result(); assertThat(comparisonResult).isPositive();
底層比較是使用compareTo()方法實(shí)現(xiàn)的,因此傳遞給compare()方法的參數(shù)必須是原始類型或可比較的對(duì)象。
八、完整代碼
到此這篇關(guān)于Java各種比較對(duì)象的方式的對(duì)比總結(jié)的文章就介紹到這了,更多相關(guān)Java比較對(duì)象的方式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java pom.xml parent引用報(bào)錯(cuò)問(wèn)題解決方案
這篇文章主要介紹了Java pom.xml parent引用報(bào)錯(cuò)問(wèn)題解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08IntelliJ IDEA Run時(shí)報(bào)“無(wú)效的源發(fā)行版:16“錯(cuò)誤問(wèn)題及解決方法
這篇文章主要介紹了IntelliJ IDEA Run時(shí)報(bào)“無(wú)效的源發(fā)行版:16“錯(cuò)誤問(wèn)題及解決方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-05-05SpringBoot如何通過(guò)Feign調(diào)用傳遞Header中參數(shù)
這篇文章主要介紹了SpringBoot通過(guò)Feign調(diào)用傳遞Header中參數(shù),本文給大家分享兩種解決方案給大家詳細(xì)講解,需要的朋友可以參考下2023-04-04java中sleep方法和wait方法的五個(gè)區(qū)別
這篇文章主要介紹了java中sleep方法和wait方法的五個(gè)區(qū)別,sleep?方法和?wait?方法都是用來(lái)將線程進(jìn)入休眠狀態(tài),但是又有一些區(qū)別,下面我們就一起來(lái)看看吧2022-05-05Java中基于推、拉模式的sentinel規(guī)則持久化詳解
這篇文章主要介紹了Java中基于推、拉模式的sentinel規(guī)則持久化詳解,推模式是sentinelDashboard?把規(guī)則推給Nacos,Nacos監(jiān)聽(tīng)規(guī)則的變化推給微服務(wù),拉模式是sentinelDashboard?把規(guī)則直接給微服務(wù),?Nacos定時(shí)的同步微服務(wù)端的規(guī)則,需要的朋友可以參考下2023-09-09Java實(shí)現(xiàn)簡(jiǎn)易撲克牌游戲的完整實(shí)例
這篇文章主要介紹了Java實(shí)現(xiàn)簡(jiǎn)易撲克牌游戲的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04java從命令行獲取數(shù)據(jù)的三種方式代碼實(shí)例
這篇文章主要介紹了java從命令行獲取數(shù)據(jù)的三種方式代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12