资讯专栏INFORMATION COLUMN

Java 集合 Set

verano / 801人阅读

摘要:当复制集合中的所有元素来创建新的集合时,要求集合中的所有元素必须是同一个枚举类的枚举值各实现类的性能分析的性能总比好,特别是最常用的添加查询元素等操作。因为需要额外的红黑树算法来维护集合元素的次序。在创建时进行,以防对集合的意外非同步访问

HashSet

大多时候使用Set集合时就是使用HashSet实现类。HashSet按Hash算法来存储集合中的元素,因此具有很好的存取和查找性能

HashSet具有以下特点

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

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

集合元素值可以是null

HashSet会调用该对象的hashCode()方法来得到该对象的hashCode()值,然后根据该hashCode()值决定该对象在HashSet中的存储位置;HashSet集合判断两个元素相等的标准是两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法返回值也相等

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

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

HashSet中每个能存储元素的“槽位”(slot)通常称为“桶”(bucket)

hashCode()方法的基本重写规则

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

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

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

hashCode()方法的基本重写步骤

把对象内每个有意义的实例变量(即每个参与equals()方法比较标准的实例变量)计算出一个Int类型的hashCode值

用第一步计算出来的多个hashCode值组合计算出一个hashCode值返回

LinkedHashSet类

LinkedHashSet集合根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的。当遍历LinkedHashSet集合里的元素时,LinkedHashSet将会按元素的添加顺序来访问集合里的元素

LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet的性能,但在迭代访问Set里的全部元素时将有很好的性能,因为它以链表来维护内部顺序

虽然LinkedHashSet使用了链表记录集合元素的添加顺序,但LinkedHashSet依然是HashSet,因此不允许集合元素重复

TreeSet类

TreeSet是SortedSet接口的实现类,可以确保集合元素处于排序状态。根据元素实际值的大小进行排序

TreeSet的额外方法

Comparator comparator():如果TreeSet采用了定制排序,则该方法返回定制排序所使用的Comparator;如果TreeSet采用了自然排序,则返回null

Object first():返回集合中的第一个元素

Object last():返回集合中的最后一个元素

Object lower(Object e):返回集合中位于指定元素之前的元素(即小于指定元素的最大元素,参考元素不需要是TreeSet集合里的元素)

Object higher(Object e):返回集合中位于指定元素之后的元素(即大于指定元素的最小元素,参考元素不需要是TreeSet集合里的元素)

SortedSet subSet(Object fromElement, Object toElement):返回此Set的子集,范围从fromElement(包括)到toElement(不包括)

SortedSet headSet(Object toElement):返回此Set的子集,由小于toElement的元素组成

SortedSet tailSet(Object fromElement):返回此Set的子集,由大于或等于fromElement的元素组成

HashSet采用hash算法来决定元素的存储位置,TreeSet采用红黑树的数据结构来存储集合元素。

TreeSet支持两种排序方法。在默认情况下,TreeSet采用自然排序

import java.util.*;

public class TreeSetTest
{
    public static void main(String[] args)
    {
        TreeSet nums = new TreeSet();
        // 向TreeSet中添加四个Integer对象
        nums.add(5);
        nums.add(2);
        nums.add(10);
        nums.add(-9);
        // 输出集合元素,看到集合元素已经处于排序状态
        System.out.println(nums);
        // 输出集合里的第一个元素
        System.out.println(nums.first()); // 输出-9
        // 输出集合里的最后一个元素
        System.out.println(nums.last());  // 输出10
        // 返回小于4的子集,不包含4
        System.out.println(nums.headSet(4)); // 输出[-9, 2]
        // 返回大于5的子集,如果Set中包含5,子集中还包含5
        System.out.println(nums.tailSet(5)); // 输出 [5, 10]
        // 返回大于等于-3,小于4的子集。
        System.out.println(nums.subSet(-3 , 4)); // 输出[2]
    }
}
自然排序

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

compareTo(Object obj)方法返回一个整数值,实现该接口的类必须实现该方法,实现了该接口的类的对象就可以比较大小。当一个对象调用该方法与另一个对象进行比较时,例如obj1.compareTo(obj2),如果该方法返回0,则表明这两个对象相等;如果该方法返回一个正整数,则表明obj1大于obj2;如果该方法返回一个负整数,则表明obj1小于obj2

实现了Comparable接口的常用类

BigDecimal、BigInteger以及所有的数组型对应的包装类:按它们对应的数组大小进行比较

Character:按字符的UNICODE值进行比较

Boolean:true对应的包装类实例大于false对应的包装类实例

String:按字符串中字符的UNICODE值进行比较

Date、Time:后面的时间、日期比前面的时间、日期大

一个对象添加到TreeSet时,则该对象的类必须实现Comparable接口,否则程序将会抛出异常

import java.util.TreeSet;

class Error{ }
public class TreeSetErrorTest 
{
    public static void main(String[] args) 
    {
        TreeSet treeSet = new TreeSet<>();
        treeSet.add(new Error());
        treeSet.add(new Error());        //①
    }
}

添加第一个对象时,TreeSet里没有任何元素,所以不会出现任何问题;当添加第二个Error对象时,TreeSet就会调用该对象的compareTo(Object obj)方法与集合中的其他元素进行比较——如果其对应的类没有实现Comparable接口,则会引发ClassCastException异常

向TreeSet集合中添加元素时,只有第一个元素无须实现Comparable接口,后面添加的所有元素都必须实现Comparable接口
把一个对象添加到TreeSet集合时,TreeSet会调用该对象的compareTo(Object obj)方法与集合中的其他元素进行比较。向TreeSet中添加的应该是同一个类的对象,否则也会引发ClassCastException异常

如果希望TreeSet能正常运行,TreeSet只能添加同一种类型的对象

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

定制排序

TreeSet的自然排序是根据集合元素的大小,TreeSet将它们以升序排列。如果需要实现定制排序,例如以降序排列,则可以通过Comparator接口的帮助。

class M
{
    int age;
    public M(int age)
    {
        this.age = age;
    }
    public String toString()
    {
        return "M[age:" + age + "]";
    }
}
public class TreeSetTest4
{
    public static void main(String[] args)
    {
        // 此处Lambda表达式的目标类型是Comparator
        TreeSet ts = new TreeSet((o1 , o2) ->
        {
            M m1 = (M)o1;
            M m2 = (M)o2;
            // 根据M对象的age属性来决定大小,age越大,M对象反而越小
            return m1.age > m2.age ? -1
                : m1.age < m2.age ? 1 : 0;
        });
        ts.add(new M(5));
        ts.add(new M(-3));
        ts.add(new M(9));
        System.out.println(ts);
    }
}

EnumSet类

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

EnumSet在内部以位向量的形式存储,EnumSet对象占用内存很小,运行效率很好。尤其是进行批量操作(如调用containsAll()和retainAll()方法)时,如果其参数也是EnumSet集合,则该批量操作的执行速度也非常快

EnumSet集合不允许加入null元素,否则抛出NullPointException异常

EnumSet没有暴露任何构造器来创建该类的实例,应通过其提供的类方法来创建EnumSet对象

EnumSet allOf(Class elementType): 创建一个包含指定枚举类里所有枚举值的EnumSet集合

EnumSet complementOf(EnumSet e): 创建一个其元素类型与指定EnumSet里元素类型相同的EnumSet集合,新EnumSet集合包含原EnumSet集合所不包含的、此类枚举类剩下的枚举值(即新EnumSet集合和原EnumSet集合的集合元素加起来是该枚举类的所有枚举值)

EnumSet copyOf(Collection c): 使用一个普通集合来创建EnumSet集合

EnumSet copyOf(EnumSet e): 创建一个指定EnumSet具有相同元素类型、相同集合元素的EnumSet集合

EnumSet noneOf(Class elementType): 创建一个元素类型为指定枚举类型的空EnumSet

EnumSet of(E first,E…rest): 创建一个包含一个或多个枚举值的EnumSet集合,传入的多个枚举值必须属于同一个枚举类

EnumSet range(E from,E to): 创建一个包含从from枚举值到to枚举值范围内所有枚举值的EnumSet集合

enum Season
{
    SPRING,SUMMER,FALL,WINTER
}
public class EnumSetTest
{
    public static void main(String[] args)
    {
        // 创建一个EnumSet集合,集合元素就是Season枚举类的全部枚举值
        EnumSet es1 = EnumSet.allOf(Season.class);
        System.out.println(es1); // 输出[SPRING,SUMMER,FALL,WINTER]
        // 创建一个EnumSet空集合,指定其集合元素是Season类的枚举值。
        EnumSet es2 = EnumSet.noneOf(Season.class);
        System.out.println(es2); // 输出[]
        // 手动添加两个元素
        es2.add(Season.WINTER);
        es2.add(Season.SPRING);
        System.out.println(es2); // 输出[SPRING,WINTER]
        // 以指定枚举值创建EnumSet集合
        EnumSet es3 = EnumSet.of(Season.SUMMER , Season.WINTER);
        System.out.println(es3); // 输出[SUMMER,WINTER]
        EnumSet es4 = EnumSet.range(Season.SUMMER , Season.WINTER);
        System.out.println(es4); // 输出[SUMMER,FALL,WINTER]
        // 新创建的EnumSet集合的元素和es4集合的元素有相同类型,
        // es5的集合元素 + es4集合元素 = Season枚举类的全部枚举值
        EnumSet es5 = EnumSet.complementOf(es4);
        System.out.println(es5); // 输出[SPRING]
    }
}

EnumSet可以复制另一个EnumSet集合中的所有元素来创建新的EnumSet集合,或者复制另一个Collection集合中的所有元素来创建新的EnumSet集合。当复制Collection集合中的所有元素来创建新的EnumSet集合时,要求Collection集合中的所有元素必须是同一个枚举类的枚举值

各Set实现类的性能分析

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

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

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

HashSet、TreeSet、EnumSet都是线程不安全的,如果有多个线程同时访问一个Set集合,并且有超过一个线程修改了该Set集合,则必须手动保证该Set集合的同步性。通常可以通过Collections工具类的synchronizedSortedSet方法来“包装”该Set集合。在创建时进行,以防对Set集合的意外非同步访问

SortedSet s = Collections.synchronizedSortedSet(new TreeSet(...));

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

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

相关文章

  • java集合

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

    Pluser 评论0 收藏0
  • java集合-Set

    摘要:集合判断两个元素的标准是两个对象通过方法比较相等,并且两个对象的方法返回值也相等。的集合元素也是有序的,以枚举值在类内的定义顺序来决定集合元素的顺序。是所有实现类中性能最好的,但它只能保存同一个枚举类的枚举值作为集合元素。 Set集合通常不能记住元素的添加顺序。Set不允许包含重复的元素。 Set集合不允许包含相同的元素,如果试图把两个相同的元素加入同一个Set集合中,则添加操作...

    xavier 评论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条评论

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