Java集合

Arrays数组操作类

Arrays类提供查找操作数组的静态方法。

集合Collection

遍历travers和迭代iterate的区别。

Collections集合操作类

Collections类为集合提供查找排序的静态方法。

列表List

列表即数据结构中的线性表。

Vector 的所有核心方法(如 add()get()remove() 等)都使用了 synchronized 关键字修饰,确保在多线程环境下对 Vector 的操作是原子性和线程安全的 。Vector默认扩容2倍。

ArrayList默认扩容1.5倍。

ArrayDeque

ArrayDeque 是 Java 编程语言中一种‌基于数组的双端队列‌,既能当普通队列用也能当栈用,性能通常比 LinkedList 更好 。‌‌‌

用起来有什么讲究

  1. 存数据有限制‌:这个容器里‌不允许存放空值(null)‌,如果强行放入会直接报错 。‌‌‌
  2. 容量自动变化‌:
    • 初始默认容量是 16,最小不能低于 8。
    • 每次扩容都是‌变成原来的两倍‌,且总容量始终是 2 的 n 次方,这样计算位置更快 。‌‌‌
  3. 线程不安全‌:它没有内置锁,如果在多线程环境下同时修改,需要自己加锁或者用其他线程安全的类替代 。‌‌‌

常用方法怎么分

  1. 从两头插入数据‌:
    • 头部插入‌:用 addFirst 或 offerFirst,相当于把新元素塞到队伍最前面 。
    • 尾部插入‌:用 addLast 或 offerLast,相当于把新元素排到队伍最后面 。‌‌‌
  2. 从两头取出数据‌:
    • 移除并返回‌:remove 系列方法在没数据时会报错,poll 系列方法没数据时返回空值,更安全 。
    • 只看不移除‌:get 系列方法没数据时报错,peek 系列方法没数据时返回空值,适合只是想看看队头或队尾是谁的情况 。‌‌‌
  3. 当栈使用‌:
    • 直接用 push 方法压入数据,用 pop 方法弹出数据,完全符合栈的先进后出规则 。‌‌‌

跟 LinkedList 比咋样

  1. 性能更优‌:ArrayDeque 底层是数组,内存占用更紧凑,‌访问速度比 LinkedList 快‌,因为 LinkedList 每个节点还要额外存前后连接信息 。
  2. 功能覆盖‌:虽然 LinkedList 也能当双端队列用,但 ArrayDeque 是 JDK 1.6 之后专门为了优化这个场景推出的,‌官方更推荐用 ArrayDeque 代替 LinkedList 做队列或栈‌ 。
  3. 适用场景‌:如果你只需要在两头操作数据,不需要在中间插入删除,‌优先选 ArrayDeque‌;只有需要在列表中间频繁增删时,才考虑 LinkedList。‌‌‌

线程安全的List

Collections.synchronizedList 和 Vector 都是 Java 中用于实现线程安全的 List 的方式。

核心区别

  • Vector‌ 是 JDK 1.0 就提供的‌原生线程安全列表‌,其所有方法(如 add()get()remove())都使用 synchronized 关键字修饰,直接在方法级别加锁。
  • Collections.synchronizedList()‌ 是 JDK 1.2 引入的‌包装器工具‌,可以将任意 List(如 ArrayListLinkedList)转换为线程安全的版本,通过内部锁(通常是 mutex 对象)同步方法调用。

关键对比

特性VectorCollections.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 实现‌,采用"写时复制"策略,读操作无需加锁,适合读多写少的并发场景。‌‌‌

什么时候用它最合适

  1. 读操作远多于写操作‌:读取频率高、修改频率低的场景,如系统配置项、白名单/黑名单等。
  2. 遍历期间不允许抛异常‌:迭代器基于数组快照,遍历时其他线程修改不会影响当前遍历,不会抛出 ConcurrentModificationException。
  3. 数据量不大且变化少‌:集合较小、初始化后很少修改的场景,如事件监听器列表、只读数据缓存等。‌‌‌

优缺点

  1. 优点‌:
    • 读操作性能极高,无需加锁,适合高并发读取。
    • 线程安全,写操作通过锁和数组复制保证一致性。
    • 迭代安全,遍历时不会因并发修改而异常。‌‌‌
  2. 缺点‌:
    • 写操作开销大,每次修改都要复制整个数组,内存占用翻倍。
    • 数据弱一致性,读操作可能获取到旧数据,不适合强一致性要求的场景。
    • 不适合写多读少,频繁写操作会导致性能严重下降和大量垃圾回收。‌‌‌

和 ArrayList 区别

  1. 线程安全性‌:ArrayList 非线程安全,多线程修改可能数据不一致;CopyOnWriteArrayList 线程安全,支持并发读写。
  2. 内部实现‌:ArrayList 直接修改底层数组;CopyOnWriteArrayList 写操作时创建新数组副本,修改后再替换原数组引用。
  3. 迭代器行为‌: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
  1. 使用 TreeMap‌:
    TreeMap 是一个红黑树实现的 NavigableMap,它按键排序。

  2. 使用 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

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值