HashSet类
底层:数组+链表[+红黑树(JDK1.8+)]
HashSet底层是HashMap,用了HashMap的key(key不可重复)。
public HashSet() { map = new HashMap<>(); } public boolean add(E e) { return map.put(e, PRESENT)==null; } private static final Object PRESENT = new Object(); // PRESENT是一个不变的值。
基于HashCode计算元素存放位置。
当存入元素的哈希码相同时,会调用equals进行确认,如结果为true,则拒绝后者存入。
如果要改写HashSet判断关于某两个对象是否相等,需要同时改写
hashCode()
和equals()
方法。
1. HashSet
Set<String> set = new HashSet<>();
// 1.添加数据
set.add("tang");
set.add("he");
set.add("yu");
System.out.println("数据个数:" + set.size());
System.out.println(set.toString());//无序输出
// 2.删除数据
/*
* set.remove("tang"); System.out.println(set.toString());
*/
// 3.遍历【重点】
// 3.1 使用增强for
for (String string : set) {
System.out.println(string);
}
// 3.2 使用迭代器
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
// 4.判断
System.out.println(set.contains("tang"));
System.out.println(set.isEmpty());
HashSet<Person> hashSet = new HashSet<>();
Person p1 = new Person("tang", 21);
Person p2 = new Person("he", 22);
Person p3 = new Person("yu", 21);
// 1.添加元素
hashSet.add(p1);
hashSet.add(p2);
hashSet.add(p3);
// 重复,添加失败
hashSet.add(p3);
// 直接new一个相同属性的对象,依然会被添加,不难理解。
// 假如相同属性便认为是同一个对象,怎么修改?
hashSet.add(new Person("yu", 21));
System.out.println(hashSet.toString());
// 2.删除元素
hashSet.remove(p2);
// 3.遍历
// 3.1 增强for
for (Person person : hashSet) {
System.out.println(person);
}
// 3.2 迭代器
Iterator<Person> iterator = hashSet.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
// 4.判断
System.out.println(hashSet.isEmpty());
// 直接new一个相同属性的对象结果输出是false,不难理解。
// 注:假如相同属性便认为是同一个对象,该怎么做?
System.out.println(hashSet.contains(new Person("tang", 21)));
注:hashSet存储过程:
- 根据hashCode计算保存的位置,如果位置为空,则直接保存,否则执行第二步。
- 执行equals方法,如果方法返回true,则认为是重复,拒绝存储,否则形成链表。
存储过程实际上就是重复依据,要解决上述的问题,可以重写hashCode和equals代码:
@Override
public int hashCode () {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals (Object obj){
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
hashCode方法里为什么要使用31这个数字大概有两个原因:
- 31是一个质数,这样的数字在计算时可以尽量减少散列冲突。
- 可以提高执行效率,因为31*i=(i<<5)-i,31乘以一个数可以转换成移位操作,这样能快一点;但是也有网上一些人对这两点提出质疑。
2. HashSet源码
HashSet的存储结构就是HashMap
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
很明了地发现它的add方法调用的就是map的put方法,把元素作为map的key传进去的。。