HashMap和ArrayList如何扩容
来源:互联网 发布:看盘软件哪个最好 编辑:程序博客网 时间:2024/06/05 05:13
Java中最长用的是HashMap和ArrayList两种集合,这里介绍他们是如何扩容的。
HashMap
初始化
1.直接初始化
/** * Constructs an empty <tt>HashMap</tt> with the default initial capacity * (16) and the default load factor (0.75). */ public HashMap() { this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);}2.设置参数初始化
/** * Constructs an empty <tt>HashMap</tt> with the specified initial * capacity and load factor. * * @param initialCapacity the initial capacity * @param loadFactor the load factor * @throws IllegalArgumentException if the initial capacity is negative * or the load factor is nonpositive */ public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); this.loadFactor = loadFactor; threshold = initialCapacity; init();}3.用Map初始化
/** * Constructs a new <tt>HashMap</tt> with the same mappings as the * specified <tt>Map</tt>. The <tt>HashMap</tt> is created with * default load factor (0.75) and an initial capacity sufficient to * hold the mappings in the specified <tt>Map</tt>. * * @param m the map whose mappings are to be placed in this map * @throws NullPointerException if the specified map is null */ public HashMap(Map<? extends K, ? extends V> m) { this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR); inflateTable(this.threshold); putAllForCreate(m); },负载因子是0.75。
注意:m.size() / DEFAULT_LOAD_FACTO,再看下面的代码:
/** * Inflates the table. */ private void inflateTable(int toSize) { // Find a power of 2 >= toSize int capacity = roundUpToPowerOf2(toSize); threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1); table = new Entry[capacity]; initHashSeedAsNeeded(capacity); }所以,桶的个数是initCapcity,而阈值是capacity* this.loadFactor。表示阈值只有桶个数*loadFactor,那么痛不会满。同时,看下面添加可知,元素个数在添加过程中,也不会多余桶个数*loadFactor。
roundUpToPowerOf2其实就是取比这个值(initCapcity)大的第一个2的整数幂。
上面的代码在put方法中也有使用。
添加
/** * Associates the specified value with the specified key in this map. * If the map previously contained a mapping for the key, the old * value is replaced. * * @param key key with which the specified value is to be associated * @param value value to be associated with the specified key * @return the previous value associated with <tt>key</tt>, or * <tt>null</tt> if there was no mapping for <tt>key</tt>. * (A <tt>null</tt> return can also indicate that the map * previously associated <tt>null</tt> with <tt>key</tt>.) */ public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key == null) return putForNullKey(value); int hash = hash(key); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }最终的添加是调用addEntry操作的
/** * Adds a new entry with the specified key, value and hash code to * the specified bucket. It is the responsibility of this * method to resize the table if appropriate. * * Subclass overrides this to alter the behavior of put method. */ void addEntry(int hash, K key, V value, int bucketIndex) { if ((size >= threshold) && (null != table[bucketIndex])) { resize(2 * table.length); hash = (null != key) ? hash(key) : 0; bucketIndex = indexFor(hash, table.length); } createEntry(hash, key, value, bucketIndex); }这里可以看到,HashMap是在元素个数(不是桶的个数)达到阈值的情况下,将HashMap的的桶数翻倍的。
/** * Rehashes the contents of this map into a new array with a * larger capacity. This method is called automatically when the * number of keys in this map reaches its threshold. * * If current capacity is MAXIMUM_CAPACITY, this method does not * resize the map, but sets threshold to Integer.MAX_VALUE. * This has the effect of preventing future calls. * * @param newCapacity the new capacity, MUST be a power of two; * must be greater than current capacity unless current * capacity is MAXIMUM_CAPACITY (in which case value * is irrelevant). */ void resize(int newCapacity) { Entry[] oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; } Entry[] newTable = new Entry[newCapacity]; transfer(newTable, initHashSeedAsNeeded(newCapacity)); table = newTable; threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1); }
void transfer(Entry[] paramArrayOfEntry, boolean paramBoolean) {int i = paramArrayOfEntry.length;Entry[] arrayOfEntry = this.table;int j = arrayOfEntry.length;for (int k = 0; k < j; ++k) {Entry localEntry;for (Object localObject = arrayOfEntry[k]; null != localObject; localObject = localEntry) {localEntry = ((Entry) localObject).next;if (paramBoolean)((Entry) localObject).hash = ((null == ((Entry) localObject).key) ? 0: hash(((Entry) localObject).key));int l = indexFor(((Entry) localObject).hash, i);((Entry) localObject).next = paramArrayOfEntry[l];paramArrayOfEntry[l] = localObject;}}}可以看出,扩容后,HashMap会重新将元素按照hash算法插入新的桶中;所以平时写代码,要避免这种情况,如果能预先知道或者估计HashMap的大小,就分配足够的空间。
void createEntry(int paramInt1, K paramK, V paramV, int paramInt2) {Entry localEntry = this.table[paramInt2];this.table[paramInt2] = new Entry(paramInt1, paramK, paramV, localEntry);this.size += 1;}
这里顺带看一下hash算法。
/** * Retrieve object hash code and applies a supplemental hash function to the * result hash, which defends against poor quality hash functions. This is * critical because HashMap uses power-of-two length hash tables, that * otherwise encounter collisions for hashCodes that do not differ * in lower bits. Note: Null keys always map to hash 0, thus index 0. */ final int hash(Object k) { int h = hashSeed; if (0 != h && k instanceof String) { return sun.misc.Hashing.stringHash32((String) k); } h ^= k.hashCode(); // This function ensures that hashCodes that differ only by // constant multiples at each bit position have a bounded // number of collisions (approximately 8 at default load factor). h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); }
删除
/** * Removes and returns the entry associated with the specified key * in the HashMap. Returns null if the HashMap contains no mapping * for this key. */ final Entry<K,V> removeEntryForKey(Object key) { if (size == 0) { return null; } int hash = (key == null) ? 0 : hash(key); int i = indexFor(hash, table.length); Entry<K,V> prev = table[i]; Entry<K,V> e = prev; while (e != null) { Entry<K,V> next = e.next; Object k; if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { modCount++; size--; if (prev == e) table[i] = next; else prev.next = next; e.recordRemoval(this); return e; } prev = e; e = next; } return e; }
可见删除的时候,HashMap并没resize。
ArrayList
初始化
/** * Constructs a list containing the elements of the specified * collection, in the order they are returned by the collection's * iterator. * * @param c the collection whose elements are to be placed into this list * @throws NullPointerException if the specified collection is null */ public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); size = elementData.length; // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); }其中
/** * Copies the specified array, truncating or padding with nulls (if necessary) * so the copy has the specified length. For all indices that are * valid in both the original array and the copy, the two arrays will * contain identical values. For any indices that are valid in the * copy but not the original, the copy will contain <tt>null</tt>. * Such indices will exist if and only if the specified length * is greater than that of the original array. * The resulting array is of the class <tt>newType</tt>. * * @param original the array to be copied * @param newLength the length of the copy to be returned * @param newType the class of the copy to be returned * @return a copy of the original array, truncated or padded with nulls * to obtain the specified length * @throws NegativeArraySizeException if <tt>newLength</tt> is negative * @throws NullPointerException if <tt>original</tt> is null * @throws ArrayStoreException if an element copied from * <tt>original</tt> is not of a runtime type that can be stored in * an array of class <tt>newType</tt> * @since 1.6 */ public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) { T[] copy = ((Object)newType == (Object)Object[].class) ? (T[]) new Object[newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength); System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy; }最常用的初始化方法是
/** * Constructs an empty list with an initial capacity of ten. */ public ArrayList() { super(); this.elementData = EMPTY_ELEMENTDATA; }默认是10个大小的数组。
添加
/** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return <tt>true</tt> (as specified by {@link Collection#add}) */ public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }这是会调用ensureCapacityInternal,作用是保证数组可以容纳足够元素
private void ensureCapacityInternal(int minCapacity) { if (elementData == EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity);}private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity);}增长方法在grow中。
/** * Increases the capacity to ensure that it can hold at least the * number of elements specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity */ private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }显然,每次增长原来长度的一半。
删除
/** * Removes the element at the specified position in this list. * Shifts any subsequent elements to the left (subtracts one from their * indices). * * @param index the index of the element to be removed * @return the element that was removed from the list * @throws IndexOutOfBoundsException {@inheritDoc} */ public E remove(int index) { rangeCheck(index); modCount++; E oldValue = elementData(index); int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work return oldValue; }删除调用系统的复制功能,size会自动设置好。
1 0
- HashMap和ArrayList如何扩容
- ArrayList、Vector、HashMap、HashTable是如何扩容
- ArrayList、Vector、HashMap、HashTable是如何扩容
- ArrayList、Vector、HashMap、HashTable是如何扩容
- StringBuffer、StringBuilder、ArrayList、Vector、HashMap、HashTable是如何扩容的
- ArrayList,HashMap,LinkedList 初始化大小和 扩容机制
- ArrayList基础和扩容
- ArrayList及HashMap的扩容规则
- 集合(ArrayList,HashMap,HashSet)扩容
- HashMap与ArrayList 的扩容问题
- 遍历HashMap和ArrayList
- 浅谈JAVA中HashMap、ArrayList、StringBuilder等的扩容机制
- ArrayList扩容
- ArrayList扩容
- ArrayList的初始化和扩容解读
- ArrayList和Vector的扩容机制
- ArrayList和Vector的扩容机制
- ArrayList和Vector的扩容机制
- ndk-stack的使用
- DNA序列(uva-1368)
- pdf转换成html网页的操作方法
- Facebook产品设计总监!设计APP时的14个必考题
- java scanner 接收键盘输入 中文乱码问题
- HashMap和ArrayList如何扩容
- Android 模式 持续更新
- php array_push()函数在clone()中的应用
- POJ 3468 A Simple Problem with Integers
- MySQL架构方案
- windows内核编程基础篇之字符串的初始化
- 【POJ1733】【Parity game】【map离散化】【加权并查集模板】
- linux服务器必要的两个系统设置
- 欢迎使用CSDN-markdown编辑器