hashset 容器中只能存储不重复的对象,而说到其中的原理,就不得不提到哈希表的存储结构,与一般的数组和链表,栈的存储结构不同 ,哈希表的存储并不是顺序存储,存入其中的元素 ,他们的地址并不一定是连续的,而是按照元素的特点,通过固定的公式,将元素的哈希值算出之后,根据哈希值来安排他们的存储地址,当hashset在存储数据时,首先,根据元素来算出 哈希值,不同的哈希值一定是不同的元素,存入hashset中,如果哈希值相同,再来进行比较,如果真的相同 ,就放弃存储 ,如果相同就不再存入,大家可以发现这样很大程度上减少了元素之间的比较次数,用不着将对象与别的对象依次比较。
这种算法有点像幼儿园里,小朋友们的排排坐吃果果的理论,小朋友不可能像大人一样乖乖听话,自觉主动的做好,怎么给他们安排座位呢,这样吧 ,先按个头,个头大的坐在前面,个头小的坐在后面,如果有小朋友跑过来 说跟某个座位的人身高一样,我们就把他和那个座位的小朋友比较一下,是否是同一个人,如果不是,就再给这个孩子安排一个作为,如果是,就只在这个座位上。
这里面涉及到两个方法,一个是得出哈希值的方法 ,一个是比较的方法。当hashset存储的是基本数据类型的包装类对象时
HashSet<String> hs = new HashSet<>(); //创建HashSet对象
hs.add("a");
hs.add("a");
hs.add("b");
hs.add("c");
hs.add("d");
string 类 中的hashCode(),equals() 方法 ,会根据字符串的字典顺序,进行排序,而后比较。
而当 hashset 中 存储的是自定义对象时 :
HashSet<Person> hs = new HashSet<>();
hs.add(new Person("张三", 23));
hs.add(new Person("张三", 23));
hs.add(new Person("李四", 24));
hs.add(new Person("李四", 24));
hs.add(new Person("李四", 24));
hs.add(new Person("李四", 24));
这时候,如果person类如果只声明name, age 属性,在存储这六条数据时,都会存入hashset ,只是由于,person类没有重写父类的hashcode() ,和equal()方法,它是按照在堆内存中开辟的地址值来存储的,相同属性的对象它的地址值是不同的,所以都会存入。所以要想用属性值来区别不同的对象需要重写父类的hashcode() ,和equal()方法
@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; //直接返回true
if (obj == null) //传入的对象为null
return false; //返回false
if (getClass() != obj.getClass()) //判断两个对象对应的字节码文件是否是同一个字节码
return false; //如果不是直接返回false
Person other = (Person) obj; //向下转型
if (age != other.age) //调用对象的年龄不等于传入对象的年龄
return false; //返回false
if (name == null) { //调用对象的姓名为null
if (other.name != null) //传入对象的姓名不为null
return false; //返回false
} else if (!name.equals(other.name)) //调用对象的姓名不等于传入对象的姓名
return false; //返回false
return true; //返回true
}
|
|