C# 对象比较
最近工作中接触到这个,有点迷糊。
.Net 中主要有四种相等比较,分别是:
- ==操作符、
- Object.Equals方法、
- Object.ReferenceEquals方法、
- 对象实例的Equals方法。
Object 的 Equals 静态方法实际上是对实例Equals方法的扩展,
增加了 null 的判断,适用于比较两个可能为空引用的对象。
对于值类型,和 Equals 实例方法完全一样。
public static bool Equals(object objA, object objB)
{
if (objA == objB)
{
return true;
}
if (objA != null && objB != null)
{
return objA.Equals(objB);
}
return false;
}
ReferenceEquals 方法是比较两个对象的引用是否相同,即栈上的地址是否一样
对于值类型没有意义,参数中若有值类型参数出现,必定返回false。
对于引用类型,如果方法结果为True,这个相等是最严格、最纯粹、如假包换的相等,说明这两个参数其实是同一个对象,当然无论用其他哪种相等比较方式,同样也应返回True。
public static bool ReferenceEquals(object objA, object objB)
{
return objA == objB;
}
==,
上面,我们说两个Object静态方法区别在值类型和引用类型上,对于其他相等比较区别也主要在此。
一般情况下,不是所有,对于引用类型 == 和 ReferenceEquals 静态方法作用相同;
值类型在这里则有区分,对于一些原生值类型,如int,long,char等,==是直接比较其数值,而且不同类型间可以互相比较,比如int和char,'A’==65返回的是True;
而对于一般的Struct,如果没有在代码中定义==(也包括!=)操作符,是不能用==比较的。
引用类型也可以定义 == 操作符,覆盖CLR原生支持的比较。
最常见的是String类型,它就定义了==操作符,很合理地放宽了相等的条件,使得String类型像原生值类型一样按值比较。String类的 == 操作符其实就是直接调用的被自己重写过Equals方法。
String类是最常用也最特别的一个类,大部分面试都会问到String的特点,除了不可变和内存驻留机制外,其他主要特点就是相等的特殊性了。
public virtual bool Equals(object obj)
{
return RuntimeHelpers.Equals(this, obj);
}
实例 Equals 方法,这是个 Virtual 方法。
定义并使用操作符固然方便,不过除了像String之类的特殊情况,引用类型让 == 保持默认规则是更好的选择,而让 Equals 方法实现业务上的“值”相等。
如果不覆写,Equals 方法也是比较对象的引用。
对于值类型,实现==操作像一个点缀,
而如果想实现相等比较操作,应该优先重写Equals方法(同样若要实现大小比较,应该优先实现 IComparable 接口,而不是实现比较操作符),
从 Object 继承的 Equals 方法用于值类型时,比较两个对象的所有字段,全相等才为True。
为什么一定要优先重写它?
因为所有 .Net Framework 键值集合,都是用Equals实例方法做比较的,
所以它实际上成了.Net中的“潜规则”,无论是原生类型、结构或类的实例,都应以Equals方法作为其标准的相等比较方式,包括我们自己实现的类型。
用实例方法的好处也可以理解,更灵活,我们可以添加一些重载的Equals方法,申明不同的比较前提条件。
与重写的默认Equals方法配合,构成一套完整的比较规则,以符合现实复杂多变的标准。
.Net Framework 为较为复杂的比较提供了一个接口 System.Collections.IEqualityComparer,并提供了内置的实现,
如 StringComparer、EqualityComparer 我们自己写的比较类也可以实现这个接口。
总结:
==操作符、 只能比较基本类型,对于引用类型 == 和 ReferenceEquals 静态方法作用相同;结构体需要重载==比较符才能比较
Object.Equals方法、 使用对象实例的Equals方法,增加null判断
Object.ReferenceEquals方法、 比较两个对象引用地址是否一致,判断是否是同一个对象。 值类型返回false
对象实例的Equals方法。 比较两个对象引用是否一致,可以重载
String 被重载所以可以比较
java
“==”比较两个变量本身的值,即两个对象在内存中的首地址。
“equals()”比较字符串中所包含的内容是否相同。
equals()比较内容前提是对象重载了object的equals()方法,比如String类型,才能成功比较内容是否相同
equal方法有以下五个性质:
- 自反性
- 对称性:不管谁调equal方法都不会影响结果
- 传递性
- 一致性:多次比较不会影响判断结果
- 任何非空对象与调用equal(null)均返回false
java中需要选用合适的方法比较
- 对象域,使用equals方法 。
- 类型安全的枚举,使用equals或== 。
- 可能为null的对象域 : 使用 == 和 equals 。
- 数组域 : 使用 Arrays.equals 。
- 除float和double外的原始数据类型 : 使用 == 。
- float类型: 使用Float.floatToIntBits转换成int类型,然后使用==。
- double类型: 使用Double.doubleToLongBit转换成long类型,然后使用==。
总的来说:类一般调用equal判断,当然首先需要对类进行判空,除了float和double之外的内置类型直接使用==进行内容比较,而float和double使用Float和Double的方法比较,也可以先生成Float或者Double对象调用equal方法,从前面Double的equal方法中可以看出,实质上还是调用了[float/double]To[Int/Long]Bit方法,建议直接使用该方法,避免不必要的开销。
从最新的JDK8而言,有三种实现对象比较的方法:
一、覆写Object类的equals()方法;
二、继承Comparable接口,并实现compareTo()方法;
三、定义一个单独的对象比较器,继承自Comparator接口,实现compare()方法。
总结:
对于float和double的比较使用BigDecimal的compareTo方法
如果需要保证精度,最好是不要使用BigDecimal的double参数的构造函数,因为存在损失double参数精度的可能,最好是使用BigDecimal的String参数的构造函数。最好是杜绝使用BigDecimal的double参数的构造函数。
参考文献:
https://www.cnblogs.com/Aaxuan/p/9520883.html
https://www.cnblogs.com/blueSkyline/p/5655620.html
https://www.jb51.net/article/125173.htm
https://blog.csdn.net/asdfsadfasdfsa/article/details/79332012