Java数据结构使用总结(2)-Set接口

本文详细介绍了Java中的Set集合,包括HashSet、LinkedHashSet和TreeSet的特点及使用场景。探讨了HashSet的存储原理,LinkedHashSet的有序性,以及TreeSet的排序机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Set集合

Set集合和Collection集合基本相同,行为略有不同,即无序,不允许重复。

HashSet类

大多数使用Set的时候,我们基本都使用HashSet类。HashSet类使用hash算法来角色元素在集合中的存储位置,因此具有很好的存取和查找速度。特点如下:

  • 元素无序,即你添加的顺序可能和你遍历出来的顺序不同。
  • 线程不安全,多个线程同时修改HashSet中的元素时,需要同步保证安全
  • 集合元素可以是null

HashSet如何存储元素?

当向HashSet中添加元素时,HashSet先调用该元素的hashCode()方法,获取该对象的hashCode值,然后根据hashCode值决定该元素的存储位置;

当两个元素的hashCode值相同时,就会调用元素的equals()方法,比较该元素是否相等,如果相等,则后添加的元素添加失败,如果不相等,则会在该存储位置形成链式结构(链表)来存储,此时会导致性能下降

综上,HashSet是通过hashCode()和equals()判断元素是否相同的;为了性能考虑,当重新hashCode()和equals()方法时,尽量保证,当两个对象的hashCode值相同时,equals()比较返回true

具体实例如下:

public class CollectionTest {

    class A{
        @Override
        public int hashCode(){
            return 1;
        }
    }
    class B{
        @Override
        public boolean equals(Object obj){
            return true;
        }
    }
    class C{
        @Override
        public int hashCode(){
            return 2;
        }
        @Override
        public boolean equals(Object obj){
            return true;
        }
    }
    public static void main(String[] args) {
        CollectionTest c = new CollectionTest();
        c.test();

    }
    public void test(){
        HashSet<Object> hash = new HashSet<>();
        hash.add(new A());
        hash.add(new A());
        hash.add(new B());
        hash.add(new B());
        hash.add(new C());
        hash.add(new C());
        System.out.println(hash);
    }
}

输出结果如下:

[CollectionTest$A@1, CollectionTest$A@1, CollectionTest$C@2, CollectionTest$B@677327b6, CollectionTest$B@1540e19d]
可发现A类和B类对象可以添加两个,而C类对象只能添加一个;表明HashSet判断是否未同意对象的标准是hashCode()、equals()同时相等


LinkedHashSet类

LinkedHashSet类是HashSet类的子类,也是通过对象hashCode值决定对象存储位置,通过hashCode值和equals()判断对象是否相同。

和HashSet类的区别是:LinkedHashSet类的内部元素通过链表连接在一起,导致内部元素时有序的;元素的顺序和添加顺序相同

TreeSet类

TreeSet类是SortedSet的实现类,顾名思义TreeSet可以保证集合中元素的排序状态。TreeSet提供了独特的方法:

  • Object first():返回集合第一个元素
  • Object last():返回集合的最后一个元素
  • Object lower(Object obj):返回比obj小的最大元素
  • Object higher(Object obj):返回比obj大的最小的元素
  • SortedSet subSet(Object obj1,Object obj2):返回集合中在obj1和obj2之间的元素
  • SortSet headSet(Object obj):返回集中中所有小于obj的元素集合
  • SortSet tailSet(Object obj):返回集合中所有大于obj的元素集合

HashSet采用hashCode决定元素的存储位置,TreeSet采用红黑树来存储集合元素。

TreeSet类如何判断元素大小?

自然排序

TreeSet中元素实现Comparable集合,重写其中的int compareTo(Object obj),通过返回判断元素大小。

Java中常用类已实现该接口:

  • BigDecimal/BigInteger和所有数字类型的包装类(Integer等)
  • Character/Boolean/String/Date等

当向TreeSet中添加元素第二个元素时,会将该元素和第一个元素相比,决定元素的顺序。之后添加的元素和一次如此。

因此集合中元素必须实现Comparable接口,否则无法比较大小,导致出现抛出异常。TreeSet元素是否相等的标准是

int compareTo(Object obj)是否返回0

public class Main {

    class A implements Comparable{

        @Override
        public int compareTo(Object o) {
            return 1;
        }
    }
    public static void main(String[] args) {
        Main main = new Main();
        main.test();
    }

    public void test(){
        TreeSet<A> tree = new TreeSet<>();
        A a = new A();
        tree.add(a);
        tree.add(a);
        System.out.println(tree);
    }
}

输出结果如下:

[Main$A@1540e19d, Main$A@1540e19d]

可以发现将同一个对象同时添加到TreeSet中成功了。因此判断equals()和compareTo(Object obj)的判断标准应该相同,否则与Set集合的规则向冲突。

当修改TreeSet中的元素导致集合中两个元素相等时,会导致接下来的删除这两个相等的元素失败。但是,一旦删除集合中其他元素成功后,将导致重新索引,此时有可以删除成功了。

定制排序

TreeSet<B> treeSet = new TreeSet<>(new Comparator<B>() {
            @Override
            public int compare(B o1, B o2) {
                return 0;
            }
        });

只需创建TreeSet对象时,重写compare(object obj1,Object obj2)方法即可


性能比较

HashSet和TreeSet是两个具有代表性的Set集合,HashSet的性能比TreeSet优秀,因为TreeSet需要维护一个红黑树来保证集合元素的顺序。因此如果没有排序需求,则选择使用HashSet。

HashSet和LinkedHashSet相比,HashSet的性能要好,因为LinkedHashSet要维护链表。但也是因为链表,导致LinkedHashSet遍历集合元素的性能要优秀

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值