在 Java 中,集合类如 ArrayList、HashMap 等在执行增删操作时,常常会遇到需要动态扩容的情况。理解 Java 集合的扩容机制,对于优化性能、避免内存浪费以及避免不必要的性能开销非常重要。
我们将从以下几个方面深入探讨 Java 集合扩容的原理与实践:
在 Java 中,大多数集合类(如 ArrayList、HashMap)都采用了动态扩容策略。当集合容量达到一定阈值时,它会通过扩容来保证能继续存储数据。
扩容的目的:
ArrayList 是 Java 中最常用的动态数组实现。当元素的数量超过当前容量时,ArrayList 会自动扩容。具体的扩容规则和过程如下:
ArrayList 使用一个数组来存储元素,初始化时,ArrayList 会分配一个默认的数组容量(通常为 10)。ArrayList 添加元素时,如果当前数组已满,ArrayList 会自动进行扩容。ArrayList 的容量不足时,它会将容量扩大为原容量的 1.5 倍(即容量的 50%)。public class ArrayList<E> extends AbstractList<E> implements List<E> { private Object[] elementData; // 存储数据的数组 private int size; // 当前 ArrayList 中元素的个数 private static final int DEFAULT_CAPACITY = 10; public ArrayList() { this.elementData = new Object[DEFAULT_CAPACITY]; // 默认容量为 10 } public void add(E e) { ensureCapacityInternal(size + 1); // 保证容量足够 elementData[size++] = e; } private void ensureCapacityInternal(int minCapacity) { if (elementData.length < minCapacity) { grow(minCapacity); } } private void grow(int minCapacity) { int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容 50% if (newCapacity - minCapacity < 0) { newCapacity = minCapacity; } elementData = Arrays.copyOf(elementData, newCapacity); }}php911 Bytes© 菜鸟-创作你的创作解释:
ensureCapacityInternal() 方法用来检查是否需要扩容,如果需要扩容,则调用 grow() 方法。grow() 方法将当前数组的容量增加 50%(即原容量的 1.5 倍)。ArrayList 的性能,尤其是当数据量非常大时。HashMap 是基于哈希表的集合,它的扩容机制与 ArrayList 类似,但有所不同。HashMap 中的扩容与 负载因子(load factor)和 容量(capacity)密切相关。
HashMap 通过数组存储键值对,数组中的每个元素是一个链表或树结构(在高冲突情况下)。当 负载因子 达到一定阈值时,HashMap 会进行扩容。
HashMap 会进行扩容。HashMap 扩容时,会将容量翻倍。size > capacity * load factor 时,触发扩容。public class HashMap<K, V> { private transient Entry<K, V>[] table; // 哈希表数组 private int size = 0; // 元素数量 private int threshold; // 扩容阈值 private final float loadFactor; // 负载因子 public HashMap(int initialCapacity, float loadFactor) { this.loadFactor = loadFactor; this.threshold = (int) (initialCapacity * loadFactor); // 计算扩容阈值 } public void put(K key, V value) { if (size >= threshold) { resize(); } // 哈希插入代码... } private void resize() { int newCapacity = table.length * 2; // 容量翻倍 threshold = (int) (newCapacity * loadFactor); // 重新计算阈值 table = Arrays.copyOf(table, newCapacity); // 创建新数组 // 重新散列所有元素... }}php738 Bytes© 菜鸟-创作你的创作解释:
resize() 方法在元素数量超过阈值时调用,触发容量的翻倍。threshold 会重新计算,防止容量不够。HashMap 需要重新计算每个元素的哈希值,并将它们放入新数组中。这是一个 O(n) 的操作,因此频繁扩容会影响性能。虽然扩容是一个有效的性能优化手段,但频繁的扩容会带来不必要的性能开销。以下是一些优化扩容策略的方法:
ArrayList 初始化时,如果能够预估元素的数量,可以直接指定合适的初始容量,避免不必要的扩容。List<String> list = new ArrayList<>(100); // 提前指定容量HashMap 的初始容量和负载因子,避免不必要的扩容。Map<String, Integer> map = new HashMap<>(100, 0.75f); // 设置初始容量和负载因子考虑一个需要不断添加元素的应用,假设从 1 万个元素增加到 100 万个元素。若使用默认的 ArrayList 初始化容量为 10,扩容机制会频繁发生,从而影响性能。
在 HashMap 的使用中,频繁的扩容和重新散列操作会带来额外的开销,尤其是当负载因子不合适时。可以通过调整初始容量和负载因子来减少扩容次数,提高性能。
Java 集合类的扩容机制是为了在动态增长时提供更高效的内存管理和性能优化。了解不同集合类(如 ArrayList 和 HashMap)的扩容规则和实现,可以帮助我们在开发中优化性能,减少不必要的内存开销和处理时间。
优化建议:
HashMap 的负载因子。理解这些扩容机制,能够帮助我们在大数据量处理和高并发场景下提高程序的性能和稳定性。https://www.52runoob.com/archives/5129
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。