Arrays数组操作类
Arrays类提供查找操作数组的静态方法。
集合Collection
遍历travers和迭代iterate的区别。
Collections集合操作类
Collections类为集合提供查找排序的静态方法。
列表List
列表即数据结构中的线性表。
Vector 的所有核心方法(如 add()、get()、remove() 等)都使用了 synchronized 关键字修饰,确保在多线程环境下对 Vector 的操作是原子性和线程安全的 。Vector默认扩容2倍。
ArrayList默认扩容1.5倍。
ArrayDeque
ArrayDeque 是 Java 编程语言中一种基于数组的双端队列,既能当普通队列用也能当栈用,性能通常比 LinkedList 更好 。
用起来有什么讲究
- 存数据有限制:这个容器里不允许存放空值(null),如果强行放入会直接报错 。
- 容量自动变化:
- 初始默认容量是 16,最小不能低于 8。
- 每次扩容都是变成原来的两倍,且总容量始终是 2 的 n 次方,这样计算位置更快 。
- 线程不安全:它没有内置锁,如果在多线程环境下同时修改,需要自己加锁或者用其他线程安全的类替代 。
常用方法怎么分
- 从两头插入数据:
- 头部插入:用
addFirst或offerFirst,相当于把新元素塞到队伍最前面 。 - 尾部插入:用
addLast或offerLast,相当于把新元素排到队伍最后面 。
- 头部插入:用
- 从两头取出数据:
- 移除并返回:
remove系列方法在没数据时会报错,poll系列方法没数据时返回空值,更安全 。 - 只看不移除:
get系列方法没数据时报错,peek系列方法没数据时返回空值,适合只是想看看队头或队尾是谁的情况 。
- 移除并返回:
- 当栈使用:
- 直接用
push方法压入数据,用pop方法弹出数据,完全符合栈的先进后出规则 。
- 直接用
跟 LinkedList 比咋样
- 性能更优:ArrayDeque 底层是数组,内存占用更紧凑,访问速度比 LinkedList 快,因为 LinkedList 每个节点还要额外存前后连接信息 。
- 功能覆盖:虽然 LinkedList 也能当双端队列用,但 ArrayDeque 是 JDK 1.6 之后专门为了优化这个场景推出的,官方更推荐用 ArrayDeque 代替 LinkedList 做队列或栈 。
- 适用场景:如果你只需要在两头操作数据,不需要在中间插入删除,优先选 ArrayDeque;只有需要在列表中间频繁增删时,才考虑 LinkedList。
线程安全的List
Collections.synchronizedList 和 Vector 都是 Java 中用于实现线程安全的 List 的方式。
核心区别
-
Vector 是 JDK 1.0 就提供的原生线程安全列表,其所有方法(如add()、get()、remove())都使用synchronized关键字修饰,直接在方法级别加锁。 -
Collections.synchronizedList() 是 JDK 1.2 引入的包装器工具,可以将任意List(如ArrayList、LinkedList)转换为线程安全的版本,通过内部锁(通常是mutex对象)同步方法调用。
关键对比
| 特性 | Vector | Collections.synchronizedList() |
|---|---|---|
| 实现方式 | 内置同步方法(synchronized 方法) | 包装任意 List,用同步代码块包裹 |
| 底层结构 | 固定为数组 | 可基于 ArrayList(数组)或 LinkedList(链表) |
| 扩展性 | 仅支持数组结构 | 支持所有 List 子类,保留原结构特性 |
| 迭代器同步 | 自动同步(迭代器方法加锁) | 需手动同步,否则可能抛 ConcurrentModificationException |
| 性能 | 性能较差,因每个方法都加锁 | 略优,但整体仍不如 CopyOnWriteArrayList 等并发集合 |
| 是否推荐使用 | 不推荐(遗留类,性能差) | 较推荐(灵活,兼容性强) |
⚠️ 注意:尽管
Vector未被标记为@Deprecated,但官方和社区普遍认为它不应在新代码中使用。
使用建议
-
优先选择
Collections.synchronizedList(new ArrayList<>()):
如果需要线程安全的List且写操作不多,这是比Vector更优的选择,因为它兼容性强,可基于ArrayList或LinkedList,且能自定义锁对象。 -
读多写少,考虑
CopyOnWriteArrayList:
它在读操作上无锁,性能更好,适合高并发读场景。 -
避免使用
Vector:
除非维护旧代码,否则不应在新项目中使用。其性能差、灵活性低,已被现代并发工具取代。
迭代时的注意事项
对于 java.util.Collections.synchronizedList(),必须手动同步迭代过程,否则可能引发并发异常:
List<String> list = Collections.synchronizedList(new ArrayList<>()); // ... 添加元素
synchronized (list) {
// 手动加锁
for (String item : list) {
System.out.println(item);
}
}
而 Vector 的迭代器已内置同步,无需额外处理。
总结
-
Collections.synchronizedList更灵活、更现代,适合大多数需要线程安全List的场景。 -
Vector是历史遗留类,应避免在新代码中使用。 - 两者都不是高性能并发集合,若对性能有要求,建议使用
java.util.concurrent包中的CopyOnWriteArrayList或其他并发容器。
CopyOnWriteArrayList
CopyOnWriteArrayList 是 Java 并发包中提供的线程安全 List 实现,采用"写时复制"策略,读操作无需加锁,适合读多写少的并发场景。
什么时候用它最合适
- 读操作远多于写操作:读取频率高、修改频率低的场景,如系统配置项、白名单/黑名单等。
- 遍历期间不允许抛异常:迭代器基于数组快照,遍历时其他线程修改不会影响当前遍历,不会抛出 ConcurrentModificationException。
- 数据量不大且变化少:集合较小、初始化后很少修改的场景,如事件监听器列表、只读数据缓存等。
优缺点
- 优点:
- 读操作性能极高,无需加锁,适合高并发读取。
- 线程安全,写操作通过锁和数组复制保证一致性。
- 迭代安全,遍历时不会因并发修改而异常。
- 缺点:
- 写操作开销大,每次修改都要复制整个数组,内存占用翻倍。
- 数据弱一致性,读操作可能获取到旧数据,不适合强一致性要求的场景。
- 不适合写多读少,频繁写操作会导致性能严重下降和大量垃圾回收。
和 ArrayList 区别
- 线程安全性:ArrayList 非线程安全,多线程修改可能数据不一致;CopyOnWriteArrayList 线程安全,支持并发读写。
- 内部实现:ArrayList 直接修改底层数组;CopyOnWriteArrayList 写操作时创建新数组副本,修改后再替换原数组引用。
- 迭代器行为:ArrayList 迭代时修改会抛 ConcurrentModificationException;CopyOnWriteArrayList 迭代器基于快照,遍历时修改不影响当前迭代。
Set集合
Set<T>接口声明数学含义的集合,即元素可以没有次序且各不相同。
HashSet是散列集合类,元素无序。
TreeSet是树集合类,使用平衡二叉排序树存储,元素按关键字排序。
映射
映射不是Collection的继承者,有HashMap、TreeMap
- Hashtable自JDK1.0就有,是线程安全的散列容器,其线程安全是用synchronized实现的。
- HashMap是JDK1.2增加的,非线程安全的,但是单线程环境下比Hashtable性能提高很多;
- ConcurrentHashMap是JDK1.5增加的,是线程安全的, 基于显示的lock;
Hashtable和HashMap的区别:
(1).都实现了Map接口,HashTable是Dictionary的子类,HashMap是AbstractMap的子类;
(2).HashTable中的方法是同步的,而HashMap中的方法在缺省情况下是非同步的。即是说,在多线程应用程序中,不用专门的操作就安全地可以使用HashTable了;而对于HashMap,则需要额外的同步机制。
(3).在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,既可以表示HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键,而应该用containsKey()方法来判断。
(4).Map.put()方法会覆盖上一次同键的值。
按顺序遍历Map
HashMap 基于哈希表实现不保证任何顺序。遍历HashMap元素的顺序可能与它们被插入的顺序或在哈希表中的存储顺序不同。需要按键(Key)或值(Value)排序遍历 Map,有几种方法可以实现。
方法1:使用 TreeMap 或 LinkedHashMap
-
使用
TreeMap:
TreeMap是一个红黑树实现的NavigableMap,它按键排序。 -
使用
LinkedHashMap:
LinkedHashMap维护了一个双向链表来记录插入顺序,因此可以按插入顺序遍历。
方法2:使用 Stream API 和 Collectors
-
按键排序:
Map<Integer, String> hashMap = new HashMap<>();
hashMap.put(3, "C");
hashMap.put(1, "A");
hashMap.put(2, "B");
hashMap.entrySet().stream()
.sorted(Map.Entry.comparingByKey()) // 根据键排序
.forEach(entry -> System.out.println(entry.getKey() + ": " + entry.getValue()));
-
按值排序:
hashMap.entrySet().stream() .sorted(Map.Entry.comparingByValue()) // 根据值排序 .forEach(entry -> System.out.println(entry.getKey() + ": " + entry.getValue()));
方法3:自定义排序规则
hashMap.entrySet().stream()
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue())) // 例如,按值降序排序
.forEach(entry -> System.out.println(entry.getKey() + ": " + entry.getValue()));
new ArrayList<Group>(1); Constructs a new instance of ArrayList with the specified initial capacity.
HashMap比较重要的问题是容量、负荷系数和阈值调整。HashMap默认的初始容量是16,负荷系
数是0.75。阈值是为容量乘以负荷系数,无论何时我们尝试添加一个entry,如果map的大小比阀
值大的时候,HashMap会对map的内容进行重新哈希,且使用更大的容量。容量总是2的幂。=即当哈希表中的元素数量超过其容量的75%时,哈希表会自动扩容。扩容通常是按照原来的容量的两倍来进行的。例如创建了一个初始容量为16的HashMap,当元素数量达到16*0.75=12时,哈希表会自动扩容到32。
LinkedHashMap
LinkedHashMap 是 Java 中一种有序的 Map 集合,它在 HashMap 基础上加了双向链表,能按插入顺序或访问顺序保存键值对,适合需要保持顺序或实现 LRU 缓存的场景 。
java中各种集合的用法和比较
Collection 是对象集合, 有两个子接口 List 和 Set,List 可以通过下标取值,值可以重复,而 Set 只能通过游标来取值,并且值不能重复。
ArrayList , Vector , LinkedList 是 List 的实现类,ArrayList 是线程不安全的, Vector 是线程安全的,这两个类底层都是由数组实现的 。
LinkedList 是线程不安全的,底层是由链表实现的 。
Map 是键值对集合
HashTable 和 HashMap 是 Map 的实现类 。
HashTable 是线程安全的,不能存储 null 值 。
HashMap 不是线程安全的,可以存储 null 值 。
列表转数组
//第一种技术(推荐)
String[] wordA=(String[]) wordList.toArray (new String[0]);
//第二种技术
String[] wordB;
synchronized ( wordList ) {
wordB = new String[wordList.size()];
wordList.toArray(wordB);
}
//第三种技术
String[] wordc;
synchronized (wordList){
wordC = (String[]) wordList.toArray(new String[wordList.size()]);
}
序列化
变量被transient修饰,变量将不再是对象持久化的一部分。
可以实现接口Externalizable,重写其读写序列化方法。Externalizable 继承自 java.io.Serializable

1374

被折叠的 条评论
为什么被折叠?



