深入理解Java集合框架系列-第二章、Java集合中的hashCode方法

时间:2021-07-02 16:20:07

深入理解Java集合框架系列-第二章、Java集合中的hashCode方法

 

众所周知,Java中的集合框架三大类,一类是List,一类是Set,另一类是QueueList内的元素是有序的,元素可以重复。Set元素无序,但元素不可重复。要想保证元素不重复,两个元素是否重复应该依据什么来判断呢?用Object.equals方法。但若每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说若集合中已有10000个元素,那么第10001个元素加入集合时,它就要调用10000equals方法。这显然会大大降低效率。那么有没有更好的方法了?
    Java中数据存储方式最底层的两种结构,一种是数组,另一种就是链表,数组的特点:连续空间,寻址迅速,但是在删除或者添加元素的时候需要有较大幅度的移动,所以查询速度快,增删较慢。而链表正好相反,由于空间不连续,寻址困难,增删元素只需修改指针,所以查询慢、增删快。

有没有一种数据结构来综合一下数组和链表,以便发挥他们各自的优势?答案是肯定的!就是:哈希表。哈希表具有较快(常量级)的查询速度,及相对较快的增删速度,所以很适合在海量数据的环境中使用。一般实现哈希表的方法采用拉链法,我们可以理解为链表的数组,见上图:

从上图中,我们可以发现哈希表是由数组+链表组成的,一个长度为16的数组中,每个元素存储的是一个链表的头结点。那么这些元素是按照什么样的规则存储到数组中呢。一般情况是通过hash(key)%len获得,也就是元素的key的哈希值对数组长度取模得到。比如上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12。所以1228108以及140都存储在数组下标为12的位置。它的内部其实是用一个Entity数组来实现的,属性有keyvaluenext

HashMapHashTableHashSet等集合对象接收一个元素时,默认根据该对象的内存地址算出hashCode,看它属于哪一个具体的数组元素,在这个数组元素里调用equeals方法。这么做确实提高了效率。但一个面临问题:若两个对象equals相等,但不在一个数组元素里,根本没有机会进行比较,会被认为是不同的对象。所以Java对于eqauls方法和hashCode方法是这样规定的:

1、如果两个对象相同,那么它们的hashCode值一定要相同。也告诉我们重写equals方法,一定要重写hashCode方法。

2、如果两个对象的hashCode相同,它们并不一定相同,这里的对象相同指的是用eqauls方法比较。

如下代码,如果没有重写hashCode()equals(),则输出3,如果重写hashCode()equals(),则输出值为1Coordinate的代码请查看上一篇文章。

Coordinate c1=new Coordinate(12,34);

Coordinate c2=new Coordinate(12,34);

Coordinate c3=new Coordinate(12,34);

Set<Coordinate> sets=new HashSet<Coordinate>();

  sets.add(c1);

  sets.add(c2);

  sets.add(c3);

System.out.println(sets.size());