集合/容器

集合的概念

在我们的开发实践中,经常需要保存一些变长的数据集合,于是,我们需要一些能够动态增长长度的容器来保存我们的数据。Java中对于各种数据结构的实现,就是我们用到的集合。

集合体系概述

Collection 接口-定义了存取一组对象的方法,其子接口Set和List分别定义了存储方式。

  • Set 中的数据对象没有顺序且不可以重复。
  • List 中的数据对象有顺序且可以重复。

在这里插入图片描述

List 接口及实现类

List继承了Collection接口,有三个实现的类

  • ArrayList
    数组列表,数据采用数组方式存储。
  • LinkedList
    链表
  • Vector
    数组列表,添加同步锁,线程安全的

ArrayList实现了长度可变的数组,在内存中分配连续的空间。
遍历元素和随机访问元素的效率比较高
在这里插入图片描述
LinkedList采用链表存储方式。插入、删除元素时效率比较高
在这里插入图片描述
ArrayList需要注意的使用方法

removeRange(int fromIndex, int toIndex) 删除指定区间的元素(子类继承使用)

List接口集合迭代

for循环遍历

     通过索引值获取所对应的数据信息

增强for循环的遍历

     也是通过迭代器的方法获取信息

迭代器遍历(Iterator)

              通过集合返回迭代器。
    Iterator iter = list.iterator();
	while(iter.hasNext()){
		 iter.next();
		 } 

注意:迭代时删除元素

泛型

为什么会有泛型呢?
早期的Object类型可以接收任意的对象类型,但是在实际的使用中,会有类型转换的问题。也就存在这隐患,所以Java提供了泛型来解决这个安全问题。
如果没有定义具体类型,默认为Object.

泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。
参数化类型,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

public class Demo<T> {
    private T p;
    public T getP() {
       return p;
    }

    public void setP(T p) {
         this.p = p;
     }

1、泛型的类型参数只能是类类型(包括自定义类
2、泛型的类型参数可以有多个。
如果没有定义具体类型,默认为Object

Set 接口

Set接口继承了Collection接口。

Set中所存储的元素是不重复的,但是是无序的, Set中的元素是没有索引的

Set接口有两个实现类

HashSet
HashSet类中的元素不能重复,即彼此调用equals方法比较,都返回false。
底层数据结构是哈希表+链表
哈希表依赖于哈希值存储

TreeSet
可以给Set集合中的元素进行指定方式的排序。存储的对象必须实现Comparable接口。
TreeSet底层数据结构是二叉树(红黑树是一种自平衡的二叉树)
在这里插入图片描述

Map 接口

Map接口概述

将键映射到值的对象
键不能重复
键和值一一对应

HashMap

HashMap中元素的key值不能重复,即彼此调用equals方法,返回为false。排列顺序是不固定的。

TreeMap

TreeMap中所有的元素都保持着某种固定的顺序,如果需要得到一个有序的Map就应该使用TreeMap,key值所在类必须实现Comparable接口。

HashTable
实现了同步。

Map集合遍历

方式1:根据键找值
获取所有键的集合
遍历键的集合,获取到每一个键
根据键找值

方式2:根据键值对对象找键和值
获取所有键值对对象的集合
遍历键值对对象的集合,获取到每一个键值对对象
根据键值对对象找键和值

TreeMap
适用于按自然顺序或自定义顺序遍历键(key)。
TreeMap根据key值排序,key值需要实现Comparable接口,
重写compareTo方法。TreeMap根据compareTo的逻辑,对
key进行排序。
键是红黑树结构,可以保证键的排序和唯一性

Collections类

  • 是集合类的工具类,与数组的工具类Arrays类似
  • 定义了大量静态方法( 同步集合对象的方法 对List排序的方法)
<think>我们正在讨论Java并发包中的并发容器。根据引用[1]和[2],Java并发包提供了一系列线程安全的容器,用于解决多线程环境下的共享数据问题。这些容器可以分为几类:线程安全的List、Set、Map以及队列(队列已在之前的问题中详细讨论,这里主要关注非队列的容器)。 分类如下: 1. List: - CopyOnWriteArrayList: 基于写时复制技术,适用于读多写少的场景。 2. Set: - CopyOnWriteArraySet: 基于CopyOnWriteArrayList实现,同样适用于读多写少的场景。 - ConcurrentSkipListSet: 基于跳表实现的有序集合,支持并发访问。 3. Map: - ConcurrentHashMap: 高并发环境下使用的HashMap,通过分段锁(JDK7)或CAS+synchronized(JDK8)实现线程安全。 - ConcurrentSkipListMap: 基于跳表实现的有序Map,支持并发访问。 4. 队列(Queue/Deque): - 上一问题已详细讨论,包括阻塞队列、非阻塞队列等,这里不再重复。 下面我们详细说明每个容器的特点和使用场景。 一、List容器 1. CopyOnWriteArrayList - 原理:写操作(add, set, remove等)时,会复制底层数组,修改在新数组上进行,最后将新数组替换旧数组。读操作无锁,直接访问当前数组。 - 特点:读操作非常快,写操作因为需要复制数组,所以开销较大。适合读多写少的场景。 - 注意:由于读操作不会阻塞,所以可能读到旧数据,因此适合对数据一致性要求不高的场景。 - 示例代码: ```java CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(); // 写操作(会复制数组) list.add("item1"); // 读操作(无锁) String item = list.get(0); ``` 二、Set容器 1. CopyOnWriteArraySet - 原理:内部使用CopyOnWriteArrayList实现,因此特性与CopyOnWriteArrayList类似。 - 特点:同样适用于读多写少的场景,写操作开销大,读操作无锁。 - 注意:由于基于CopyOnWriteArrayList,所以它不适用于元素数量很大的集合,因为每次写操作都要复制整个数组。 2. ConcurrentSkipListSet - 原理:基于ConcurrentSkipListMap实现,使用跳表数据结构,保证元素的有序性(自然顺序或通过Comparator)。 - 特点:支持并发的插入、删除和查找操作,且操作的时间复杂度为$O(\log n)$。 - 适用场景:需要有序且并发的集合。 三、Map容器 1. ConcurrentHashMap - 原理:在JDK7中,使用分段锁(Segment)来减小锁的粒度,每个段独立加锁。在JDK8中,改为使用CAS+synchronized来保证并发安全,并且当链表长度超过阈值时转换为红黑树,以提高查找效率。 - 特点:高并发环境下性能优异,比Hashtable和Collections.synchronizedMap()性能好很多。 - 适用场景:高并发且需要键值对存储的场景。 - 示例代码: ```java ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(); map.put("key1", 1); int value = map.get("key1"); ``` 2. ConcurrentSkipListMap - 原理:基于跳表(Skip List)实现的有序Map。跳表是一种可以快速查找、插入、删除的数据结构,通过多级索引实现。 - 特点:支持高并发的有序Map,所有操作都是线程安全的,且操作的时间复杂度为$O(\log n)$。 - 适用场景:需要有序且并发的键值对存储,例如排行榜等。 四、队列容器(简要提及,因为之前问题已覆盖) - 包括BlockingQueue的实现(如ArrayBlockingQueue, LinkedBlockingQueue, PriorityBlockingQueue等)和非阻塞队列(如ConcurrentLinkedQueue, ConcurrentLinkedDeque)等。 总结表格: | 容器类型 | 实现类 | 特点 | 适用场景 | |---------|--------|------|---------| | List | CopyOnWriteArrayList | 读无锁,写复制数组 | 读多写少,如监听器列表 | | Set | CopyOnWriteArraySet | 基于CopyOnWriteArrayList,读多写少 | 读多写少的集合 | | Set | ConcurrentSkipListSet | 基于跳表,有序,并发安全 | 需要有序的并发集合 | | Map | ConcurrentHashMap | 分段锁或CAS+synchronized,高并发 | 高并发键值存储,如缓存 | | Map | ConcurrentSkipListMap | 跳表实现,有序 | 需要有序的并发键值存储 | 注意:使用这些并发容器时,需要根据具体场景选择。例如,如果对读性能要求极高且写操作很少,可以选择CopyOnWrite系列;如果对并发写要求高,则选择ConcurrentHashMap或ConcurrentSkipListMap。 常见问题: 1. 如何选择List容器? - 如果需要频繁修改(写操作),则不建议使用CopyOnWriteArrayList,因为写操作会复制整个数组。此时,可以考虑使用Collections.synchronizedList(),但要注意其性能不如CopyOnWriteArrayList在读多写少场景下的表现。 2. ConcurrentHashMap在JDK7和JDK8中的区别? - JDK7:使用分段锁(Segment),每个段相当于一个小的HashMap,锁的粒度是段。 - JDK8:使用Node数组+链表+红黑树,锁的粒度是每个数组元素(桶),使用CAS和synchronized来保证并发安全。当链表长度大于8且数组长度超过64时,链表转换为红黑树。 3. 为什么需要ConcurrentSkipListMap? - 因为ConcurrentHashMap是无序的,当需要有序的Map时,可以使用ConcurrentSkipListMap。它提供了并发环境下的有序映射。 示例代码:ConcurrentHashMap的线程安全操作 ```java ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(); // 多个线程可以安全地执行put操作 new Thread(() -> map.put("key2", 2)).start(); new Thread(() -> map.put("key3", 3)).start(); // 使用原子更新方法 map.computeIfAbsent("key4", k -> 4); ``` 最后,注意并发容器并不能保证所有复合操作都是原子性的,例如: ```java // 非原子操作 if (!map.containsKey("key")) { map.put("key", 1); } // 应该使用原子方法 map.putIfAbsent("key", 1); ``` 因此,使用并发容器时,应尽量使用其提供的原子方法(如putIfAbsent, compute, computeIfAbsent, merge等)来避免竞态条件。 相关问题: 1. CopyOnWriteArrayList的写操作是如何保证线程安全的? 2. ConcurrentHashMap在JDK8中是如何实现扩容的? 3. 跳表(Skip List)的数据结构是怎样的?为什么它适合并发? 4. 并发容器和普通容器通过Collections.synchronizedXXX包装的容器有什么区别?</think>### Java 并发包中的并发容器详解 Java 并发包(`java.util.concurrent`)提供了多种线程安全的容器类,用于解决多线程环境下的数据共享问题。这些容器通过高效的同步机制(如 CAS、写时复制、分段锁等)平衡线程安全与性能。以下是主要分类及实现: --- #### 一、线程安全 List | 实现类 | 核心机制 | 特点 | 适用场景 | |-------------------------|----------------------|----------------------------------------------------------------------|------------------------------| | **`CopyOnWriteArrayList`** | 写时复制 (Copy-on-Write) | 写操作复制新数组,读操作无锁;读多写少时性能高,写操作开销大[^1] | 监听器列表、配置管理等低频写场景 | | **`Collections.synchronizedList`** | 互斥锁 | 包装普通`ArrayList`,所有方法用`synchronized`同步;简单但吞吐量低 | 兼容旧代码的简单同步需求 | **示例**: ```java // 高并发读场景 List<String> safeList = new CopyOnWriteArrayList<>(); safeList.add("data"); // 写时复制 String data = safeList.get(0); // 无锁读 // 简单同步 List<String> syncList = Collections.synchronizedList(new ArrayList<>()); ``` --- #### 二、线程安全 Set | 实现类 | 核心机制 | 特点 | 适用场景 | |-------------------------|----------------------|----------------------------------------------------------------------|------------------------------| | **`CopyOnWriteArraySet`** | 写时复制 | 基于`CopyOnWriteArrayList`实现,特性类似[^1] | 读多写少的集合操作 | | **`ConcurrentSkipListSet`** | 跳表 (Skip List) | 有序集合(自然序或`Comparator`),CAS无锁实现,高并发插入/删除 | 需排序的高并发集合(如排行榜)| | **`Collections.synchronizedSet`** | 互斥锁 | 包装普通`HashSet`,方法全同步 | 简单同步需求 | **示例**: ```java Set<Integer> leaderboard = new ConcurrentSkipListSet<>(); leaderboard.add(100); // 并发插入有序集合 ``` --- #### 三、线程安全 Map | 实现类 | 核心机制 | 特点 | 适用场景 | |-------------------------|----------------------|----------------------------------------------------------------------|------------------------------| | **`ConcurrentHashMap`** | 分段锁 + CAS (JDK7)<br>CAS + `synchronized` (JDK8+) | 高并发读写,锁粒度细;JDK8 引入红黑树优化哈希冲突 | 缓存、会话存储等高并发 K-V 场景 | | **`ConcurrentSkipListMap`** | 跳表 | 有序 Map,支持并发排序和范围查询 | 需排序的并发字典(如范围检索) | | **`Collections.synchronizedMap`** | 互斥锁 | 包装普通`HashMap`,全方法同步 | 简单同步需求 | **`ConcurrentHashMap` 关键优化**: - JDK8 取消分段锁,改用 `Node` + CAS + `synchronized` 锁单个桶(Bucket) - 哈希冲突时链表转红黑树(阈值:链表长度 ≥ 8) - 支持原子方法:`compute()`, `merge()`, `search()` 等[^1] **示例**: ```java ConcurrentHashMap<String, Integer> cache = new ConcurrentHashMap<>(); // 原子更新 cache.compute("key", (k, v) -> (v == null) ? 1 : v + 1); ``` --- #### 四、线程安全队列(Queue/Deque) > 详见上一问题总结,核心实现包括: > - **阻塞队列**:`ArrayBlockingQueue`, `LinkedBlockingQueue` > - **非阻塞队列**:`ConcurrentLinkedQueue`, `ConcurrentLinkedDeque` > - **特殊队列**:`DelayQueue`, `PriorityBlockingQueue`[^2][^3] --- ### 并发容器选择指南 | 需求场景 | 推荐容器 | |------------------------|-----------------------------------| | 高频读 + 低频写 | `CopyOnWriteArrayList`/`CopyOnWriteArraySet` | | 高并发 K-V 存储 | `ConcurrentHashMap` | | 有序集合 + 高并发 | `ConcurrentSkipListSet`/`ConcurrentSkipListMap` | | 生产者-消费者模型 | `ArrayBlockingQueue`(有界) | | 定时任务调度 | `DelayQueue` | --- ### 常见问题解决方案 #### ❌ 复合操作非原子性 ```java // 错误:contains + add 非原子 if (!set.contains(item)) { set.add(item); } // 正确:使用 ConcurrentHashMap 的原子方法 ConcurrentHashMap<String, Boolean> map = new ConcurrentHashMap<>(); map.putIfAbsent("key", true); ``` #### ❌ `CopyOnWriteArrayList` 写性能差 ```java // 避免频繁写操作:批量添加 List<String> batchData = Arrays.asList("a", "b", "c"); safeList.addAll(batchData); // 减少复制次数 ``` #### ❌ 遍历时并发修改 ```java // ConcurrentHashMap 迭代器弱一致性(不抛异常) ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(); map.forEach((k, v) -> process(k, v)); // 安全遍历 ``` --- ### 总结 Java 并发容器的核心设计思想: 1. **降低锁粒度**:如 `ConcurrentHashMap` 的分段锁/CAS 优化。 2. **读写分离**:如 `CopyOnWriteArrayList` 写时复制。 3. **无锁算法**:如 `ConcurrentSkipListMap` 的跳表+CAS。 4. **专用场景优化**:如 `DelayQueue` 的延迟调度。 > **关键原则**: > - 优先选择并发专用容器(如 `ConcurrentHashMap`),而非 `Collections.synchronizedXXX` 包装类。 > - 根据读写比例选择数据结构(如读多写少用 `CopyOnWriteArrayList`)。 > - 有界性设计防止资源耗尽(如队列显式设置容量)。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值