Java 容器类源码详解 Set

 更新时间:2019年06月20日 16:18:06   作者:Givefine  
这篇文章主要介绍了Java 容器类源码详解 Set,Set 表示由无重复对象组成的集合,也是集合框架中重要的一种集合类型,直接扩展自 Collection 接口。,需要的朋友可以参考下

前言

Set 表示由无重复对象组成的集合,也是集合框架中重要的一种集合类型,直接扩展自 Collection 接口。在一个 Set 中,不能有两个引用指向同一个对象,或两个指向 null 的引用。如果对象 a 和 b 的引用满足条件 a.equals(b),那么这两个对象也不能同时出现在集合中。

通常 Set 是不要求元素有序的,但也有一些有序的实现,如 SortedMap 接口、LinkedHashSet 接口等。

概述

Set 的具体实现通常都是基于 Map 的。因为 Map 中键是唯一的,因而在基于 Map 实现 Set 时,只需要关心 Map 中的键,和键关联的值不需要有意义,使用一个任意的对象“占位”即可。我们在前面分析 Map 中的迭代器时,KeySet() 方法得到的就是一个 Set。

前面我们分析过 Map 接口的几个具体实现,通用的实现 HahsMap ,插入或访问序的 LinkedHashMap , 按照键升序的 TreeMap。同样,在 Set 的具体实现中,也有 HashSet 、 LinkedHashSet 和 TreeSet 等,分别和 Map 一一对应,它们的特性对应着相应的 Map 实现的特性。下面基于 HashSet 的实现做一个简略的介绍。

HashSet 的实现

public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
static final long serialVersionUID = -5024744406713321676L;
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
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);
}
}


从成员变量和构造方法可以清楚地看到,内部使用了一个 HahsMap,同时定义了一个无意义的空的静态 Object 对象(占用8byte) PRESENT。既然 map 中和键关联的值没有意义,为什么不干脆使用 null 呢?我们看一下 add() 方法:

public boolean add(E e) {
return map.put(e, PRESENT)==null;
}

Map 的 put() 方法在添加一个新的键时会返回 null,在更新一个已经存在的键关联的值时会返回旧值。因而 Set 中的 add() 方法可以据此判断新加入的元素是否改变了集合,如果改变了就返回 true。因而 PRESENT 不可以使用 null 。

其它的方法这里简单地列一下,都是基于 map 实现的:

public boolean contains(Object o) {
return map.containsKey(o);
}
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
public Iterator<E> iterator() {
return map.keySet().iterator();
}
public int size() {
return map.size();
}
public boolean isEmpty() {
return map.isEmpty();
}
public void clear() {
map.clear();
}
@SuppressWarnings("unchecked")
public Object clone() {
try {
HashSet<E> newSet = (HashSet<E>) super.clone();
newSet.map = (HashMap<E, Object>) map.clone();
return newSet;
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
}
//序列化
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// Write out any hidden serialization magic
s.defaultWriteObject();
// Write out HashMap capacity and load factor
s.writeInt(map.capacity());
s.writeFloat(map.loadFactor());
// Write out size
s.writeInt(map.size());
// Write out all elements in the proper order.
for (E e : map.keySet())
s.writeObject(e);
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in any hidden serialization magic
s.defaultReadObject();

// Read capacity and verify non-negative.
int capacity = s.readInt();
if (capacity < 0) {
throw new InvalidObjectException("Illegal capacity: " +
capacity);
}
// Read load factor and verify positive and non NaN.
float loadFactor = s.readFloat();
if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
throw new InvalidObjectException("Illegal load factor: " +
loadFactor);
}
// Read size and verify non-negative.
int size = s.readInt();
if (size < 0) {
throw new InvalidObjectException("Illegal size: " +
size);
}
// Set the capacity according to the size and load factor ensuring that
// the HashMap is at least 25% full but clamping to maximum capacity.
capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),
HashMap.MAXIMUM_CAPACITY);
// Create backing HashMap
map = (((HashSet<?>)this) instanceof LinkedHashSet ?
new LinkedHashMap<E,Object>(capacity, loadFactor) :
new HashMap<E,Object>(capacity, loadFactor));
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
@SuppressWarnings("unchecked")
E e = (E) s.readObject();
map.put(e, PRESENT);
}
}

小结

Set 的内部通常是基于 Map 来实现的,Map 中的 Key 构成了 Set,而 Value 全部使用一个无意义的 Object 。

Set 的特征与其内部的 Set 的特征是一致的。基于 HashMap 的 HashSet 是无序时的最佳通用实现,基于 LinkedHashMap 的 LinkedHashSet 保留插入或访问的顺序,基于 TreeMap 的 TreeSet 可以按照元素升序排列,要求元素实现 Comaprable 接口或自定义比较器。

HashSet , LinkedHashSet, TreeSet 都不是线程安全的,在多线程环境下使用时要注意同步问题。

CopyOnWriteArraySet 是一个线程安全的实现,但是并不是基于 Map 实现的,而是通过 CopyOnWriteArrayList 实现的。使用 addIfAbsent() 方法进行去重,性能比较一般。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Red Hat 安装JDK与IntelliJ IDEA的详细过程

    Red Hat 安装JDK与IntelliJ IDEA的详细过程

    YUM是基于Red Hat的Linux发行版的一个强大而用户友好的包管理工具,这篇文章主要介绍了Red Hat安装JDK与IntelliJ IDEA,需要的朋友可以参考下
    2023-08-08
  • Java检查非空的三种方法总结

    Java检查非空的三种方法总结

    这篇文章主要介绍了Java检查非空的三种方法总结,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • 三种SpringBoot中实现异步调用的方法总结

    三种SpringBoot中实现异步调用的方法总结

    Spring Boot 提供了多种方式来实现异步任务,这篇文章主要为大家介绍了常用的三种实现方式,文中的示例代码讲解详细,需要的可以参考一下
    2023-05-05
  • java解析sina视频

    java解析sina视频

    本文介绍了一个java解析sina视频地址的例子,从这个例子中可以学习到java使用sax解析xml的方法,大家可以参考修改成其它功能
    2014-01-01
  • 多个版本JAVA切换的简单步骤记录

    多个版本JAVA切换的简单步骤记录

    在工作中或者学习过程中,有一些特殊情况我们需要来切换java版本来做比较,比如一些新特性等等的相关资料,这篇文章主要介绍了多个版本JAVA切换的相关资料,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2024-09-09
  • spring中FactoryBean中的getObject()方法实例解析

    spring中FactoryBean中的getObject()方法实例解析

    这篇文章主要介绍了spring中FactoryBean中的getObject()方法实例解析,分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
    2018-02-02
  • Java中BitMap(位图)hutool版、IntMap、LongMap示例详解

    Java中BitMap(位图)hutool版、IntMap、LongMap示例详解

    这篇文章主要给大家介绍了关于Java中BitMap(位图)hutool版、IntMap、LongMap的相关资料,通过位运算高效存储和检索整数,相比于传统数组,它们在内存占用和性能上都有显著优势,需要的朋友可以参考下
    2024-12-12
  • 详解Java中List接口底层实现原理

    详解Java中List接口底层实现原理

    Java是一种广泛应用的编程语言,被广泛应用于各种平台和应用领域,List接口是Java中最重要的数据结构之一,它为我们提供了一种灵活、高效、可扩展的数据结构,本篇文章将首先介绍Java中List接口的基本特性和使用方法,然后深入研究List接口的底层实现原理
    2023-11-11
  • spring boot 使用utf8mb4的操作

    spring boot 使用utf8mb4的操作

    这篇文章主要介绍了spring boot 使用utf8mb4的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-02-02
  • springboot整合netty框架实现站内信

    springboot整合netty框架实现站内信

    Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,这篇文章主要介绍了springboot整合netty框架的方式小结,需要的朋友可以参考下
    2022-12-12

最新评论