Java集合之HashSet和LinkedHashSet

来源:互联网 发布:淘宝电子面单打印机 编辑:程序博客网 时间:2024/05/16 11:28

阅读这篇文章只需5-10分钟。

HashSet

HashSetHash 算法来存储集合中的元素,因此具有很好的存取和查找性能。

Hash算法:
(也被翻译成哈希、散列)算法,它能保证快速查找被检索的对象,hash算法的价值在于速度。
当需要查询集合中某个元素时,hash算法可以直接根据该元素的hashCode值计算出该元素的存储位置,从未快速定位该元素。

为什不直接使用数组,还需要使用HashSet呢?

因为数组元素的索引是连续的,而且数组的长度是固定的,无法自由的增加数组的长度。而HashSet不一样,HashSet采用每个元素的hashCode值来计算其存储的位置,从而可以自由增加HashSet的长度,并可以根据元素的hashCode值来访问元素。
因此,当从HashSet中访问元素时,HashSet先计算该元素的hashCode值(也就是调用该对象的hashCode()方法返回的值),然后直接到该hashCode值对应的位置取出该元素,这就是HashSet速度很快的原因。

特点:
1 不保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化;
2 HashSet不是同步的,如果多个线程同时访问HashSet,则必须通过代码来保证同步;
3 集合元素值允许为null;

注意:

当向HashSet中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据该hashCode值来决定该对象在HashSet中的存储位置。如果有两个元素通过equals()方法比较返回true,但它们的hashCode不相等,HashSet会将它们存储在不同的位置,依然可以添加成功。也就是说,HashSet判断两个元素相等的标准是两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法返回的值也相等。

示例:

package com.collection;import java.util.HashSet;public class HashSetTest {    public static void main(String[] args) {        HashSet set = new HashSet();        set.add(new A());        set.add(new A());        set.add(new B());        set.add(new B());        set.add(new C());        set.add(new C());        System.out.println(set);        //[com.collection.A@1, com.collection.A@1,         //com.collection.C@2,//当hashCode 与equals()都满足条件时,HashSet是不允许有重复数据出现的        //com.collection.B@15db9742, com.collection.B@6d06d69c]    }}class A {    public int hashCode() {        return 1;    }}class B {    public boolean  equals(Object o) {        return true;    }}class C {    public boolean  equals(Object o) {        return true;    }    public int hashCode() {        return 2;    }}

HashSet注意事项

当向HashSet中添加可变对象时,必须十分小心,如果修改HashSet集合中的对象,有可能导致该对象与集合中的其他对象相等,从而导致HashSet无法准确访问该对象。

示例:

package com.collection;import java.util.HashSet;import java.util.Iterator;public class ChangeHashSetTest {    public static void main(String[] args) {        HashSet set = new HashSet();        set.add(new R(5));        set.add(new R(9));        set.add(new R(-3));        set.add(new R(-2));        System.out.println("----原");        System.out.println(set);        System.out.println("set.contains(new R(-2)) = "+set.contains(new R(-2)));        System.out.println("set.contains(new R(-3)) = "+set.contains(new R(-3)));        Iterator it = set.iterator();        R r = (R) it.next();        r.count = -3;        System.out.println("----修改对象");        System.out.println(set);        set.remove(new R(-3));        System.out.println("----删除对象 -3 ");        System.out.println(set);        System.out.println("set.contains(new R(-2)) = "+set.contains(new R(-2)));        System.out.println("set.contains(new R(-3)) = "+set.contains(new R(-3)));输出内容://----原//[R[count-2], R[count-3], R[count5], R[count9]]//set.contains(new R(-2)) = true//set.contains(new R(-3)) = true//----修改对象//[R[count-3], R[count-3], R[count5], R[count9]]//----删除对象 -3 //[R[count-3], R[count5], R[count9]]//set.contains(new R(-2)) = false//set.contains(new R(-3)) = false    }}class R {    int count;    public R(int count) {        this.count = count;    }    public int hashCode() {        return this.count;    }    public String toString() {        return "R[count" + count + "]";    }    public boolean equals(Object obj) {        if (this == obj) {            return true;        }        if (obj != null && obj.getClass() == R.class) {            R r = (R) obj;            return this.count == r.count;        }        return false;    }}

问题:

R类重写了hsshCode()与equals()方法,这两个方法都是根据R对象的count示例变量来判断的。当Set集合中第一个对象的count实例变量被改变时,这将导致该R对象与集合中其他对象相同。

HashSet集合中的第一个元素和第二个元素完全相同,这表明两个元素已经重复。此时HashSet会比较混乱:当试图删除count 为-3的R 对象时,HashSet会计算出该对象hashCode值,从而找到该对象在集合中的保存位置,然后把此处的对象与count为-3的R 对象通过equals()方法进行比较,如果相等则删除该对象–HashSet只有第二个元素才满足该条件(第一个元素实际上保存在count 为-2的R 对象相应的位置),所以第二个元素被删除。至于第一个count为 -3的R对象,是保存在count 为 -2的R 对象对应的位置,但使用equals()方法拿它和count为 -2 的R对象比较时又返回false,这将导致HashSet不可能准确的访问该元素

LinkedHashSet

HashSet的子类,LinkedHashSet也是根据元素的hashCode值来决定元素的存储位置,但它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的。

当遍历LinkedHashSet集合时,LinkedHashSet将会按元素的添加顺序来访问集合里的元素。
缺点:
LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet的性能;
优点:
但在迭代访问Set里的全部元素时将会有很好性能,因为它以链表来维护内部顺序。

示例:

package com.collection;import java.util.LinkedHashSet;public class LinkedHashSetTest {    public static void main(String[] args) {        LinkedHashSet set = new LinkedHashSet();        set.add("Java课堂");        set.add("Java doc");        set.add("Java doc");        set.add("Java 精简版");        System.out.println(set);        //输出 [Java课堂, Java doc, Java 精简版]    }}

注意:
LinkedHashSet依然是HashSet,因此,它不允许有重复的元素。

LinkedHashSet, 对于普通的插入,删除操作,LinkedHashSet比HashSet要略微慢一点,这是由维护链表所带来的额外开销造成的,但由于有了链表,遍历LinkedHashSet会更快。

Things won are done; joy’s soul lies in the doing.
得到即是完结,快乐的精髓在于过程

0 0
原创粉丝点击