云原生 + 边缘计算 +KubeEdge,打造智能边缘治理平台
download:网盘链接
hashCode() 和 equals() 的区别
equals()
equals() 方法用于比较两个对象是否相等,它与 == 相等比较符有着本质的不同。
在万物皆对象的 Java 体系中,零碎把判断对象是否相等的权力交给程序员。具体的措施是把 equals() 方法写到 Object 类中,并让所有类继承 Object 类。这样程序员就能在自定义的类中重写 equals() 方法, 从而实现自己的比较逻辑。
hashCode()
hashCode() 的意义是哈希值, 哈希值是经哈希函数运算后失去的后果,哈希函数能够保障雷同的输出能够失去雷同的输入(哈希值),然而不能够保障不同的输出总是能得出不同的输入。
当输出的样本量足够大时,是会产生哈希冲突的,也就是说不同的输出产生了雷同的输入。
暂且不谈冲突,就雷同的输出能够产生雷同的输入这点而言,是及其宝贵的。它使得零碎只需要通过简略的运算,在工夫复杂度 O(1)的情况下就能得出数据的映射关系,根据这种个性,散列表应运而生。
一种支流的散列表实现是:用数组作为哈希函数的输入域,输出值通过哈希函数计算后失去哈希值。而后根据哈希值,在数组种找到对应的存储单元。当发生冲突时,对应的存储单元以链表的形式保存冲突的数据。
两者关系
在大多数编程实践中,归根结底会落实到数据的存取问题上。在汇编语言期间,你需要老诚恳实地对每个数据操作编写存取语句。
而随着期间发展到明天,咱们都用更便利灵活的高级语言编写代码,比如 Java。
Java 以面向对象为中心思想,封装了一系列操作数据的 api,升高了数据操作的复杂度。
但在咱们对数据进行操作之前,首先要把数据按照肯定的数据结构保存到存储单元中,否则操作数据将无从谈起。
然而不同的数据结构有各自的个性,咱们在存储数据的时候需要抉择合适的数据结构进行存储。Java 根据不同的数据结构提供了丰富的容器类,便利程序员抉择适合业务的容器类进行开发。
通过继承关系图咱们看到 Java 的容器类被分为 Collection 和 Map 两大类,Collection 又可能进一步分为 List 和 Set。其中 Map 和 Set 都是不容许元素重复的,严格来说 Map 存储的是键值对,它不容许重复的键值。
值得注意的是:Map 和 Set 的绝大多数实现类的底层都会用到散列表结构。
讲到这里咱们提取两个关键字不容许重复和散列表结构,
回顾 hashCode() 和 equals() 的个性,你是否想到了些什么货色呢?
equals()力不从心
下面提到 Set 和 Map 不存放重复的元素(key),这些容器在存储元素的时必须对元素做出判断:在以后的容器中有没有和新元素雷同的元素?
你可能会想:这容易呀,间接调用元素对象的 equals() 方法进行比较不就行了吗?
如果容器中的存储的对象数量较少,这确实是个好主意,然而如果容器中存放的对象达到了肯定的范畴,要调用容器中所有对象的 equals() 方法和新元素进行比较,就不是一件容易的事件了。
就算 equals() 方法的比较逻辑简略无比,总的来说也是一个工夫复杂度为 O(n) 的操作啊。
hashCode() 小力出奇迹
但在散列表的基础上,判断新对象是否和已存在对象雷同就容易得多了。
因为每个对象都自带有 hashCode(),这个 hashCode 将会用作散列表哈希函数的输出,hashCode 通过哈希函数计算后失去哈希值,新对象会根据哈希值,存储到相应的内存的单元。
咱们不妨假设两个雷同的对象,hashCode() 肯定雷同,这么一来就体现出哈希函数的能力了。
因为雷同的输出肯定会产生雷同的输入,于是如果新对象,和容器中已存在的对象雷同,新对象计算出的哈希值就会和已存在的对象的哈希值产生冲突。
这时容器就能判断:这个新加入的元素已经存在,需要另作处理:覆盖掉原来的元素(key)或舍弃。
按照这个思路,如果这个元素计算出的哈希值所对应的内存单元没有产生冲突,也就是没有重复的元素,那么它就可能直接插入。
所以当使用 hashCode() 时,判断是否有雷同元素的代价,只是一次哈希计算,工夫复杂度为 O(1),这极大地提高了数据的存储性能。
总结
1、如果两个对象雷同(即用 equals 比较返回 true),那么它们的 hashCode 值肯定要雷同;
2、如果两个对象的 hashCode 雷同,它们并不一定雷同(即用 equals 比较返回 false)。
为了提高程序的效率才实现了 hashcode 方法,先进行 hashcode 的比较,如果不同,那没就不必在进行 equals 的比较了,这样就大大减少了 equals 比较的次数。