资讯专栏INFORMATION COLUMN

java集合-Set

xavier / 2495人阅读

摘要:集合判断两个元素的标准是两个对象通过方法比较相等,并且两个对象的方法返回值也相等。的集合元素也是有序的,以枚举值在类内的定义顺序来决定集合元素的顺序。是所有实现类中性能最好的,但它只能保存同一个枚举类的枚举值作为集合元素。

Set集合通常不能记住元素的添加顺序。Set不允许包含重复的元素。

Set集合不允许包含相同的元素,如果试图把两个相同的元素加入同一个Set集合中,则添加操作失败,add()方法返回false,且新元素不会被加入。

HashSet类

HashSet按照Hash算法来存储集合中的元素,因此具有很好的存取和查询性能。

HashSet特点

不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化。

HashSet不是同步的,如果多个线程同时访问一个HashSet,假设有两个或者两个以上线程同时修改了HashSet集合时,则必须通过代码来保证其同步。

集合元素值可以是null;

当HashSet集合中存入一个元素时,HashSet会调用该对象的HashCode()方法来得到该对象的hashCode值,然后根据该hashCode值决定该对象在HashSet中的存储位置,如果有两个元素通过equals()方法比较返回true,但他们的hashCode()方法返回值不相等,HashSet将会把他们存在不同位置,依然可以添加成功。

HashSet集合判断两个元素的标准是两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法返回值也相等。

即使两个A对象通过equals()方法比较返回true,但HashSet依然把他们当成两个对象,即使两个B对象的hashCode()方法返回相同值,但HashSet依然把他们当成两个对象。

当把一个对象放入HashSet中时,如果需要重写该对象对应类的equals()方法,则也应该重写其hashCode()方法,规则是:如果两个对象通过equals()方法比较返回true,这两个对象的hashCode值也应该相同。

如果两个对象通过equals()比较返回true,但这两个对象的hashCode()方法返回不同的hashCode值时,这将导致HashSet会把这两个对象保存在Hash表的不同位置,从而使两个对象都可以添加成功,这就与Set集合规则冲突了。

如果两个对象的hashCode()值返回的值相同,但他们通过equals()方法比较返回false时更麻烦。因为两个对象的hashCode值相同,HashSet将试图把他们保存在同一个位置,但又不行(否则将只剩下一个对象),所以实际上回在这个位置用链式结构来保存多个对象;而HashSet访问集合元素时也是根据元素的hashCode值来快速定位的,如果HashSet中两个以上的元素具有相同的hashCode值,将会导致性能下降。

Hash算法可以直接根据该元素的hashCode值计算出该元素的存储位置,从而快速定位该元素。

数组是所有能存储一组元素里最快的数据结构。

当从HashSet中访问元素时,HashSet先计算该元素的hashCode值,也就是调用该元素的hashCode()方法的返回值,然后直接到该hashCode值对应的位置去取出该元素。这就是HashSet速度很快的原因。

HashSet中每个能存储元素的“槽位slot”通常称为桶bucket,如果有多个元素的hashCode值相同,但他们通过equals方法比较返回false,就需要在一个桶里存放多个元素,就会导致性能下降。

重写hashCode方法的基本原则。

在程序运行过程中,同一个对象多次调用hashCode()方法应该返回相同的值。

当两个对象通过equals()方法比较返回true时,hashCode()应该也返回相同值。

对象中用作equals()方法比较标准的实例变量,都应该用于计算hashCode值。

当程序把可变对象添加到HashSet中之后,尽量不要去修改该集合元素中参与计算hashCode()’equals()的实例变量,否则将会导致HashSet无法争取操作这些集合。

LinkedHashSet类

LinkedHashSet集合也是根据元素的hashCode值来决定元素的存储位置,但它同时使用链表维护元素的次序,LinkedHashSet将会按元素的添加顺序来访问集合里的元素。

同样不能允许集合元素重复,

public class LinkedHashSetTest {
    public static void main(String[] args) {
        Collection collection = new LinkedHashSet<>();
        collection.add("java");
        collection.add("python");
        //[java, python]
        System.out.println(collection);
        collection.remove("java");
        collection.add("java");
        //[python, java]
        System.out.println(collection);
    }
}
TreeSet类

TreeSet是StortedSet接口的实现类。TreeSet可以保证集合元素处于排序状态。

public class TreeSetTest {
    public static void main(String[] args) {
        TreeSet treeSet = new TreeSet<>();
        treeSet.add("5");
        treeSet.add("4");
        treeSet.add("3");
        treeSet.add("2");
        treeSet.add("1");
        System.out.println(treeSet);
        System.out.println(treeSet.first());//1
        System.out.println(treeSet.last());//5
        //返回集合中位于指定元素之前的元素
        System.out.println(treeSet.lower("4"));//3
        //返回集合中位于指定元素之后的元素
        System.out.println(treeSet.higher("4"));//5
        //返回此set的子集,由小于指定元素的元素组成
        SortedSet headSet = treeSet.headSet("4");
        System.out.println(headSet);//[1, 2, 3]
        //返回set的子集。由大于或者等于指定元素的元素组成
        SortedSet tailSet = treeSet.tailSet("4");
        System.out.println(tailSet);//[4, 5]
    }
}

Tree并不是根据元素的插入顺序进行排序的,而是根据元素实际的大小来进行排序的。

与HashSet集合采用hash算法来决定元素的存储位置不同,TreeSet是采用红黑树的数据结构来存储集合元素,TreeSet支持两种排序方法,自然排序和定制排序。默认下TreeSet采用自然排序。

自然排序

TreeSet会调用集合元素的compareTo(Object o)方法来比较元素之间的大小关系,然后将集合元素按升序排序,这种方式就是自然排序。

如果试图把一个对象添加到TreeSet时,则该对象的类必须实现comparable接口。否则报错

public class TreeSetErrorTest {
    public static void main(String[] args) {
        USER user = new USER("", 2);
        TreeSet treeSet = new TreeSet<>();
        //Exception in thread "main" java.lang.ClassCastException: setTest.USER cannot be cast to java.base/java.lang.Comparable
        treeSet.add(user);
    }
}

当试图把一个对象添加到TreeSet集合时,TreeSet会调用该对象的comparaTo(Object o)方法与集合中的其他元素进行比较,这就要求集合中的其他元素与该元素时同一类的实例,也就是说,向TreeSet中添加的应该是同一个类的对象,否则也会引发ClassCastException异常。

public class TreeSetErrorTest2 {
    public static void main(String[] args) {
        TreeSet set = new TreeSet<>();
        set.add(new String());
        //Exception in thread "main" java.lang.ClassCastException: java.base/java.lang.String cannot be cast to java.base/java.util.Date
        set.add(new Date());
        /*
         * 在添加String时,是没有错误的,当添加Date对象时,TreeSet就会调用该对象的comparaTo方法与集合中的其他元素进行比较--
         * Date对象的comparaTo方法无法与字符串对象比较大小,所以引发异常
         * */
    }
}

TreeSet只能添加同一种类型的对象,

当把一个对象加入TreeSet集合中时,TreeSet调用该对象的compareTo(Obejct o)方法与容器中的其他对象比较大小,然后根据红黑树结构找到他的存储位置,如果两个对象通过compareTo(Object o)方法比较相等,新对象将无法添加到TreeSet集合中。

TreeSet集合判断两个对象是够相等的唯一标准是:如果通过compareTo方法比较返回0,TreeSet则会认为他们相等,否则就认为他们不相等。

当需要把一个对象放入TreeSet中,重写该对象对应类的equals方法时,应保证该方法与compareTo方法有一致的结果,其规则是,如果两个对象通过equals方法比较返回true时,这两个对象通过compareTo方法比较应返回0;

TreeSet可以删除没有被修改实例变量,且不与其他被修改实例变量的对象重复的对象。P309

推荐不要修改放入HashSet和TreeSet集合中元素的关键实例变量。

定制排序
public class MSort {
    public static void main(String[] args) {
        TreeSet treeSet = new TreeSet<>((o1,o2)->{
            M m1 = o1;
            M m2 = o2;
            return Integer.compare(m1.a, m2.a);
        });
        treeSet.add(new M(12));
        treeSet.add(new M(1232));
        treeSet.add(new M(121));
        //[12, 121, 1232]
        System.out.println(treeSet);
    }
}

当通过Comparator对象或Lambda表达式来实现TreeSet的定制排序时,依然不可以向TreeSet中添加类型不同的对象,否则会引发ClassCastException异常,使用定制排序时,TreeSet对集合元素排序不管集合元素本身的大小,而是由Comparator对象或Lambda表达式。负责集合元素的排序规则,TreeSet判断两个集合元素相等的标准是:通过Comparator比较两个元素返回了0,这样TreeSet不会把第二个元素添加到集合中。

EnumSet类

EnumSet是一个转为枚举类设计的集合类,EnumSet中的所有元素都必须是指定枚举类型的枚举值,该枚举值在创建EnumSet时显示或隐式地指定。EnumSet的集合元素也是有序的,EnumSet以枚举值在Enum类内的定义顺序来决定集合元素的顺序。

EnumSet在内部以位向量的形式存储,这种存储形式非常紧凑高效,因此EnumSet对象占用内存很小,运行效率好,尤其是在进行批量操作的时候。

EnumSet集合不允许加入NULL;

public class SeasonEnumSetTest {
    public static void main(String[] args) {
        //创建一个EnumSet集合,集合元素是Season的全部枚举
        EnumSet enumSet = EnumSet.allOf(Season.class);
        System.out.println(enumSet);//[SPTING, SUMMER, FALL, WINTER]
        //创建一个空集合,指定其集合元素是Season类的枚举类
        EnumSet noneOf = EnumSet.noneOf(Season.class);
        System.out.println(noneOf);//[]
        noneOf.add(Season.FALL);
        noneOf.add(Season.WINTER);
        System.out.println(noneOf);//[FALL, WINTER]
        //利用现有枚举进行创建EnumSet集合
        EnumSet of = EnumSet.of(Season.SPTING,Season.SUMMER);
        System.out.println(of);//[SPTING, SUMMER]
        //创建几个从begin到end之间的枚举作为新集合的元素
        EnumSet range = EnumSet.range(Season.SUMMER, Season.WINTER);
        System.out.println(range);//[SUMMER, FALL, WINTER]
        //range与complementof枚举值和是Season的全部枚举
        EnumSet complementOf = EnumSet.complementOf(range);
        System.out.println(complementOf);//[SPTING]
    }
}

当试图复制一个Collection集合里的元素来创建EnumSet集合时,必须保证Collection集合里的所有元素都是同一个枚举类的枚举值。

Set实现类的性能分析

HashSet的性能总是比TreeSet好,特别是最常用的添加,查询元素等操作,因为TreeSet需要额外的红黑树算法来维护集合元素的次序,只有当需要一个保持排序的Set时,才应该使用TreeSet,否则都应该使用HashSet。

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

EnumSet是所有Set实现类中性能最好的,但它只能保存同一个枚举类的枚举值作为集合元素。

Set的三个实现类HashSet,TreeSet和EnumSet都是线程不安全的。

如果有多个线程同时访问Set集合那么需要手动保证该Set集合的同步性。

Collections.synchronizedSortedSet(new TreeSet(...));

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69316.html

相关文章

  • java集合

    摘要:集合类简介集合类包含在包下集合类存放的是对象的引用,而非对象本身。集合类型主要分为集,列表,映射。返回此有序集合中当前第一个最小的元素。集合中元素被访问的顺序取决于集合的类型。 Java集合类 1.简介: java集合类包含在java.util包下集合类存放的是对象的引用,而非对象本身。集合类型主要分为Set(集),List(列表),Map(映射)。 1.1 java集合类图 sho...

    Pluser 评论0 收藏0
  • Java™ 教程(Set接口)

    Set接口 Set是一个不能包含重复元素的Collection,它模拟了数学集抽象,Set接口仅包含从Collection继承的方法,并添加禁止重复元素的限制,Set还为equals和hashCode操作的行为添加了一个更强的契约,允许Set实例有意义地进行比较,即使它们的实现类型不同,如果两个Set实例包含相同的元素,则它们是相等的。 Java平台包含三个通用的Set实现:HashSet、Tre...

    Apollo 评论0 收藏0
  • Java™ 教程(集合接口)

    集合接口 核心集合接口封装了不同类型的集合,如下图所示,这些接口允许独立于其表示的细节来操纵集合,核心集合接口是Java集合框架的基础,如下图所示,核心集合接口形成层次结构。 showImg(https://segmentfault.com/img/bVbntJW?w=402&h=146); Set是一种特殊的Collection,SortedSet是一种特殊的Set,依此类推,另请注意,层次结构...

    elisa.yang 评论0 收藏0
  • 带你入门 JavaScript ES6 (五) 集合

    摘要:一概述集合是引入的新的内置对象类型,其特点同数学意义的集合,即集合内所有元素不重复元素唯一。数组集合对比数组和集合,数组可以加入重复数据,而集合的所有元素是唯一的不允许重复。因此,适合临时存放一组对象,以及存放跟对象绑定的信息。 本文同步带你入门 带你入门 JavaScript ES6 (五) 集合,转载请注明出处。 前面我们学习了: for of 变量和扩展语法 块作用域变量和解构...

    BetaRabbit 评论0 收藏0
  • Java集合框架——Map接口

    摘要:第三阶段常见对象的学习集合框架集合在实际需求中,我们常常会遇到这样的问题,在诸多的数据中,通过其编号来寻找某一些信息,从而进行查看或者修改,例如通过学号查询学生信息。面试题和的区别是单列集合的顶层接口,有子接口和。 第三阶段 JAVA常见对象的学习 集合框架——Map集合 showImg(https://segmentfault.com/img/remote/1460000019683...

    princekin 评论0 收藏0

发表评论

0条评论

xavier

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<