1、HashSet
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable
实现原理,基于哈希表(HashMap) 实现。它不允许重复,可以有一个NULL元素,不保证顺序恒久不变。
public HashSet() { map = new HashMap<>(); }
public boolean add(E e) { return map.put(e, PRESENT)==null; }
2、HashSet 的特性
2.1 哈希表的存储结构:数组 + 链表,数组里的每个元素以链表的形式存储。
2.2 如何将对象存储到哈希表中:先计算对象的hashCode值,再对数组的长度求余数,来决定对象要存储在数组中的位置。
2.3 添加元素时把元素作为HashMap的key存储, HashMap的value使用一个固定的Object常量对象。
2.4 Java集合里判断两个对象是否相同,先判断两个对象的hashCode 是相同,相同再用equals() 进行判断,equals相同则是同一个对象,不是则不是同一个对象。
2.5 自定义对象根据实际情况需要重写hashCode() 与 equals()。
class Person { private String name; private Integer age; public Person(String name, Integer age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public int hashCode() { // 基数 int prime = 31; int result = 1; result = prime * result + name == null? 0 : name.hashCode(); result = prime * result + age; return result; } @Override public boolean equals(Object obj) { if(this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof Person)) { return false; } Person person = (Person)obj; if (!name.equals(person.getName())) { return false; } else if(age != person.getAge()) { return false; } return true; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } }
3、选择31作为基数的原因:
3.1 选择系数的时候要选择尽量长的系数并且让乘法尽量不要溢出的系数,因为如果计算出来的hash地址越大,所谓的“冲突”就越少,查找起来效率也会提高。
3.2 31*N可以被编译器优化为左移5位后减1即31*N = (N<<5)-1,有较高的性能。