要懂抽象,也要懂是如何产生抽象。
课上学到,在Java中,“==”比较的是两个对象的地址是否相同,想要比较行为等价性需要使用equals()方法,而所有数据类型的equals()方法都继承自Object类的equals方法,相当于“==“,为了实现对ADT行为等价性的判断需要我们override equals()。
那么自然产生的一个问题是,Java给我们提供的一些常用数据结构,如Set和List的equals方法是否重写了呢,如果重写又是如何实现的呢?
写一段简单的代码,比较两个Set<Integer>
import java.util.*; public class settest { public static void main(String[] args) { HashSet<Integer> set1 =new HashSet<>(); HashSet<Integer> set2 = new HashSet<>(); if(set1.equals(set2)){ System.out.println("equal"); } } }
输出为equal,可见其equals()已经重写过了,接下来开始探究其具体的实现。经查找资料发现List的equals()实现逻辑是对两个List中的对应元素依次调用其自身的equals()方法。
List是有序的,这样的实现逻辑很好理解,那么无序的Set又是怎样呢?为此我查看了其源码AbstractSet.java
public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Set)) return false; Collection<?> c = (Collection<?>) o; if (c.size() != size()) return false; try { return containsAll(c); } catch (ClassCastException unused) { return false; } catch (NullPointerException unused) { return false; } }
可以看出关键在于containsAll(),再去AbstractCollection.java查看代码,containsAll()就是逐个调用contains(),contains()的代码如下:
public boolean contains(Object o) { Iterator<E> it = iterator(); if (o==null) { while (it.hasNext()) if (it.next()==null) return true; } else { while (it.hasNext()) if (o.equals(it.next())) return true; } return false; }
随着不断向底层深入,我们会发现最终还是调用其成员泛型E的equals(),而泛型的equals()我们无法继续查看代码了,那么抽象数据类型的内容相等通过什么实现呢,答案是基本类型,一切抽象类的“内容相等”归根结底都要依托于基本类型之间的某种相等关系来实现。
从面向过程编程转向面向对象编程,一开始会发现很多之前需要考虑的问题可以不用考虑了,只要了解功能的抽象即可,但不要忘记所有的高层次的抽象也都来自低层次的组成与操作,而不是什么外来的别的东西。我之所以研究Set的equals实现多少就是因为幻想着有什么截然不同的判断ADT行为等价性的方法,但归根结底还是要靠基本数据类型实现。