Java中各种比较对象方式对比
2021/6/15 1:21:00
本文主要是介绍Java中各种比较对象方式对比,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
1.介绍
比较对象是面向对象编程语言的一个基本特征。在本教程中,我们将介绍Java语言的一些特性,这些特性允许我们比较对象。此外,我们还将研究外部库中的这些特性。
2.==和!=操作符
让我们从==和!=开始可以分别判断两个Java对象是否相同的操作符。
2.1 原始类型(Primitives)
对于原始类型,相同意味着具有相等的值:
assertThat(1 == 1).isTrue();
感谢自动拆箱,在将原语值与其包装类型对应值进行比较时,也可以这样做:
Integer a = new Integer(1); assertThat(1 == a).isTrue();
如果两个整数的值不同,==运算符将返回false,而!=运算符将返回true。
2.2 对象比较
假设我们要比较两个具有相同值的整数包装类型:
Integer a = new Integer(1); Integer b = new Integer(1); assertThat(a == b).isFalse();
通过比较两个对象,这些对象的值不是1,而是它们在堆栈中的内存地址,因为这两个对象都是使用new操作符创建的。如果我们把a分配给b,我们会得到不同的结果:
Integer a = new Integer(1); Integer b = a; assertThat(a == b).isTrue();
现在,让我们看看使用Integer#valueOf factory方法时会发生什么:
Integer a = Integer.valueOf(1); Integer b = Integer.valueOf(1); assertThat(a == b).isTrue();
在这种情况下,它们被认为是相同的。这是因为valueOf()方法将整数存储在缓存中,以避免创建太多具有相同值的包装器对象。因此,该方法为两个调用返回相同的整数实例。
对字符串也是一样:
assertThat("Hello!" == "Hello!").isTrue();
但是,如果它们是使用new操作符创建的,那么它们就不一样了。最后,两个空引用被认为是相同的,而任何非空对象将被认为与空对象不同:
assertThat(null == null).isTrue(); assertThat("Hello!" == null).isFalse();
当然,相等运算符的行为可能是有限的。如果我们想比较两个映射到不同地址的对象,并根据它们的内部状态将它们视为相等,那该怎么办?我们将在下一节中看到如何操作。
3.Object的equals方法
现在,让我们用equals()方法讨论一个更广泛的相等概念。这个方法是在Object类中定义的,以便每个Java对象都继承它。默认情况下,它的实现比较对象内存地址,因此它的工作方式与==运算符相同。但是,我们可以重写这个方法来定义相等对我们的对象意味着什么。
首先,让我们看看它对现有对象(如Integer)的表现:
Integer a = new Integer(1); Integer b = new Integer(1); assertThat(a.equals(b)).isTrue();
当两个对象相同时,该方法仍然返回true。我们应该注意,我们可以传递一个空对象作为方法的参数,但是当然,不能作为调用方法的对象。我们可以对自己的对象使用equals()方法。假设我们有一个Person类:
public class Person { private String firstName; private String lastName; public Person(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } }
我们可以重写该类的equals()方法,以便根据两个人的内部详细信息进行比较:
@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); }
4.Objects的静态方法equals
现在让我们看看Objects的equals静态方法。我们前面提到过,不能使用null作为第一个对象的值,否则会抛出NullPointerException。Objects助手类的equals()方法解决了这个问题。它接受两个参数并比较它们,同时处理空值。
让我们再次比较Person对象:
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();
如前所述,该方法处理空值。因此,如果两个参数都为null,则返回true;如果只有一个参数为null,则返回false。这真的很方便。假设我们想给Person类添加一个可选的出生日期:
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);
但是,如果我们在类中添加许多可为null的字段,它可能会变得非常混乱。在equals()实现中使用Objects#equals方法更加简洁,并且提高了可读性:
Objects.equals(birthDate, that.birthDate);
5.Comparable接口
比较逻辑也可用于给对象排序。可比较接口允许我们通过确定一个对象是大于、等于还是小于另一个对象来定义对象之间的顺序。
Compariable接口是泛型的,只有一个方法compareTo(),该方法接受泛型类型的参数并返回int。如果当前值小于参数,则返回负值;如果它们相等,则返回0;否则返回正值。
比方说,在我们的Person类中,我们希望按Person对象的姓氏进行比较:
public class Person implements Comparable<Person> { //... @Override public int compareTo(Person o) { return this.lastName.compareTo(o.lastName); } }
如果使用姓氏大于此的人调用compareTo()方法,则返回负int;如果姓氏相同,则返回零;否则返回正int。
6.Comparator接口
Comparator接口是泛型的,并且有一个compare方法,该方法接受该泛型类型的两个参数并返回一个整数。我们在前面的可比较接口中已经看到了这种模式。
比较器相似;但是,它与类的定义是分离的。因此,我们可以为一个类定义任意多个比较器,其中我们只能提供一个可比较的实现。
假设我们有一个在网页中有一个展示人信息的表格,我们想让用户能够按名字而不是姓氏对他们进行排序。如果我们还想保持当前的实现,那么使用Comparable是不可能的,但是我们可以实现自己的比较器。
让我们创建一个Person Comparator,它将只根据他们的名字进行比较:
Comparator<Person> compareByFirstNames = Comparator.comparing(Person::getFirstName);
现在让我们用这个比较器对一组人进行排序:
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()实现中,可以使用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); }
在这种情况下,我们首先比较姓,然后比较名。然后,我们比较出生日期,但是由于它们是可空的,我们必须说明如何处理它,所以我们给出了第二个参数,告诉它们应该根据它们的自然顺序进行比较,但是空值最后才是。
7.使用Apache Commons
现在让我们看看apachecommons库。首先,让我们导入Maven依赖项:
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.11</version> </dependency>
7.1 ObjectUtils的notEqual方法
首先,我们来讨论ObjectUtils#notEqual方法。它需要两个对象参数,根据它们自己的equals()方法实现来确定它们是否相等。它还处理空值。
让我们重新使用我们的字符串示例:
String a = new String("Hello!"); String b = new String("Hello World!"); assertThat(ObjectUtils.notEqual(a, b)).isTrue();
应该注意,ObjectUtils有一个equals()方法。但是,自从Java7出现Objects#equals之后,这种方法就被弃用了。
7.2 ObjectUtils的compare方法
现在,让我们用ObjectUtils#compare方法比较对象顺序。它是一个泛型方法,它接受该泛型类型的两个可比较参数并返回一个整数。
让我们再看看如何使用字符串:
String first = new String("Hello!"); String second = new String("How are you?"); assertThat(ObjectUtils.compare(first, second)).isNegative();
默认情况下,该方法通过将空值视为更大值来处理空值。它提供了一个重载版本,它提供了一个布尔参数来反转该行为,并将它们考虑得更小。
8. 使用Guava
现在,我们来看看Guava。Guava 是 Google 的一个开源项目,包含许多 Google 核心 Java 常用库,如:集合 [collections] 、缓存 [caching] 、原生类型支持 [primitives support] 、并发库 [concurrency libraries] 、通用注解 [common annotations] 、字符串处理 [string processing] 与 I/O 等。
首先,让我们导入依赖项:
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>29.0-jre</version> </dependency>
8.1 Objects的equal方法
与apache commons库类似,Google为我们提供了一种方法来确定两个对象是否相等,objects#equal。尽管它们有不同的实现,但它们返回相同的结果:
String a = new String("Hello!"); String b = new String("Hello!"); assertThat(Objects.equal(a, b)).isTrue();
尽管它没有被标记为deprecated,但是这个方法的JavaDoc说它应该被视为deprecated,因为java7提供了Objects#equals方法。
8.2 Comparison方法
现在,Guava库没有提供一个方法来比较两个对象(我们将在下一节中看到如何实现这一点),但它确实提供了比较原始值的方法。让我们看看Ints helper类的compare()方法是如何工作的:
assertThat(Ints.compare(1, 2)).isNegative();
通常,如果第一个参数小于、等于或大于第二个参数,则返回一个整数,该整数可能为负、零或正。除了bytes之外,所有基元类型都有类似的方法。
8.3 ComparisonChain类
最后,Guava库提供了ComparisonChain类,它允许我们通过一系列比较来比较两个对象。我们可以很容易地比较两个人的名字和姓氏:
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()方法实现的,因此传递给compare()方法的参数必须是原始类型或可比较的对象。
9.完整代码
Java对象比较
这篇关于Java中各种比较对象方式对比的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-27消息中间件底层原理资料详解
- 2024-11-27RocketMQ底层原理资料详解:新手入门教程
- 2024-11-27MQ底层原理资料详解:新手入门教程
- 2024-11-27MQ项目开发资料入门教程
- 2024-11-27RocketMQ源码资料详解:新手入门教程
- 2024-11-27本地多文件上传简易教程
- 2024-11-26消息中间件源码剖析教程
- 2024-11-26JAVA语音识别项目资料的收集与应用
- 2024-11-26Java语音识别项目资料:入门级教程与实战指南
- 2024-11-26SpringAI:Java 开发的智能新利器