因为是Collection的子类,所以Set的方法使用上和Collection差异不大,可以使用Collection的所有方法。Set添加的元素是无需,不可重复的。在添加自定义对象元素时,若不重写equals()与hashCode()则会添加两个相同的对象元素。
Person类(此时注释hashCode()方法):
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
// @Override
// public int hashCode() {
// return Objects.hash(name, age);
// }
}
@Test
public void test(){
HashSet hashset = new HashSet();
hashset.add("tom");
hashset.add(123);
hashset.add(new Person("jack",34));
hashset.add(new Person("jack",34));
//使用iterator遍历
Iterator iterator = hashset.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
?输出结果:
tom Person{name='jack', age=34} 123 Person{name='jack', age=34}
若将hashCode()注释去掉,则输出结果为:
?tom Person{name='jack', age=34} 123
基本原理:
这里的HashSet涉及的存储与数据结构中的散列存储一致,首先通过哈希函数例如除留取余法,可以确定元素存放在哈希表的位置,在这其中肯定会有相关元素会有在哈希表上的位置冲突,所以为了解决冲突,又有开放地址法与链地址法。在存储过程中,要通过重写hashCode()方法比较他们的hash地址是否相同,若相同则再调用重写后的equals()方法判断其对象内容是否相同,这就是为什么注释掉hashCode()方法后,会添加同样的元素数据到Set集合中了,因为没有重写hashCode()方法,此时会调用Object中自带的hashCode()方法,得到随机的哈希值添加到散列表中,所以哈希值不同,且位置没有其他元素,并没有调用equals()方法就已经添加到set集合中了,但是调用重写hashCode()方法后,通过哈希函数运算后的哈希值实际上是一致的。
总结:
①若此位置没有元素,则直接添加成功,无需调用方法判断
②若此位置有其他元素,则比较哈希值,哈希值不一致,无需调用equals()方法,存储成功。
哈希值一致,equals()比较内容,内容不一样,存储成功(以链地址法存储,其中jdk7使用的是头插法,jdk8使用的尾插法)。
hash底层也是数组,初始容量为16,若使用率超过0.75,则会扩容2倍。????????
|