public static void main(String[] args) {
LinkedHashSet<Integer> set = new LinkedHashSet<>();
set.add(123);
set.add(123);
System.out.println(set);
}
set集合的底层其实也还是map集合,这是通过map的put方法添加元素
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
put方法进去计算元素的hashCode值,返回一个哈希值
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
如果集合没有初始化,会进行初始化
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
通过哈希值对集合的容量取模(求余),得到的余数作为数组索引值,判断数组在该索引值处是否有值,如果没有则会直接存入
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
如果有值,则会比较是否相同,如果((哈希值相同)并且((地址值相同 )或者(equals结果为true)))则视为相同数据,不会添加到集合里。相反,则会加入到集合里
if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))
e = p;
总的来说,加入元素时,先计算元素的哈希值,比较哈希值是否相同,如果相同则比较地址值或者equals,地址值相同或者equals为true时,则不再加入集合,否则可以加入集合。
注意:
public class Test05{
public static void main(String[] args) {
LinkedHashSet<Student> set = new LinkedHashSet<>();
set.add(new Student(12));
set.add(new Student(12));
System.out.println(set);
}
}
class Student{
private int age;
public Student(int age) {
super();
this.age = age;
}
@Override
public String toString() {
return "Student [age=" + age + "]";
}
}
在集合中添加自定义类时,这是相同年龄的学生,但还是可以添加成功,这是因为在Object类下的equals方法比较的是地址值,而我们视年龄相同的对象为同一个元素,需要重写equals方法和hashCode方法,按照我们的需求进行比较
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
return true;
}
重写完以后就会视为同一个元素