Java Set集合

Set集合

Set接口是 Collection 的子接口。
Set集合不允许包含相同的元素。
如果添加相同的元素, add()返回返回FALSE, 新元素不会加入。

public interface Set<E> extends Collection<E> {
    // Query Operations
}

HashSet, TreeSet, EnumSet 是 Set 集合的三个实现类。

HashSet类

HashSet按照Hash算法存储集合中的元素。
HashSet有以下特点:

  • 不能保证元素的排列顺序
  • HashSet不是同步的,多个线程访问时,如果有多线程同时修改,需要代码来保证同步。
  • 集合元素值可以是null
    向HashSet 添加元素时,会调用该对象的 hashCode()得到该对象的hashCode值,根据hashCode值决定 HashSet的存储位置。
    判断两个元素相等的标准是 通过 equals()方法比较相等,并且hashCode()返回的值也相等。
class A{
    @Override
    public boolean equals(Object obj) {
        return true;
    }
}

class B{
    @Override
    public int hashCode() {
        return 1;
    }
}

class C{
    @Override
    public int hashCode() {
        return 2;
    }

    @Override
    public boolean equals(Object obj) {
        return true;
    }
}
public class HashSetTest {
    public static void main(String[] args){
        HashSet books = new HashSet();
        books.add(new A());
        books.add(new A());
        books.add(new B());
        books.add(new B());
        books.add(new C());
        books.add(new C());

        System.out.println(books);
    }
}
//Output
[testCode.B@1, testCode.B@1, testCode.C@2, testCode.A@7440e464, testCode.A@49476842]

上面例子发现,C只添加了一个, A, B都加入了2个对象。
如果两个对象 hashCode相同, equals为FALSE, HashSet会试图存储在一个位置,这个位置会用链式结构来保存多个对象。
重写equals()和hashCode()时,应尽量保证equals()为TRUE时,hashCode()也相等。

LinkedHashSet类

LinkedHashSet是 HashSet的子类

public class LinkedHashSet<E>
    extends HashSet<E>
    implements Set<E>, Cloneable, java.io.Serializable {
}

LinkedHashSet根据元素的hashCode值决定元素的存储位置,同时使用链表维护元素的次序。使元素看起来是按照插入顺序保存的。
由于要维护插入顺序,性能略低于HashSet性能,但迭代访问时有很好的性能。

        LinkedHashSet books1 = new LinkedHashSet();
        books1.add("A");
        books1.add("B");
        books1.add("C");
        System.out.println(books1);
        books1.remove("A");
        books1.add("A");
        System.out.println(books1);
//Output
[A, B, C]
[B, C, A]

元素顺序与添加时一致。

TreeSet

SortedSet 是 Set的子接口。
TreeSet是 SortedSet接口的实现类,保证集合元素处于排序状态。

public class TreeSet<E> extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable
}

public interface NavigableSet<E> extends SortedSet<E> {
}

public interface SortedSet<E> extends Set<E> {
}

TreeSet增加了以下方法:
comparator(): 返回定制排序的Comparator;
first(): 返回第一个元素
last(): 最后一个元素
lower(): 指定元素之前的元素
higher(): 指定元素之后的元素
subSet():返回子集
headSet(): 小于指定元素的子集
tailSet():大于等于指定元素的子集

TreeSet采用红黑树的数据结构来存储集合元素。

        TreeSet nums = new TreeSet();
        nums.add(2);
        nums.add(-5);
        nums.add(7);
        System.out.println(nums);
        System.out.println(nums.last());
        System.out.println(nums.tailSet(2));
 //Output
[-5, 2, 7]
7
[2, 7]

TreeSet支持两种排序规则: 自然排序和定制排序。

自然排序

TreeSet会调用集合元素的 compareTo()方法比较元素的大小关系,然后按照升序排列, 这种为自然排序。
Java提供 Comparable 接口, 定义了 compareTo()方法,实现该接口的类必须实现该方法。
String, 包装类,Boolean, Date, Time 等都实现了Comparable接口。

当向TreeSet添加元素时,第一个元素无须实现Comparable接口, 后面添加必须实现 Comparable 接口

class D{}
public class HashSetTest {
    public static void main(String[] args){
        TreeSet ts = new TreeSet();
        ts.add(new D());
        ts.add(new D());
    }
}

java.lang.ClassCastException: testCode.D cannot be cast to java.lang.Comparable.
上面代码会抛出这个异常。
TreeSet应该添加同一种类型的对象。
否则也会报 ClassCastException。
因为大部分类实现的 compareTo()方法时,比较对象会强制转换为相同的类型。

当向TreeSet中添加对象时, TreeSet调用 compareTo()与容器中其他对象比较大小,根据红黑树找到它的存储位置,如果compareTo()比较相等,则不能添加到集合中。
重写类的equals()方法时,应该保证和compareTO()有一致的结果。

定制排序

定制排序需要创建TreeSet时,提供一个 Comparator对象, 实现排序逻辑。
Comparator 是函数式接口,可使用Lambda表达式。

class E{
    int age;
    public E(int age){
        this.age = age;
    }

    @Override
    public String toString() {
        return "E [age]:" + age;
    }
}

public class HashSetTest {
    public static void main(String[] args){
        TreeSet treeSet1 = new TreeSet(((o1, o2) -> {
            E e1 = (E) o1;
            E e2 = (E) o2;

            return e1.age > e2.age ? -1 : e1.age < e2.age ? 1: 0;
        }));
        treeSet1.add(new E(5));
        treeSet1.add(new E(-2));
        treeSet1.add(new E(8));
        System.out.println(treeSet1);
    }
}
//Output
[E [age]:8, E [age]:5, E [age]:-2]

EnumSet

EnumSet是专门给枚举类设计的集合类。
所有元素必须是枚举类型的枚举值

public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
    implements Cloneable, java.io.Serializable
{
}

public abstract class AbstractSet<E> extends AbstractCollection<E> implements Set<E> {
}

EnumSet内部以位向量的方式存储。
不允许加入 null元素。
EnumSet 没有暴露任何构造器来创建类实例。
可以使用下面方法创建EnumSet对象。

  • allOf(): 包含全部枚举值的集合
  • complementOf(): 不包含指定集合元素,剩余部分新建一个集合
  • copyOf(): 使用普通集合创建
  • of():包含一个或多个枚举值的集合
  • range(): 根据范围创建集合。
enum Season{
    SPRING,SUMMER,FALL,WINTER;    
}
public class HashSetTest {
    public static void main(String[] args){
        EnumSet es1 = EnumSet.allOf(Season.class);
        System.out.println(es1);
        
        EnumSet es2 = EnumSet.noneOf(Season.class);
        System.out.println(es2);
        es2.add(Season.FALL);
        es2.add(Season.SPRING);
        System.out.println(es2);
    }
}
//Output
[SPRING, SUMMER, FALL, WINTER]
[]
[SPRING, FALL]

创建时要求同一个集合元素是同一个枚举类的枚举值。

各个Set性能分析

HashSet 的性能好于 TreeSet, 因为 TreeSet使用红黑树维护顺序。
LinkedHashSet比 HashSet略慢一点, 因为维护链表带来了额外开销。但是遍历会更快。
EnumSet是所有Set实现类中性能最好的。

HashSet,TreeSet,EnumSet都是线程不安全的,
多线程手动同步,可以使用 Collections 工具类包装。

SortedSet sortedSet = Collections.synchronizedSortedSet(new TreeSet<>());

地址:https://blog.csdn.net/yonggang7/article/details/86748790

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值