最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中ArrayList 和 HashMap 自動(dòng)擴(kuò)容機(jī)制詳解

 更新時(shí)間:2026年03月09日 09:04:21   作者:蕭曵 丶  
本文深入分析了Java集合框架中ArrayList和HashMap的自動(dòng)擴(kuò)容機(jī)制,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

1. ArrayList 自動(dòng)擴(kuò)容機(jī)制

1.1 核心擴(kuò)容原理

ArrayList 底層使用 Object[] 數(shù)組存儲(chǔ)元素,當(dāng)數(shù)組容量不足時(shí)自動(dòng)擴(kuò)容。

1.2 關(guān)鍵參數(shù)

  • 默認(rèn)初始容量:10
  • 擴(kuò)容因子:1.5倍(舊容量 + 舊容量右移1位)
  • 最大容量:Integer.MAX_VALUE - 8(部分VM保留頭部信息)

1.3 源碼解析

// ArrayList 擴(kuò)容核心方法
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    
    // 核心擴(kuò)容算法:新容量 = 舊容量 * 1.5
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    
    // 特殊情況處理
    if (newCapacity - minCapacity < 0) {
        newCapacity = minCapacity;
    }
    if (newCapacity - MAX_ARRAY_SIZE > 0) {
        newCapacity = hugeCapacity(minCapacity);
    }
    
    // 數(shù)組復(fù)制:時(shí)間復(fù)雜度 O(n)
    elementData = Arrays.copyOf(elementData, newCapacity);
}

// 確定最大容量
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) { // 溢出
        throw new OutOfMemoryError();
    }
    return (minCapacity > MAX_ARRAY_SIZE) ? 
           Integer.MAX_VALUE : 
           MAX_ARRAY_SIZE;
}

1.4 擴(kuò)容觸發(fā)時(shí)機(jī)

public class ArrayListExpansion {
    // 1. add() 方法觸發(fā)
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // 確保容量
        elementData[size++] = e;
        return true;
    }
    
    // 2. addAll() 方法觸發(fā)
    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // 確保容量
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }
    
    // 3. ensureCapacity() 手動(dòng)觸發(fā)
    public void ensureCapacity(int minCapacity) {
        if (minCapacity > elementData.length) {
            grow(minCapacity);
        }
    }
}

1.5 擴(kuò)容過(guò)程示例

public class ArrayListExpansionDemo {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        
        // 初始容量:10
        System.out.println("初始容量:" + getCapacity(list)); // 0(空數(shù)組)
        
        // 添加11個(gè)元素觸發(fā)擴(kuò)容
        for (int i = 0; i < 11; i++) {
            list.add(i);
            if (i == 10) {
                System.out.println("第11個(gè)元素觸發(fā)擴(kuò)容");
                System.out.println("擴(kuò)容后容量:" + getCapacity(list)); // 15
            }
        }
        
        // 繼續(xù)添加元素
        for (int i = 11; i < 23; i++) {
            list.add(i);
            if (i == 15) {
                System.out.println("第16個(gè)元素觸發(fā)擴(kuò)容");
                System.out.println("擴(kuò)容后容量:" + getCapacity(list)); // 22
            }
            if (i == 22) {
                System.out.println("第23個(gè)元素觸發(fā)擴(kuò)容");
                System.out.println("擴(kuò)容后容量:" + getCapacity(list)); // 33
            }
        }
    }
    
    // 反射獲取ArrayList實(shí)際容量(僅供演示)
    private static int getCapacity(ArrayList<?> list) {
        try {
            Field field = ArrayList.class.getDeclaredField("elementData");
            field.setAccessible(true);
            Object[] elementData = (Object[]) field.get(list);
            return elementData.length;
        } catch (Exception e) {
            return -1;
        }
    }
}

1.6 擴(kuò)容性能分析

public class ArrayListPerformance {
    /**
     * 擴(kuò)容時(shí)間復(fù)雜度:O(n)
     * 涉及數(shù)組復(fù)制,元素越多復(fù)制成本越高
     */
    
    public static void testAddPerformance() {
        int size = 1000000;
        
        // 方式1:不指定初始容量(多次擴(kuò)容)
        long start1 = System.currentTimeMillis();
        ArrayList<Integer> list1 = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            list1.add(i);
        }
        long end1 = System.currentTimeMillis();
        System.out.println("不指定容量耗時(shí):" + (end1 - start1) + "ms");
        
        // 方式2:指定初始容量(無(wú)擴(kuò)容)
        long start2 = System.currentTimeMillis();
        ArrayList<Integer> list2 = new ArrayList<>(size);
        for (int i = 0; i < size; i++) {
            list2.add(i);
        }
        long end2 = System.currentTimeMillis();
        System.out.println("指定容量耗時(shí):" + (end2 - start2) + "ms");
    }
    
    /**
     * 擴(kuò)容次數(shù)計(jì)算:
     * 10 → 15 → 22 → 33 → 49 → 73 → 109 → ...
     * 每次擴(kuò)容復(fù)制舊數(shù)組,頻繁擴(kuò)容影響性能
     */
}

2. HashMap 自動(dòng)擴(kuò)容機(jī)制

2.1 核心擴(kuò)容原理

HashMap 使用數(shù)組+鏈表/紅黑樹(shù)結(jié)構(gòu),當(dāng)元素?cái)?shù)量超過(guò)閾值時(shí)觸發(fā)擴(kuò)容。

2.2 關(guān)鍵參數(shù)

  • 默認(rèn)初始容量:16
  • 默認(rèn)負(fù)載因子:0.75
  • 擴(kuò)容閾值:容量 × 負(fù)載因子
  • 擴(kuò)容因子:2倍
  • 樹(shù)化閾值:鏈表長(zhǎng)度 ≥ 8 且數(shù)組長(zhǎng)度 ≥ 64
  • 鏈化閾值:紅黑樹(shù)節(jié)點(diǎn) ≤ 6

2.3 數(shù)據(jù)結(jié)構(gòu)演進(jìn)

數(shù)組索引: [0] -> 鏈表/紅黑樹(shù)
         [1] -> 鏈表/紅黑樹(shù)
         ...
         [n-1] -> 鏈表/紅黑樹(shù)

2.4 源碼解析

// HashMap 擴(kuò)容核心方法
final Node<K,V>[] resize() {
    Node<K,V>[] oldTab = table;
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    int oldThr = threshold;
    int newCap, newThr = 0;
    
    // 1. 計(jì)算新容量
    if (oldCap > 0) {
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }
        // 核心:新容量 = 舊容量 × 2
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY) {
            newThr = oldThr << 1; // 新閾值也×2
        }
    }
    
    // 2. 初始化閾值
    else if (oldThr > 0) {
        newCap = oldThr;
    } else {
        newCap = DEFAULT_INITIAL_CAPACITY; // 16
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); // 12
    }
    
    // 3. 創(chuàng)建新數(shù)組
    Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    table = newTab;
    threshold = newThr;
    
    // 4. 重新哈希(rehash)所有元素
    if (oldTab != null) {
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
            if ((e = oldTab[j]) != null) {
                oldTab[j] = null;
                if (e.next == null) {
                    // 單個(gè)節(jié)點(diǎn)直接計(jì)算新位置
                    newTab[e.hash & (newCap - 1)] = e;
                } else if (e instanceof TreeNode) {
                    // 紅黑樹(shù)處理
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                } else {
                    // 鏈表處理(優(yōu)化點(diǎn))
                    Node<K,V> loHead = null, loTail = null;
                    Node<K,V> hiHead = null, hiTail = null;
                    Node<K,V> next;
                    do {
                        next = e.next;
                        // 關(guān)鍵優(yōu)化:無(wú)需重新計(jì)算hash,只需判斷新增bit位
                        if ((e.hash & oldCap) == 0) {
                            // 位置不變
                            if (loTail == null) loHead = e;
                            else loTail.next = e;
                            loTail = e;
                        } else {
                            // 位置 = 原位置 + oldCap
                            if (hiTail == null) hiHead = e;
                            else hiTail.next = e;
                            hiTail = e;
                        }
                    } while ((e = next) != null);
                    
                    if (loTail != null) {
                        loTail.next = null;
                        newTab[j] = loHead;
                    }
                    if (hiTail != null) {
                        hiTail.next = null;
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}

2.5 擴(kuò)容觸發(fā)時(shí)機(jī)

public class HashMapExpansion {
    // put() 方法中的擴(kuò)容檢查
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
        // ...
        if (++size > threshold) {  // 關(guān)鍵判斷
            resize();
        }
        // ...
    }
    
    // putAll() 方法
    public void putAll(Map<? extends K, ? extends V> m) {
        // 可能會(huì)觸發(fā)擴(kuò)容
        // ...
    }
}

2.6 擴(kuò)容優(yōu)化:高位判斷

/**
 * JDK 1.8 擴(kuò)容優(yōu)化:無(wú)需重新計(jì)算hash
 * 
 * 舊容量: 16 (10000二進(jìn)制)
 * 新容量: 32 (100000二進(jìn)制)
 * 
 * 元素位置計(jì)算: hash & (capacity-1)
 * 
 * 擴(kuò)容后元素要么在原位置,要么在"原位置+舊容量"位置
 * 判斷依據(jù): (hash & oldCapacity) == 0 ?
 * 
 * 示例:
 * hash = 18 (10010) & 15(01111) = 2
 * 擴(kuò)容后: hash & 31(11111) = 18
 * 由于hash的第5位(從0開(kāi)始)是1,所以新位置 = 2 + 16 = 18
 */

2.7 擴(kuò)容過(guò)程示例

public class HashMapExpansionDemo {
    public static void main(String[] args) {
        HashMap<String, Integer> map = new HashMap<>();
        
        // 初始狀態(tài)
        System.out.println("初始容量:16,閾值:12");
        
        // 添加元素觸發(fā)擴(kuò)容
        for (int i = 0; i < 13; i++) {
            map.put("key" + i, i);
            if (i == 12) {
                System.out.println("第13個(gè)元素觸發(fā)擴(kuò)容");
                // 容量變?yōu)?2,閾值變?yōu)?4
            }
        }
        
        // 繼續(xù)添加觸發(fā)二次擴(kuò)容
        for (int i = 13; i < 25; i++) {
            map.put("key" + i, i);
            if (i == 24) {
                System.out.println("第25個(gè)元素觸發(fā)擴(kuò)容");
                // 容量變?yōu)?4,閾值變?yōu)?8
            }
        }
    }
}

2.8 樹(shù)化與鏈化

public class HashMapTreeify {
    /**
     * 樹(shù)化條件:
     * 1. 鏈表長(zhǎng)度 >= TREEIFY_THRESHOLD(8)
     * 2. 數(shù)組長(zhǎng)度 >= MIN_TREEIFY_CAPACITY(64)
     * 
     * 鏈化條件:
     * 紅黑樹(shù)節(jié)點(diǎn) <= UNTREEIFY_THRESHOLD(6)
     */
    
    public static void demonstrateTreeify() {
        HashMap<BadHashKey, Integer> map = new HashMap<>();
        
        // 創(chuàng)建大量hash沖突的key
        for (int i = 0; i < 100; i++) {
            map.put(new BadHashKey(i), i);
            // 當(dāng)鏈表長(zhǎng)度達(dá)到8且數(shù)組長(zhǎng)度>=64時(shí),鏈表轉(zhuǎn)為紅黑樹(shù)
        }
    }
    
    static class BadHashKey {
        int value;
        
        BadHashKey(int value) {
            this.value = value;
        }
        
        // 故意制造hash沖突
        @Override
        public int hashCode() {
            return value % 10; // 只有10個(gè)不同的hash值
        }
    }
}

3. 擴(kuò)容性能對(duì)比與優(yōu)化

3.1 性能對(duì)比表

特性ArrayListHashMap
擴(kuò)容觸發(fā)元素?cái)?shù)量=容量元素?cái)?shù)量>容量×負(fù)載因子
擴(kuò)容因子1.5倍2倍
時(shí)間復(fù)雜度O(n) 數(shù)組復(fù)制O(n) 重新哈希
空間復(fù)雜度臨時(shí)需要1.5倍內(nèi)存臨時(shí)需要2倍內(nèi)存
優(yōu)化策略預(yù)分配容量合適的負(fù)載因子、良好的hashCode

3.2 優(yōu)化建議

ArrayList 優(yōu)化:

public class ArrayListOptimization {
    // 1. 預(yù)分配容量(最有效優(yōu)化)
    public void optimization1() {
        // 已知需要10000個(gè)元素
        ArrayList<Integer> list = new ArrayList<>(10000);
        // 避免多次擴(kuò)容:10→15→22→33→49→73→109→...
    }
    
    // 2. 批量添加使用addAll
    public void optimization2() {
        ArrayList<Integer> list = new ArrayList<>();
        List<Integer> toAdd = Arrays.asList(1, 2, 3, 4, 5);
        
        // 一次性擴(kuò)容到位
        list.addAll(toAdd);  // 只檢查一次容量
        
        // 而不是:
        // for (Integer i : toAdd) {
        //     list.add(i);  // 可能多次檢查擴(kuò)容
        // }
    }
    
    // 3. 適時(shí)trimToSize釋放內(nèi)存
    public void optimization3() {
        ArrayList<Integer> list = new ArrayList<>(1000);
        // 添加100個(gè)元素后
        list.trimToSize(); // 釋放多余空間
    }
}

HashMap 優(yōu)化:

public class HashMapOptimization {
    // 1. 預(yù)分配容量,考慮負(fù)載因子
    public void optimization1() {
        // 預(yù)期存儲(chǔ)100個(gè)元素
        int expectedSize = 100;
        float loadFactor = 0.75f;
        
        // 計(jì)算初始容量 = expectedSize / loadFactor + 1
        int initialCapacity = (int) (expectedSize / loadFactor) + 1;
        
        HashMap<String, Integer> map = new HashMap<>(initialCapacity, loadFactor);
    }
    
    // 2. 使用合適的負(fù)載因子
    public void optimization2() {
        // 讀多寫(xiě)少:降低負(fù)載因子(減少哈希沖突)
        HashMap<String, Integer> readHeavyMap = new HashMap<>(16, 0.5f);
        
        // 寫(xiě)多讀少:提高負(fù)載因子(減少擴(kuò)容次數(shù))
        HashMap<String, Integer> writeHeavyMap = new HashMap<>(16, 0.9f);
    }
    
    // 3. 實(shí)現(xiàn)良好的hashCode和equals
    public void optimization3() {
        class GoodKey {
            String id;
            
            @Override
            public int hashCode() {
                // 良好的分布
                return id.hashCode();
            }
            
            @Override
            public boolean equals(Object obj) {
                // 正確實(shí)現(xiàn)
                if (this == obj) return true;
                if (!(obj instanceof GoodKey)) return false;
                return id.equals(((GoodKey) obj).id);
            }
        }
    }
    
    // 4. 使用LinkedHashMap保持插入順序
    public void optimization4() {
        LinkedHashMap<String, Integer> map = new LinkedHashMap<>(16, 0.75f, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry eldest) {
                // 實(shí)現(xiàn)LRU緩存
                return size() > 100;
            }
        };
    }
}

3.3 擴(kuò)容監(jiān)控與調(diào)試

public class ExpansionMonitor {
    public static void monitorArrayList() {
        ArrayList<Integer> list = new ArrayList<>();
        
        // 記錄擴(kuò)容點(diǎn)
        for (int i = 0; i < 1000; i++) {
            int oldCapacity = getCapacity(list);
            list.add(i);
            int newCapacity = getCapacity(list);
            
            if (newCapacity != oldCapacity) {
                System.out.printf("第%d個(gè)元素觸發(fā)擴(kuò)容: %d -> %d%n", 
                    i + 1, oldCapacity, newCapacity);
            }
        }
    }
    
    public static void monitorHashMap() {
        HashMap<String, Integer> map = new HashMap<>();
        
        // 使用反射監(jiān)控內(nèi)部狀態(tài)
        try {
            Field tableField = HashMap.class.getDeclaredField("table");
            tableField.setAccessible(true);
            Field thresholdField = HashMap.class.getDeclaredField("threshold");
            thresholdField.setAccessible(true);
            
            for (int i = 0; i < 100; i++) {
                int oldThreshold = (int) thresholdField.get(map);
                Object[] oldTable = (Object[]) tableField.get(map);
                int oldCapacity = oldTable == null ? 0 : oldTable.length;
                
                map.put("key" + i, i);
                
                int newThreshold = (int) thresholdField.get(map);
                Object[] newTable = (Object[]) tableField.get(map);
                int newCapacity = newTable == null ? 0 : newTable.length;
                
                if (newCapacity != oldCapacity) {
                    System.out.printf("第%d個(gè)元素觸發(fā)擴(kuò)容: 容量 %d->%d, 閾值 %d->%d%n",
                        i + 1, oldCapacity, newCapacity, oldThreshold, newThreshold);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4. 實(shí)際應(yīng)用場(chǎng)景

4.1 高并發(fā)場(chǎng)景

public class ConcurrentExpansion {
    /**
     * HashMap在并發(fā)擴(kuò)容時(shí)可能導(dǎo)致死循環(huán)(JDK 1.7之前)
     * 解決方案:
     * 1. 使用ConcurrentHashMap
     * 2. 使用Collections.synchronizedMap()
     * 3. 使用CopyOnWriteArrayList替代ArrayList
     */
    
    // 線程安全的Map
    public void safeUsage() {
        // 方案1:ConcurrentHashMap(推薦)
        ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
        
        // 方案2:同步包裝
        Map<String, Integer> synchronizedMap = 
            Collections.synchronizedMap(new HashMap<>());
        
        // 方案3:CopyOnWriteArrayList(適合讀多寫(xiě)少)
        CopyOnWriteArrayList<Integer> copyOnWriteList = new CopyOnWriteArrayList<>();
    }
}

4.2 大數(shù)據(jù)量處理

public class BigDataProcessing {
    /**
     * 處理大數(shù)據(jù)時(shí)的優(yōu)化策略
     */
    
    public void processLargeData() {
        // 場(chǎng)景:從數(shù)據(jù)庫(kù)讀取100萬(wàn)條記錄
        
        // 錯(cuò)誤做法:頻繁擴(kuò)容
        ArrayList<Record> badList = new ArrayList<>();
        // 每次add都可能觸發(fā)擴(kuò)容
        
        // 正確做法:預(yù)分配+分批處理
        int totalRecords = 1000000;
        int batchSize = 10000;
        
        ArrayList<Record> goodList = new ArrayList<>(batchSize);
        
        for (int i = 0; i < totalRecords; i++) {
            Record record = fetchRecord(i);
            goodList.add(record);
            
            // 分批處理
            if (goodList.size() >= batchSize) {
                processBatch(goodList);
                goodList.clear();  // 重用ArrayList
                // 注意:clear()只清空元素,不改變?nèi)萘?
            }
        }
    }
}

4.3 緩存實(shí)現(xiàn)

public class LRUCache<K, V> extends LinkedHashMap<K, V> {
    private final int maxSize;
    
    public LRUCache(int maxSize) {
        // 設(shè)置初始容量和負(fù)載因子
        super((int) (maxSize / 0.75f) + 1, 0.75f, true);
        this.maxSize = maxSize;
    }
    
    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > maxSize;
    }
    
    /**
     * 擴(kuò)容考慮:
     * 1. maxSize應(yīng)小于初始容量×負(fù)載因子,避免put時(shí)擴(kuò)容
     * 2. accessOrder=true開(kāi)啟訪問(wèn)順序
     */
}

5. 總結(jié)

5.1 關(guān)鍵差異

方面ArrayListHashMap
擴(kuò)容觸發(fā)size == capacitysize > capacity × loadFactor
擴(kuò)容倍數(shù)1.5倍2倍
性能影響數(shù)組復(fù)制 O(n)重新哈希 O(n)
內(nèi)存使用連續(xù)內(nèi)存分散內(nèi)存+鏈表/樹(shù)節(jié)點(diǎn)
線程安全非線程安全非線程安全(并發(fā)問(wèn)題)

5.2 最佳實(shí)踐

  • 預(yù)分配容量:已知數(shù)據(jù)量時(shí),初始化時(shí)指定合適容量
  • 監(jiān)控?cái)U(kuò)容:大數(shù)據(jù)量時(shí)監(jiān)控?cái)U(kuò)容頻率,優(yōu)化性能
  • 合理選擇結(jié)構(gòu):根據(jù)使用場(chǎng)景選擇合適的數(shù)據(jù)結(jié)構(gòu)
  • 并發(fā)安全:多線程環(huán)境使用線程安全版本
  • 內(nèi)存優(yōu)化:適時(shí)釋放未使用容量

5.3 擴(kuò)容性能公式

// ArrayList 擴(kuò)容次數(shù)估算
int expansions = 0;
int capacity = 10;
while (capacity < requiredSize) {
    capacity = capacity + (capacity >> 1); // ×1.5
    expansions++;
}

// HashMap 擴(kuò)容次數(shù)估算
int expansions = 0;
int capacity = 16;
while (capacity * 0.75 < requiredSize) {
    capacity <<= 1; // ×2
    expansions++;
}

到此這篇關(guān)于Java中ArrayList 和 HashMap 自動(dòng)擴(kuò)容機(jī)制詳解的文章就介紹到這了,更多相關(guān)Java ArrayList 和 HashMap 自動(dòng)擴(kuò)容 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

呼玛县| 阿鲁科尔沁旗| 安乡县| 连州市| 隆化县| 洮南市| 高淳县| 五台县| 紫云| 桃园县| 买车| 通渭县| 日喀则市| 孟州市| 石渠县| 鹰潭市| 翁牛特旗| 乌鲁木齐县| 怀远县| 黔南| 琼结县| 石棉县| 黑河市| 潼关县| 连州市| 普洱| 祁阳县| 大庆市| 正宁县| 潜山县| 开阳县| 成安县| 莒南县| 府谷县| 无锡市| 永兴县| 马关县| 广南县| 莫力| 邳州市| 郑州市|