Java集合框架:Set(HashSet,LinkedHashSet,TreeSet)

简介: Set概述 Set几乎都是内部用一个Map来实现, 因为Map里的KeySet就是一个Set,而value是假值,全部使用同一个Object。Set的特征也继承了那些内部Map实现的特征。

Set概述

 Set几乎都是内部用一个Map来实现, 因为Map里的KeySet就是一个Set,而value是假值,全部使用同一个Object。Set的特征也继承了那些内部Map实现的特征。


HashSet

1. 定义

package java.util;
public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    private transient HashMap<E,Object> map;
    private static final Object PRESENT = new Object();
    public HashSet() {
        map = new HashMap<>();
    }
//其余省略
}

2. 概述

 HashSet是基于HashMap来实现的,操作很简单,更像是对HashMap做了一次“封装”,而且只使用了HashMap的key来实现各种特性,而HashMap的value始终都是PRESENT。
HashSet不允许重复(HashMap的key不允许重复,如果出现重复就覆盖),允许null值,非线程安全
 罗列几个主要方法:

public Iterator<E> iterator() {
        return map.keySet().iterator();
    }
    public int size() {
        return map.size();
    }
    public boolean isEmpty() {
        return map.isEmpty();
    }
    public boolean contains(Object o) {
        return map.containsKey(o);
    }
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }
    public void clear() {
        map.clear();
    }

3. 使用

 案例1:

        Set<String> set = new HashSet<>();
        set.add("s2");
        set.add("s3");
        set.add("s4");
        set.add("s2");
        set.add("s5");
        set.add("s1");
        set.add(null);
        set.add("s21");
        set.add("sw2");
        set.add("s2");
        for(String i:set)
            System.out.println(i);
        System.out.println(set);

 输出:

null
s2
s1
sw2
s21
s5
s3
s4
[null, s2, s1, sw2, s21, s5, s3, s4]

LinkedHashSet

1. 定义(整个LinkedHashSet的代码)

package java.util;
public class LinkedHashSet<E>
    extends HashSet<E>
    implements Set<E>, Cloneable, java.io.Serializable {
    private static final long serialVersionUID = -2851667679971038690L;

    public LinkedHashSet(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor, true);
    }

    public LinkedHashSet(int initialCapacity) {
        super(initialCapacity, .75f, true);
    }

    public LinkedHashSet() {
        super(16, .75f, true);
    }

    public LinkedHashSet(Collection<? extends E> c) {
        super(Math.max(2*c.size(), 11), .75f, true);
        addAll(c);
    }
}

2. 概述

 HashSet有4个共有的构造函数:

    public HashSet() {
        map = new HashMap<>();
    }
   public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }
    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }
    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }

 还有一个包级私有的构造函数:

    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

 这个构造函数就是LinkedHashSet的关键,整个LinkedHashSet的四个构造函数全部都是调用了HashSet中的这个包级私有的构造函数(构造函数中的dummy就是标记,无实用,为了实现方法的重载而已),也就是说LinkedHashSet就是基于LinkedHashMap实现的

3. 使用

案例2:

        Set<String> set = new LinkedHashSet<>();
        set.add("s2");
        set.add("s3");
        set.add("s4");
        set.add("s2");
        set.add("s5");
        set.add("s1");
        set.add(null);
        set.add("s21");
        set.add("sw2");
        set.add("s2");
        for(String i:set)
            System.out.println(i);
        System.out.println(set);

 输出:

s2
s3
s4
s5
s1
null
s21
sw2
[s2, s3, s4, s5, s1, null, s21, sw2]

允许null值,保留插入顺序,非线程安全。


TreeSet

1. 定义

package java.util;
public class TreeSet<E> extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable
{
    private transient NavigableMap<E,Object> m;
    private static final Object PRESENT = new Object();
    public TreeSet() {
        this(new TreeMap<E,Object>());
    }
    public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }
    public TreeSet(Collection<? extends E> c) {
        this();
        addAll(c);
    }
    public TreeSet(SortedSet<E> s) {
        this(s.comparator());
        addAll(s);
    }
//其余省略
}

2. 概述

 TreeSet的内部基于TreeMap实现,同样value永远为PRESENT.
 案例3:

        Set<String> set = new TreeSet<String>();
        set.add("s2");
        set.add("s3");
        set.add("s4");
        set.add("s2");
        set.add("s5");
        set.add("s1");
//        set.add(null);
        set.add("s21");
        set.add("sw2");
        set.add("s2");
        for(String i:set)
            System.out.println(i);
        System.out.println(set);

 运行结果:

s1
s2
s21
s3
s4
s5
sw2
[s1, s2, s21, s3, s4, s5, sw2]

不允许重复,不允许null值(如果有基于null的比较器,就可以允许为null),默认按升序排列。
案例4(其中的Person2详细见《Comparable与Comparator浅析》):

        Set<Person2> set = new TreeSet<Person2>(new Comparator<Person2>(){
            @Override
            public int compare(Person2 o1, Person2 o2)
            {
                if(o1==null || o2==null)
                    return 0;
                return o1.getAge()-o2.getAge();
            }
        });
        Person2 p1 = new Person2("zzh",18);
        Person2 p2 = new Person2("jj",17);
        Person2 p3 = new Person2("qq",19);
        Person2 p4 = new Person2(null,19);
        set.add(p1);
        set.add(p2);
        set.add(p3);
        set.add(p4);

        System.out.println(set);

 输出结果:[jj:17, zzh:18, qq:19]
 可以看到不是强制性要求TreeSet的键为null。


参考资料:
1. 《Comparable与Comparator浅析

目录
相关文章
|
JSON Java 关系型数据库
Java更新数据库报错:Data truncation: Cannot create a JSON value from a string with CHARACTER SET 'binary'.
在Java中,使用mybatis-plus更新实体类对象到mysql,其中一个字段对应数据库中json数据类型,更新时报错:Data truncation: Cannot create a JSON value from a string with CHARACTER SET 'binary'.
1545 4
Java更新数据库报错:Data truncation: Cannot create a JSON value from a string with CHARACTER SET 'binary'.
|
存储 算法 Java
Java Set深度解析:为何它能成为“无重复”的代名词?
Java的集合框架中,Set接口以其“无重复”特性著称。本文解析了Set的实现原理,包括HashSet和TreeSet的不同数据结构和算法,以及如何通过示例代码实现最佳实践。选择合适的Set实现类和正确实现自定义对象的hashCode()和equals()方法是关键。
215 4
|
Java
那些与Java Set擦肩而过的重复元素,都经历了什么?
在Java的世界里,Set如同一位浪漫而坚定的恋人,只对独一无二的元素情有独钟。重复元素虽屡遭拒绝,但通过反思和成长,最终变得独特,赢得了Set的认可。示例代码展示了这一过程,揭示了成长与独特性的浪漫故事。
120 4
|
Java 开发者
Java Set:当“重复”遇见它,秒变“独宠”!
在Java编程中,Set接口确保集合中的元素不重复,每个元素都是独一无二的“独宠”。本文介绍了Set的两种常见实现:HashSet和TreeSet。HashSet基于哈希表实现,提供高效的添加、删除和查找操作;TreeSet基于红黑树实现,不仅去重还能对元素进行排序。通过示例代码,展示了这两种集合的具体应用,帮助开发者更好地理解和使用Set。
164 4
|
5月前
|
存储 JavaScript Java
(Python基础)新时代语言!一起学习Python吧!(四):dict字典和set类型;切片类型、列表生成式;map和reduce迭代器;filter过滤函数、sorted排序函数;lambda函数
dict字典 Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。 我们可以通过声明JS对象一样的方式声明dict
356 1
|
8月前
|
存储 缓存 JavaScript
Set和Map有什么区别?
Set和Map有什么区别?
585 1
|
5月前
|
存储 算法 容器
set_map的实现+set/map加持秒杀高频算法题锻炼算法思维
`set`基于红黑树实现,支持有序存储、自动去重,增删查效率为O(logN)。通过仿函数可自定义排序规则,配合空间配置器灵活管理内存。不支持修改元素值,迭代器失效需注意。`multiset`允许重复元素。常用于去重、排序及查找场景。
|
9月前
|
存储 JavaScript 前端开发
for...of循环在遍历Set和Map时的注意事项有哪些?
for...of循环在遍历Set和Map时的注意事项有哪些?
432 121
|
编译器 C++ 容器
【c++丨STL】基于红黑树模拟实现set和map(附源码)
本文基于红黑树的实现,模拟了STL中的`set`和`map`容器。通过封装同一棵红黑树并进行适配修改,实现了两种容器的功能。主要步骤包括:1) 修改红黑树节点结构以支持不同数据类型;2) 使用仿函数适配键值比较逻辑;3) 实现双向迭代器支持遍历操作;4) 封装`insert`、`find`等接口,并为`map`实现`operator[]`。最终,通过测试代码验证了功能的正确性。此实现减少了代码冗余,展示了模板与仿函数的强大灵活性。
329 2