HashSet
HsahSet底层是哈希表数据结构,可以保证元素的唯一性,但不保证元素有序
唯一性主要依赖于两个方法:一个是hasCode方法,一个是equals方法。
如果我们想要存储元素,想让元素具有唯一性,只需要在对应的元素中重写hasCode方法和equals方法
代码演示:集合中添加字符串
import java.util.HashSet;
public class HashSetDemo {
public static void main(String[] args) {
HashSet<String> h = new HashSet<String>();
h.add("hello");
h.add("world");
h.add("java");
h.add("world");
for(String s : h){
System.out.println(s);
}
}
}
运行结果:
hello
java
world
解释:上面中我添加了同样的字符串,”world“,但是打印的时候只有一个,说明其唯一性,但是为什么唯一。
查看原码可知:在String类中,有equals方法和hashCode方法,在添加的时候就会进行比较,看元素是否相同,是就不添加,不是才添加。下面来看一下原码:
h.add("hello") ;
h.add("world") ;
h.add("java") ;
h.add("world") ;
public interface Collection {....}
public interface Set extends Collection {...}
public class HashSet implements Set {
private static final Object PRESENT = new Object();//成员类
public boolean add(E e) {//e="hello"
return map.put(e, PRESENT)==null;
}
//add方法可以看出,其本质是返回map的put方法。
//map就是HashSet的构造方法中的new HashMap<>(),如下所示,在我们new HashSet()的时候就已经创建//了,创建了一个HashMap的对象,将地址值赋给map,这样map就指向了HashMap,代表了HashMap
public HashSet() {
map = new HashMap<>();//返回一个hashmap对象
}
public class HashMap {
//这是HashMap的put方法,首先传递了两个参数key和vaule,hello就key的值
public V put(K key, V value) {// key="hello",vaule是PERSON 也就上面的成员类
// 这一步是判读哈希表是否存在 , 如果不存在就创建一个哈希表
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null) // key="hello",所以这一步不走
return putForNullKey(value);
int hash = hash(key); //将key的值传递给hash方法, 通过hashCode方法计算一个int类型的值
int i = indexFor(hash, table.length); // 在哈希表中查找是否存在该hash值
for (Entry<K,V> e = table[i]; e != null; e = e.next) {//这一步要等有重复元素是才会走
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {//比较哈希值是否相等,如果相等
V oldValue = e.value;//再判断是否是同一对象,或者判断内容是否相等
e.value = value;
e.recordAccess(this);
return oldValue; //如果是同一元素,就返回oldVaule,就是该元素没有被添加进去
}
}
modCount++;
addEntry(hash, key, value, i); // 把元素添加到集合中
return null;
}
//这是hash方法,用来获取hashCode值
final int hash(Object k) {// key="hello"
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode();// key="hello"
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);//最终影响返回结果的就是h ^= k.hashCode();这一步,这是计算哈希值
}
}
private transient HashMap<E,Object> map;//hashmap集合
}
transient int hashSeed = 0;
上面的原码是为了说明方便而粘贴下来的
总结:其原理就是将元素的哈希值计算出来,和哈希表中的比较,如果没有哈希表就创建一个,比较之后判断是否存在,由判断是否是同一对象或相等,是就不添加,不是就添加,
HashSet添加对象元素:
import java.util.HashSet;
public class HashSetDemo2 {
public static void main(String[] args) {
HashSet<Student> h = new HashSet<Student>();
Student s1 = new Student("小王",23);
Student s2 = new Student("小张",24);
Student s3 = new Student("小李",21);
Student s4 = new Student("小王",23);
h.add(s1);
h.add(s2);
h.add(s3);
h.add(s4);
for(Student s : h){
System.out.println(s.getName()+"----"+s.getAge());
}
}
}
运行结果:
小王----23
小张----24
小李----21
解释:如果想保证添加对象的唯一性,就要在对象类中重写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;
Student other = (Student) 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;
}
不用担心,可以使用eclipse自动生成,但是要知道原理,和上面的其实差不多,都是首先计算哈希值,然后比较哈希值,在比较对象和内容,相同就不添加,不同才添加
本人也在学习中,如有不足和错误之处,请各位指出 |
|