Java Set讲解

Java Set讲解

Set集合用于存放无序的,不重复的元素。

HashSet

HashSet类是Set接口的实现类,使用Hash值来决定集合内部存放的位置。
HashSet使用equals()对比元素,当equals()返回true,HashCode也相同,则不接受新元素,反之则允许其放入集合中。

//类A的equals方法总是返回true,但没有重写其hashCode()方法。
public class A {
    public boolean equals(Object obj) {
        return true;
    }
}
        HashSet books = new HashSet();
        books.add(new A());
        books.add(new A());
// 调用
while (aa.hasNext())
        {
            Object bb = aa.next();
            System.out.println(bb.getClass() + "====" + bb.hashCode());
        }
/** 运行结果:
class main.A====723074861
class main.A====895328852
*/

由上可知,HashSet在添加前对比两个对象时,equals返回了true,但因为HashCode值不同,所以新添加的元素被判定为2个不同的元素。

//类B的hashCode()方法总是返回1,但没有重写其equals()方法。
public class B {
        public int hashCode() {
            return 1;
        }
    }

books.add(new B());
books.add(new B());

/** 运行结果:
class main.B====1
class main.B====1
*/

HashSet在添加两个HashCode不同的元素时,由于equals返回了false,因此新添加的元素被判定为2个不同的元素。


//类C的hashCode()方法总是返回2,且有重写其equals()方法
public class C {
        public int hashCode() {
            return 2;
        }

        public boolean equals(Object obj) {
            return true;
        }
    }
books.add(new C());
books.add(new C());
/** 运行结果:
class main.C====2
*/

当equals和hashCode都相同时,HashSet才会将元素判定为同一个对象。


总结:

  1. hashCode()决定存放的位置,它和equals必须同时满足才允许新元素加入集合。

  2. 如果两个对象的hashCode相同,但是它们的equlas返回值不同,HashSet会在这个位置用链式结构来保存多个对象。而HashSet访问集合元素时也是根据元素的HashCode值来快速定位的,这种链式结构会导致性能下降。

  3. 使用HashSet集合,在重新元素内部equals和hashCode方法时,一定要知悉HashSet内部原理。

LinkedHashSet

LinkedHashSet继承自HashSet,顾名思义,LinkedHashSet在HashSet的基础上使用了链表的存储形式。基于此,我们大致应该猜想到,这种集合保持着顺序。

基于HashSet上的代码进行修改:

Set books = new LinkedHashSet();
        books.add(new A());
        books.add(new A());

        books.add(new B());
        books.add(new B());

        books.add(new C());
        books.add(new C());
        
        books.add("aaa");
        books.add("bbb");
        books.add("aaa");
        Iterator aa = books.iterator();
        while (aa.hasNext()) {
            Object bb = aa.next();
            System.out.println(bb.getClass() + "====" + bb.hashCode());
        }
/** 运行结果:
class main.A====723074861
class main.A====895328852
class main.B====1
class main.B====1
class main.C====2
class java.lang.String====96321
class java.lang.String====97314
*/

基于HashSet的知识分析Log可得出,A对象、B对象都被添加了两次,C对象由于被判定为同一个对象,因此不可重复插入。后面新增的 aaabbbaaa 中,前两个都被添加入了集合,最后一个元素又是 aaa ,因此没有存入到集合中。和HashSet()比较得知,LinkedHashSet是保持插入顺序的。


SortedSet

SortedSet是一个接口,该接口在Set接口上又实现了排序接口。

TreeSet
        TreeSet nums = new TreeSet();
        nums.add(5);
        nums.add(2);
        nums.add(10);
        nums.add(-9);
        System.out.println("默认进行了排序:" + nums);
        System.out.println("输出集合里的第一个元素:"+nums.first());
        System.out.println("输出集合里的最后一个元素:"+nums.last());
        System.out.println("返回小于4的子集,不包含4:"+nums.headSet(4));
        System.out.println("返回大于等于5的子集:"+nums.tailSet(5));
        System.out.println("返回大于等于-3,小于4的子集:"+nums.subSet(-3, 4));
/** 运行结果:
默认进行了排序:[-9, 2, 5, 10]
输出集合里的第一个元素:-9
输出集合里的最后一个元素:10
返回小于4的子集,不包含4:[-9, 2]
返回大于等于5的子集:[5, 10]
返回大于等于-3,小于4的子集:[2]
*/

TeeeSet不再使用HashCode值来决定元素的存储位置了,它采用红黑树的数据结构来存储集合元素。

TreeSet支持两种排序方式: 自然排序、定制排序

自然排序
  1. TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间的大小关系,然后将集合元素按升序排序,即自然排序。如果试图把一个对象添加到TreeSet时,则该对象的类必须实现Comparable接口,否则程序会抛出异常。

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

  3. 当需要把一个对象放入TreeSet中,重写该对象对应类的equals()方法时,应该保证该方法与compareTo(Object obj)方法有一致的结果,即如果两个对象通过equals()方法比较返回true时,这两个对象通过compareTo(Object obj)方法比较结果应该也为0(即相等)

定制排序

若要实现自定义排序的效果,可以通过Comparator接口的帮助。该接口里包含一个int compare(T o1, T o2)方法,该方法用于比较大小

TreeSet ts = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                D d1 = (D)o1;
                D d2 = (D)o2;
                return d1.age > d2.age ? -1
                        : d1.age < d2.age ? 1 : 0;
            }
        });
        ts.add(new D(5));
        ts.add(new D(-3));
        ts.add(new D(9));
        ts.add(new D(11));
        ts.add(new D(2));
        System.out.println(ts);
/** 运行结果:
[D[age:11], D[age:9], D[age:5], D[age:2], D[age:-3]]
*/

自然排序、定制排序、Comparator决定的是谁大的问题,即按什么顺序(升序、降序)进行排序
它们的关注点是不同的,一定要注意区分


EnumSet

在这里插入图片描述

<think>好的,我现在需要帮助用户了解如何使用Java的集合框架,特别是Set的用法。首先,我得回顾一下用户提供的引用内容,看看有没有相关的信息可以利用。 用户提到了引用[1]、[2]、[3]、[4]、[5],这些引用主要涉及Java集合框架的基础知识,包括Collection接口、List和Set的区别,以及迭代器的使用。用户特别想了解Set的用法,所以需要重点讲解Set的实现类,比如HashSet、LinkedHashSet和TreeSet。 首先,我应该从整体上介绍Java集合框架的结构,说明Collection接口是基础,然后分支出List和Set。接着,详细讲解Set的特点:不允许重复元素、无序(但LinkedHashSet保持插入顺序,TreeSet有序)。然后,结合引用中的内容,比如引用[3]提到Set是Collection的子接口,不重复且无序,这可以作为理论支持。 接下来,需要给出具体的代码示例。例如,如何创建HashSet,添加元素,删除元素,遍历集合。这里可以引用用户提供的代码片段,比如引用[5]中的例子,展示集合如何存储引用类型,以及修改元素对集合的影响。不过用户的问题是关于Set的,可能需要调整示例代码,使用Set的实现类。 另外,用户可能对Set的常用方法感兴趣,比如add、remove、contains、size等,这些在引用[3]和[4]中提到过。需要说明这些方法在Set中的使用方式,以及注意事项,比如添加重复元素会返回false。 还需要解释Set的不同实现类的区别,比如HashSet基于哈希表,TreeSet基于红黑树,LinkedHashSet维护插入顺序。这有助于用户根据需求选择合适的集合类型。 最后,应该生成相关问题,帮助用户进一步学习,比如如何保证Set中元素的唯一性,或者如何对Set进行排序。这些问题可以引导用户深入理解Set的特性。 在回答过程中,要确保使用正确的中文表达,行内数学公式用$...$,但这里可能用不上数学公式。需要检查LaTeX语法是否正确,但当前问题主要是代码和集合框架的讲解,可能不需要太多数学表达式。另外,注意引用标注,比如在提到Collection接口时添加[^3],提到迭代器时引用[^2]。 总结回答的结构:先概述集合框架,然后介绍Set接口及其实现类,接着是常用方法和代码示例,最后是注意事项和相关问题。确保每个部分都结合提供的引用内容,正确标注来源。</think>### 如何使用Java集合(Collection)框架? Java集合框架(JCF)提供了一套高效的数据结构,用于存储和操作对象集合。以下是核心概念和用法说明,特别针对`Set`的用法展开。 --- #### 1. **集合框架结构** Java集合框架以`Collection`接口为根,主要分为两类: - **`List`(列表)**: 有序且允许重复元素,如`ArrayList`、`LinkedList`。 - **`Set`(集合)**: 不允许重复元素,且通常无序,如`HashSet`、`TreeSet`。 --- #### 2. **Set接口的核心特性** - **元素唯一性**: 若尝试添加重复元素,`add()`方法会返回`false`。 - **无序性**: 默认不保证顺序(`HashSet`),但`LinkedHashSet`维护插入顺序,`TreeSet`按自然顺序或自定义规则排序。 - **依赖`equals()`和`hashCode()`**: 用于判断元素是否重复[^5]。 --- #### 3. **Set的实现类及用法** ##### 3.1 **HashSet** 基于哈希表实现,适合快速查找和去重。 ```java Set<String> set = new HashSet<>(); set.add("apple"); set.add("banana"); set.add("apple"); // 重复元素,添加失败 System.out.println(set); // 输出:[banana, apple] ``` ##### 3.2 **LinkedHashSet** 维护插入顺序。 ```java Set<Integer> linkedSet = new LinkedHashSet<>(); linkedSet.add(3); linkedSet.add(1); linkedSet.add(2); System.out.println(linkedSet); // 输出:[3, 1, 2] ``` ##### 3.3 **TreeSet** 基于红黑树实现,自动排序。 ```java Set<Integer> treeSet = new TreeSet<>(); treeSet.add(3); treeSet.add(1); treeSet.add(2); System.out.println(treeSet); // 输出:[1, 2, 3] ``` --- #### 4. **常用方法** - **添加元素**:`add(E e)` - **删除元素**:`remove(Object o)` - **判断包含**:`contains(Object o)` - **遍历集合**:使用迭代器`Iterator`或增强`for`循环。 ```java Set<String> set = new HashSet<>(); set.add("Java"); Iterator<String> it = set.iterator(); while(it.hasNext()) { System.out.println(it.next()); } ``` --- #### 5. **注意事项** - **存储引用类型**: 集合存储的是对象的引用,修改对象属性会影响集合中的元素。 - **线程不安全**: `HashSet`、`TreeSet`非线程安全,多线程环境需用`Collections.synchronizedSet()`包装。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值