网站建设网站软件有哪些内容seo技巧课程
引言
Java开发经常需要借助各种集合类,常用的集合有List、Set和Map。Map存放的是key-value形式的数据,List与Set直接存放数据。List与Set的显著区别是,List中的数据是有序、可重复,而Set中的数据无序且不可重复。那么Set是如何实现数据的不可重复插入,本文以HashSet为例,分析HashSet是如何实现元素不可重复。
一、引入场景
首先,我们定义一个学生对象,包含简单的两个属性:学号(id)与姓名(name)。只要id相同,就认为对象是重复的。
- public class Student {
- private Integer id;
- private String name;
- public Student(Integer id, String name) {
- this.id = id;
- this.name = name;
- }
- public String toString() {
- return "Student{" +
- "id=" + id +
- ", name='" + name + '\'' +
- '}';
- }
- }
- public class HashSetDemo {
- public static void main(String[] args) {
- Set<Student> set = new HashSet<>();
- Student student1 = new Student(1001,"young");
- Student student2 = new Student(1001,"young");
- set.add(student1);
- set.add(student2);
- set.forEach(student -> System.out.println(student));
- }
- }
看到这个结果应该很惊讶,我们期望的是Set中存放的数据不可重复,而代码里的两个Student对象,学号与姓名都相同,插入到HashSet中后应该只保留一个对象。问题出在了哪里?要想找出原因,只有去源码里寻找答案。
二、HashSet源码分析
阅读HashSet的源码,可以发现HashSet的源码逻辑很简单。只有两个field,分别为HashMap类型的map和一个傀儡对象PRESENT。
- public class HashSet<E>
- extends AbstractSet<E>
- implements Set<E>, Cloneable, java.io.Serializable
- {
- static final long serialVersionUID = -5024744406713321676L;
- private transient HashMap<E,Object> map;
- // 傀儡对象,只是用来作为空的value值
- private static final Object PRESENT = new Object();
- /**
- * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
- * default initial capacity (16) and load factor (0.75).
- */
- public HashSet() {
- map = new HashMap<>();
- }
- //...
- }
- public boolean add(E e) {
- return map.put(e, PRESENT)==null;
- }
三、浅析HashMap
HashMap的设计十分精巧,本文不作深入分析,只简要描述HashMap的原理。HashMap的结构是一个哈希数组,数组节点在数组中的下标由节点的哈希值决定。了解哈希数组的同学都知道,不同元素的哈希映射有可能是相同的,意味着两个完全不同的节点映射到相同的数组下标上。HashMap并不对映射到相同位置上的节点二次散列,而是将节点放在已有节点的尾端。因此,数组节点的类型实际上是一个链表结构。
了解了HashMap的数据结构后,就能明白HashMap中有两个主要的判断。当key-value键值对插入到HashMap中。
首先是对key值哈希,得到key在哈希数组中的下标。HashMap中哈希是调用对象key的hashCode()方法,然后再做异或处理。
如果当前下标上已经有值,说明有两个元素的映射到相同的下标。此时,需要第二次判断,调用key对象的equals方法,对象不相同则插入链表的表尾,对象相同则覆盖。
四、验证
根据前面的分析,改造我们定义的Student类,为Student类增加hashCode()与equals()方法。
- public class Student {
- private Integer id;
- private String name;
- public Student(Integer id, String name) {
- this.id = id;
- this.name = name;
- }
- public String toString() {
- return "Student{" +
- "id=" + id +
- ", name='" + name + '\'' +
- '}';
- }
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Student student = (Student) o;
- return id.equals(student.id);
- }
- public int hashCode() {
- return id.hashCode();
- }
- }
此时,HashSet中只有一个对象。
转载: https://blog.csdn.net/veYoung/article/details/72420317