HashMap과 마찬가지로 Hashtable도 해시 테이블이고, 저장된 콘텐츠도 키-값 매핑입니다. 사전을 상속하고 Map, Cloneable, io 및 직렬화 가능 인터페이스를 구현합니다. 해시테이블은 스레드로부터 안전하며 키와 값은 비워둘 수 없으며 순서가 지정되지 않습니다.
해시테이블에는 성능에 영향을 미치는 두 가지 매개변수, 즉 초기 용량과 로딩 인자가 있습니다. 용량은 해시 테이블의 버킷 수이고, 초기 용량은 해시 테이블이 생성될 때의 용량이며, 부하율은 해시 테이블의 용량이 자동으로 증가하기 전에 해시 테이블이 얼마나 가득 찰 수 있는지를 나타내는 척도입니다. 기본 하중 계수는 0.75입니다.
해시 테이블 구조 다이어그램:
그림에서 볼 수 있듯이:
(1) Hashtable은 Dictionary 클래스를 상속하고 Map 인터페이스를 구현합니다.
(2) Hashtable은 zipper 방식(충돌에 대한 연결리스트 방식)을 통해 구현된 해시 테이블이다. 여러 중요한 구성원 변수 포함:
테이블은 Entry[]배열 유형입니다. 항목은 단방향 연결 목록이며 해시의 키-값입니다. 테이블은 모두 Entry 배열에 저장됩니다.
count는 해시테이블의 크기와 해시테이블에 저장된 키-값 쌍의 개수입니다.
Threshold는 Hashtable의 용량을 조정해야 하는지 여부를 결정하는 데 사용되는 Hashtable의 임계값입니다. 임계값 = 용량에 로딩 인자를 곱합니다.
loadFactor는 로딩 요소입니다.
modCount는 빠른 실패 메커니즘을 구현하는 데 사용됩니다.
해시테이블 순회 방법:
(1) Hashtable의 키-값 쌍 순회: 먼저 Hashtable 세트의 키-값 쌍을 얻습니다. collection , 그리고 반복자를 통해 컬렉션을 반복적으로 탐색합니다.
Integer integ = Iterator iter = table.entrySet().iterator()(iter.hasNext()) { Map.Entry entry = (Map.Entry)iter.next() key = (String)entry.getKey() integ = (Integer)entry.getValue()}
(2) Hashtable의 키 탐색: keySet()을 통해 설정된 키를 얻고, Iterator 반복 탐색을 통해 값을 얻습니다.
String key = Integer integ = Iterator iter = table.keySet().iterator()(iter.hasNext()) { key = (String)iter.next() integ = (Integer)table.get(key)}
(3) Hashtable의 값 순회: value()를 통해 Hashtable의 값 집합을 얻고, Iterator Iterator 순회를 통해 값을 얻습니다
Integer value = Collection c = table.values()Iterator iter= c.iterator()(iter.hasNext()) { value = (Integer)iter.next()}
(4) 열거를 통해 해시테이블의 키 또는 값을 탐색합니다. 먼저 키 또는 값 집합을 얻고, 열거를 통해 값을 얻습니다.
Enumeration enu = table.keys()(enu.hasMoreElements()) { System.out.println(enu.nextElement())} Enumeration enu = table.elements()(enu.hasMoreElements()) { System.out.println(enu.nextElement())}
해시 테이블 샘플 코드:
public class Hello { public static void main(String[] args) { testHashtableAPIs(); } private static void testHashtableAPIs() { // 初始化随机种子 Random r = new Random(); // 新建Hashtable Hashtable table = new Hashtable(); // 添加操作 table.put("one", r.nextInt(10)); table.put("two", r.nextInt(10)); table.put("three", r.nextInt(10)); // 打印出table System.out.println("table:"+table ); // 通过Iterator遍历key-value Iterator iter = table.entrySet().iterator(); while(iter.hasNext()) { Map.Entry entry = (Map.Entry)iter.next(); System.out.println("next : "+ entry.getKey() +" - "+entry.getValue()); } // Hashtable的键值对个数 System.out.println("size:"+table.size()); // containsKey(Object key) :是否包含键key System.out.println("contains key two : "+table.containsKey("two")); System.out.println("contains key five : "+table.containsKey("five")); // containsValue(Object value) :是否包含值value System.out.println("contains value 0 : "+table.containsValue(new Integer(0))); // remove(Object key) : 删除键key对应的键值对 table.remove("three"); System.out.println("table:"+table ); // clear() : 清空Hashtable table.clear(); // isEmpty() : Hashtable是否为空 System.out.println((table.isEmpty()?"table is empty":"table is not empty") ); } }
실행 결과:
table:{two=5, one=4, three=2} next : two - 5 next : one - 4 next : three - 2 size:3 contains key two : true contains key five : false contains value 0 : false table:{two=5, one=4} table is empty
Java8 기반 사전 소스 코드:
Dictionarya8093152e673feb7aba1828c43532094 { Dictionary() { } ()()Enumerationa8093152e673feb7aba1828c43532094 ()Enumerationa8093152e673feb7aba1828c43532094 ()(Object key)(keyvalue)(Object key)}
Java8 기반 해시테이블 소스 코드:
public class Hashtableb77a8d9c3c319e50d4b02a976b347910 extends Dictionaryb77a8d9c3c319e50d4b02a976b347910 implements Mapb77a8d9c3c319e50d4b02a976b347910, Cloneable, java.io.Serializable { /** * The hash table data. */ private transient Entryc3f2d894ed311a524f031af7191b9ddc[] table;//entry表 /** * The total number of entries in the hash table. */ private transient int count;//entry数据 /** * The table is rehashed when its size exceeds this threshold. (The * value of this field is (int)(capacity * loadFactor).) * * @serial */ private int threshold;//阈值 /** * The load factor for the hashtable. * * @serial */ private float loadFactor;//加载因子 /** * The number of times this Hashtable has been structurally modified * Structural modifications are those that change the number of entries in * the Hashtable or otherwise modify its internal structure (e.g., * rehash). This field is used to make iterators on Collection-views of * the Hashtable fail-fast. (See ConcurrentModificationException). */ private transient int modCount = 0;//fail-fast机制,记录改变的数目 /** use serialVersionUID from JDK 1.0.2 for interoperability */ private static final long serialVersionUID = 1421746759512286392L; /** * Constructs a new, empty hashtable with the specified initial * capacity and the specified load factor. * * @param initialCapacity the initial capacity of the hashtable. * @param loadFactor the load factor of the hashtable. * @exception IllegalArgumentException if the initial capacity is less * than zero, or if the load factor is nonpositive. */ //还有初始大小和加载因子的构造函数 public Hashtable(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal Load: "+loadFactor); if (initialCapacity==0) initialCapacity = 1; this.loadFactor = loadFactor; table = new Entryc3f2d894ed311a524f031af7191b9ddc[initialCapacity]; threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1); } /** * Constructs a new, empty hashtable with the specified initial capacity * and default load factor (0.75). * * @param initialCapacity the initial capacity of the hashtable. * @exception IllegalArgumentException if the initial capacity is less * than zero. */ //初始大小和默认的0.75加载因子的构造函数 public Hashtable(int initialCapacity) { this(initialCapacity, 0.75f); } /** * Constructs a new, empty hashtable with a default initial capacity (11) * and load factor (0.75). */ //使用默认的构造函数 public Hashtable() { this(11, 0.75f); } /** * Constructs a new hashtable with the same mappings as the given * Map. The hashtable is created with an initial capacity sufficient to * hold the mappings in the given Map and a default load factor (0.75). * * @param t the map whose mappings are to be placed in this map. * @throws NullPointerException if the specified map is null. * @since 1.2 */ public Hashtable(Map11df7d36ed146da2cbbceeacbc3a1d74 t) { this(Math.max(2*t.size(), 11), 0.75f); putAll(t); } /** * Returns the number of keys in this hashtable. * * @return the number of keys in this hashtable. */ //Hashtable中entry大小 public synchronized int size() { return count; } /** * Tests if this hashtable maps no keys to values. * * @return ffbe95d20f3893062224282accb13e8ftrue1cd55414ff5abdfea5dd958e7e547fdd if this hashtable maps no keys to values; * ffbe95d20f3893062224282accb13e8ffalse1cd55414ff5abdfea5dd958e7e547fdd otherwise. */ //判断是否为空 public synchronized boolean isEmpty() { return count == 0; } /** * Returns an enumeration of the keys in this hashtable. * * @return an enumeration of the keys in this hashtable. * @see Enumeration * @see #elements() * @see #keySet() * @see Map */ //key值的枚举 public synchronized Enumeration245c3adc26563b673f7297c0b3777639 keys() { return this.245c3adc26563b673f7297c0b3777639getEnumeration(KEYS); } /** * Returns an enumeration of the values in this hashtable. * Use the Enumeration methods on the returned object to fetch the elements * sequentially. * * @return an enumeration of the values in this hashtable. * @see java.util.Enumeration * @see #keys() * @see #values() * @see Map */ //value的枚举 public synchronized Enumerationd94943c0b4933ad8cac500132f64757f elements() { return this.d94943c0b4933ad8cac500132f64757fgetEnumeration(VALUES); } /** * Tests if some key maps into the specified value in this hashtable. * This operation is more expensive than the {@link #containsKey * containsKey} method. * * e388a4556c0f65e1904146cc1a846beeNote that this method is identical in functionality to * {@link #containsValue containsValue}, (which is part of the * {@link Map} interface in the collections framework). * * @param value a value to search for * @return ffbe95d20f3893062224282accb13e8ftrue1cd55414ff5abdfea5dd958e7e547fdd if and only if some key maps to the * ffbe95d20f3893062224282accb13e8fvalue1cd55414ff5abdfea5dd958e7e547fdd argument in this hashtable as * determined by the 78f983dbc27872ba42409adefe5049d9equalsd98ca7951c814b9263d12f482df06c69 method; * ffbe95d20f3893062224282accb13e8ffalse1cd55414ff5abdfea5dd958e7e547fdd otherwise. * @exception NullPointerException if the value is ffbe95d20f3893062224282accb13e8fnull1cd55414ff5abdfea5dd958e7e547fdd */ //判断是否包含某个值 public synchronized boolean contains(Object value) { if (value == null) { throw new NullPointerException(); } Entryc3f2d894ed311a524f031af7191b9ddc tab[] = table; for (int i = tab.length ; i-- > 0 ;) { for (Entryc3f2d894ed311a524f031af7191b9ddc e = tab[i] ; e != null ; e = e.next) { if (e.value.equals(value)) { return true; } } } return false; } /** * Returns true if this hashtable maps one or more keys to this value. * * e388a4556c0f65e1904146cc1a846beeNote that this method is identical in functionality to {@link * #contains contains} (which predates the {@link Map} interface). * * @param value value whose presence in this hashtable is to be tested * @return 78f983dbc27872ba42409adefe5049d9trued98ca7951c814b9263d12f482df06c69 if this map maps one or more keys to the * specified value * @throws NullPointerException if the value is ffbe95d20f3893062224282accb13e8fnull1cd55414ff5abdfea5dd958e7e547fdd * @since 1.2 */ public boolean containsValue(Object value) { return contains(value); } /** * Tests if the specified object is a key in this hashtable. * * @param key possible key * @return ffbe95d20f3893062224282accb13e8ftrue1cd55414ff5abdfea5dd958e7e547fdd if and only if the specified object * is a key in this hashtable, as determined by the * 78f983dbc27872ba42409adefe5049d9equalsd98ca7951c814b9263d12f482df06c69 method; ffbe95d20f3893062224282accb13e8ffalse1cd55414ff5abdfea5dd958e7e547fdd otherwise. * @throws NullPointerException if the key is ffbe95d20f3893062224282accb13e8fnull1cd55414ff5abdfea5dd958e7e547fdd * @see #contains(Object) */ //判断是否包含某个key public synchronized boolean containsKey(Object key) { Entryc3f2d894ed311a524f031af7191b9ddc tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; for (Entryc3f2d894ed311a524f031af7191b9ddc e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { return true; } } return false; } /** * Returns the value to which the specified key is mapped, * or {@code null} if this map contains no mapping for the key. * * e388a4556c0f65e1904146cc1a846beeMore formally, if this map contains a mapping from a key * {@code k} to a value {@code v} such that {@code (key.equals(k))}, * then this method returns {@code v}; otherwise it returns * {@code null}. (There can be at most one such mapping.) * * @param key the key whose associated value is to be returned * @return the value to which the specified key is mapped, or * {@code null} if this map contains no mapping for the key * @throws NullPointerException if the specified key is null * @see #put(Object, Object) */ //获得某个key对应的value @SuppressWarnings("unchecked") public synchronized V get(Object key) { Entryc3f2d894ed311a524f031af7191b9ddc tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; for (Entryc3f2d894ed311a524f031af7191b9ddc e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { return (V)e.value; } } return null; } /** * The maximum size of array to allocate. * Some VMs reserve some header words in an array. * Attempts to allocate larger arrays may result in * OutOfMemoryError: Requested array size exceeds VM limit */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; /** * Increases the capacity of and internally reorganizes this * hashtable, in order to accommodate and access its entries more * efficiently. This method is called automatically when the * number of keys in the hashtable exceeds this hashtable's capacity * and load factor. */ @SuppressWarnings("unchecked") protected void rehash() { int oldCapacity = table.length; Entryc3f2d894ed311a524f031af7191b9ddc[] oldMap = table; // overflow-conscious code int newCapacity = (oldCapacity 5f877ca6e95c37537536a45618cbe95c 0) { if (oldCapacity == MAX_ARRAY_SIZE) // Keep running with MAX_ARRAY_SIZE buckets return; newCapacity = MAX_ARRAY_SIZE; } Entryc3f2d894ed311a524f031af7191b9ddc[] newMap = new Entryc3f2d894ed311a524f031af7191b9ddc[newCapacity]; modCount++; threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1); table = newMap; for (int i = oldCapacity ; i-- > 0 ;) { for (Entryb77a8d9c3c319e50d4b02a976b347910 old = (Entryb77a8d9c3c319e50d4b02a976b347910)oldMap[i] ; old != null ; ) { Entryb77a8d9c3c319e50d4b02a976b347910 e = old; old = old.next; int index = (e.hash & 0x7FFFFFFF) % newCapacity; e.next = (Entryb77a8d9c3c319e50d4b02a976b347910)newMap[index]; newMap[index] = e; } } } private void addEntry(int hash, K key, V value, int index) { modCount++; Entryc3f2d894ed311a524f031af7191b9ddc tab[] = table; if (count >= threshold) { // Rehash the table if the threshold is exceeded rehash(); tab = table; hash = key.hashCode(); index = (hash & 0x7FFFFFFF) % tab.length; } // Creates the new entry. @SuppressWarnings("unchecked") Entryb77a8d9c3c319e50d4b02a976b347910 e = (Entryb77a8d9c3c319e50d4b02a976b347910) tab[index]; tab[index] = new Entrya8093152e673feb7aba1828c43532094(hash, key, value, e); count++; } /** * Maps the specified ffbe95d20f3893062224282accb13e8fkey1cd55414ff5abdfea5dd958e7e547fdd to the specified * ffbe95d20f3893062224282accb13e8fvalue1cd55414ff5abdfea5dd958e7e547fdd in this hashtable. Neither the key nor the * value can be ffbe95d20f3893062224282accb13e8fnull1cd55414ff5abdfea5dd958e7e547fdd. e388a4556c0f65e1904146cc1a846bee * * The value can be retrieved by calling the ffbe95d20f3893062224282accb13e8fget1cd55414ff5abdfea5dd958e7e547fdd method * with a key that is equal to the original key. * * @param key the hashtable key * @param value the value * @return the previous value of the specified key in this hashtable, * or ffbe95d20f3893062224282accb13e8fnull1cd55414ff5abdfea5dd958e7e547fdd if it did not have one * @exception NullPointerException if the key or value is * ffbe95d20f3893062224282accb13e8fnull1cd55414ff5abdfea5dd958e7e547fdd * @see Object#equals(Object) * @see #get(Object) */ //key和value都不为空 public synchronized V put(K key, V value) { // Make sure the value is not null if (value == null) { throw new NullPointerException(); } // Makes sure the key is not already in the hashtable. Entryc3f2d894ed311a524f031af7191b9ddc tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entryb77a8d9c3c319e50d4b02a976b347910 entry = (Entryb77a8d9c3c319e50d4b02a976b347910)tab[index]; for(; entry != null ; entry = entry.next) { if ((entry.hash == hash) && entry.key.equals(key)) { V old = entry.value; entry.value = value; return old; } } addEntry(hash, key, value, index); return null; } /** * Removes the key (and its corresponding value) from this * hashtable. This method does nothing if the key is not in the hashtable. * * @param key the key that needs to be removed * @return the value to which the key had been mapped in this hashtable, * or ffbe95d20f3893062224282accb13e8fnull1cd55414ff5abdfea5dd958e7e547fdd if the key did not have a mapping * @throws NullPointerException if the key is ffbe95d20f3893062224282accb13e8fnull1cd55414ff5abdfea5dd958e7e547fdd */ //删除某个key对应的value public synchronized V remove(Object key) { Entryc3f2d894ed311a524f031af7191b9ddc tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entryb77a8d9c3c319e50d4b02a976b347910 e = (Entryb77a8d9c3c319e50d4b02a976b347910)tab[index]; for(Entryb77a8d9c3c319e50d4b02a976b347910 prev = null ; e != null ; prev = e, e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { modCount++; if (prev != null) { prev.next = e.next; } else { tab[index] = e.next; } count--; V oldValue = e.value; e.value = null; return oldValue; } } return null; } /** * Copies all of the mappings from the specified map to this hashtable. * These mappings will replace any mappings that this hashtable had for any * of the keys currently in the specified map. * * @param t mappings to be stored in this map * @throws NullPointerException if the specified map is null * @since 1.2 */ public synchronized void putAll(Map11df7d36ed146da2cbbceeacbc3a1d74 t) { for (Map.Entry11df7d36ed146da2cbbceeacbc3a1d74 e : t.entrySet()) put(e.getKey(), e.getValue()); } /** * Clears this hashtable so that it contains no keys. */ //清空 public synchronized void clear() { Entryc3f2d894ed311a524f031af7191b9ddc tab[] = table; modCount++; for (int index = tab.length; --index >= 0; ) tab[index] = null; count = 0; } /** * Creates a shallow copy of this hashtable. All the structure of the * hashtable itself is copied, but the keys and values are not cloned. * This is a relatively expensive operation. * * @return a clone of the hashtable */ //浅拷贝 public synchronized Object clone() { try { Hashtablec3f2d894ed311a524f031af7191b9ddc t = (Hashtablec3f2d894ed311a524f031af7191b9ddc)super.clone(); t.table = new Entryc3f2d894ed311a524f031af7191b9ddc[table.length]; for (int i = table.length ; i-- > 0 ; ) { t.table[i] = (table[i] != null) ? (Entryc3f2d894ed311a524f031af7191b9ddc) table[i].clone() : null; } t.keySet = null; t.entrySet = null; t.values = null; t.modCount = 0; return t; } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(e); } } /** * Returns a string representation of this 78f983dbc27872ba42409adefe5049d9Hashtabled98ca7951c814b9263d12f482df06c69 object * in the form of a set of entries, enclosed in braces and separated * by the ASCII characters "78f983dbc27872ba42409adefe5049d9, d98ca7951c814b9263d12f482df06c69" (comma and space). Each * entry is rendered as the key, an equals sign 78f983dbc27872ba42409adefe5049d9=d98ca7951c814b9263d12f482df06c69, and the * associated element, where the 78f983dbc27872ba42409adefe5049d9toStringd98ca7951c814b9263d12f482df06c69 method is used to * convert the key and element to strings. * * @return a string representation of this hashtable */ public synchronized String toString() { int max = size() - 1; if (max == -1) return "{}"; StringBuilder sb = new StringBuilder(); Iterator<Map.Entryb77a8d9c3c319e50d4b02a976b347910> it = entrySet().iterator(); sb.append('{'); for (int i = 0; ; i++) { Map.Entryb77a8d9c3c319e50d4b02a976b347910 e = it.next(); K key = e.getKey(); V value = e.getValue(); sb.append(key == this ? "(this Map)" : key.toString()); sb.append('='); sb.append(value == this ? "(this Map)" : value.toString()); if (i == max) return sb.append('}').toString(); sb.append(", "); } } private 8742468051c85b06f0a0af9e3e506b5c Enumeration8742468051c85b06f0a0af9e3e506b5c getEnumeration(int type) { if (count == 0) { return Collections.emptyEnumeration(); } else { return new Enumeratora8093152e673feb7aba1828c43532094(type, false); } } private 8742468051c85b06f0a0af9e3e506b5c Iterator8742468051c85b06f0a0af9e3e506b5c getIterator(int type) { if (count == 0) { return Collections.emptyIterator(); } else { return new Enumeratora8093152e673feb7aba1828c43532094(type, true); } } // Views /** * Each of these fields are initialized to contain an instance of the * appropriate view the first time this view is requested. The views are * stateless, so there's no reason to create more than one of each. */ private transient volatile Set245c3adc26563b673f7297c0b3777639 keySet; private transient volatile Set<Map.Entryb77a8d9c3c319e50d4b02a976b347910> entrySet; private transient volatile Collectiond94943c0b4933ad8cac500132f64757f values; /** * Returns a {@link Set} view of the keys contained in this map. * The set is backed by the map, so changes to the map are * reflected in the set, and vice-versa. If the map is modified * while an iteration over the set is in progress (except through * the iterator's own 78f983dbc27872ba42409adefe5049d9removed98ca7951c814b9263d12f482df06c69 operation), the results of * the iteration are undefined. The set supports element removal, * which removes the corresponding mapping from the map, via the * 78f983dbc27872ba42409adefe5049d9Iterator.removed98ca7951c814b9263d12f482df06c69, 78f983dbc27872ba42409adefe5049d9Set.removed98ca7951c814b9263d12f482df06c69, * 78f983dbc27872ba42409adefe5049d9removeAlld98ca7951c814b9263d12f482df06c69, 78f983dbc27872ba42409adefe5049d9retainAlld98ca7951c814b9263d12f482df06c69, and 78f983dbc27872ba42409adefe5049d9cleard98ca7951c814b9263d12f482df06c69 * operations. It does not support the 78f983dbc27872ba42409adefe5049d9addd98ca7951c814b9263d12f482df06c69 or 78f983dbc27872ba42409adefe5049d9addAlld98ca7951c814b9263d12f482df06c69 * operations. * * @since 1.2 */ public Set245c3adc26563b673f7297c0b3777639 keySet() { if (keySet == null) keySet = Collections.synchronizedSet(new KeySet(), this); return keySet; } private class KeySet extends AbstractSet245c3adc26563b673f7297c0b3777639 { public Iterator245c3adc26563b673f7297c0b3777639 iterator() { return getIterator(KEYS); } public int size() { return count; } public boolean contains(Object o) { return containsKey(o); } public boolean remove(Object o) { return Hashtable.this.remove(o) != null; } public void clear() { Hashtable.this.clear(); } } /** * Returns a {@link Set} view of the mappings contained in this map. * The set is backed by the map, so changes to the map are * reflected in the set, and vice-versa. If the map is modified * while an iteration over the set is in progress (except through * the iterator's own 78f983dbc27872ba42409adefe5049d9removed98ca7951c814b9263d12f482df06c69 operation, or through the * 78f983dbc27872ba42409adefe5049d9setValued98ca7951c814b9263d12f482df06c69 operation on a map entry returned by the * iterator) the results of the iteration are undefined. The set * supports element removal, which removes the corresponding * mapping from the map, via the 78f983dbc27872ba42409adefe5049d9Iterator.removed98ca7951c814b9263d12f482df06c69, * 78f983dbc27872ba42409adefe5049d9Set.removed98ca7951c814b9263d12f482df06c69, 78f983dbc27872ba42409adefe5049d9removeAlld98ca7951c814b9263d12f482df06c69, 78f983dbc27872ba42409adefe5049d9retainAlld98ca7951c814b9263d12f482df06c69 and * 78f983dbc27872ba42409adefe5049d9cleard98ca7951c814b9263d12f482df06c69 operations. It does not support the * 78f983dbc27872ba42409adefe5049d9addd98ca7951c814b9263d12f482df06c69 or 78f983dbc27872ba42409adefe5049d9addAlld98ca7951c814b9263d12f482df06c69 operations. * * @since 1.2 */ public Set<Map.Entryb77a8d9c3c319e50d4b02a976b347910> entrySet() { if (entrySet==null) entrySet = Collections.synchronizedSet(new EntrySet(), this); return entrySet; } private class EntrySet extends AbstractSet<Map.Entryb77a8d9c3c319e50d4b02a976b347910> { public Iterator<Map.Entryb77a8d9c3c319e50d4b02a976b347910> iterator() { return getIterator(ENTRIES); } public boolean add(Map.Entryb77a8d9c3c319e50d4b02a976b347910 o) { return super.add(o); } public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entryc3f2d894ed311a524f031af7191b9ddc entry = (Map.Entryc3f2d894ed311a524f031af7191b9ddc)o; Object key = entry.getKey(); Entryc3f2d894ed311a524f031af7191b9ddc[] tab = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; for (Entryc3f2d894ed311a524f031af7191b9ddc e = tab[index]; e != null; e = e.next) if (e.hash==hash && e.equals(entry)) return true; return false; } public boolean remove(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entryc3f2d894ed311a524f031af7191b9ddc entry = (Map.Entryc3f2d894ed311a524f031af7191b9ddc) o; Object key = entry.getKey(); Entryc3f2d894ed311a524f031af7191b9ddc[] tab = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entryb77a8d9c3c319e50d4b02a976b347910 e = (Entryb77a8d9c3c319e50d4b02a976b347910)tab[index]; for(Entryb77a8d9c3c319e50d4b02a976b347910 prev = null; e != null; prev = e, e = e.next) { if (e.hash==hash && e.equals(entry)) { modCount++; if (prev != null) prev.next = e.next; else tab[index] = e.next; count--; e.value = null; return true; } } return false; } public int size() { return count; } public void clear() { Hashtable.this.clear(); } } /** * Returns a {@link Collection} view of the values contained in this map. * The collection is backed by the map, so changes to the map are * reflected in the collection, and vice-versa. If the map is * modified while an iteration over the collection is in progress * (except through the iterator's own 78f983dbc27872ba42409adefe5049d9removed98ca7951c814b9263d12f482df06c69 operation), * the results of the iteration are undefined. The collection * supports element removal, which removes the corresponding * mapping from the map, via the 78f983dbc27872ba42409adefe5049d9Iterator.removed98ca7951c814b9263d12f482df06c69, * 78f983dbc27872ba42409adefe5049d9Collection.removed98ca7951c814b9263d12f482df06c69, 78f983dbc27872ba42409adefe5049d9removeAlld98ca7951c814b9263d12f482df06c69, * 78f983dbc27872ba42409adefe5049d9retainAlld98ca7951c814b9263d12f482df06c69 and 78f983dbc27872ba42409adefe5049d9cleard98ca7951c814b9263d12f482df06c69 operations. It does not * support the 78f983dbc27872ba42409adefe5049d9addd98ca7951c814b9263d12f482df06c69 or 78f983dbc27872ba42409adefe5049d9addAlld98ca7951c814b9263d12f482df06c69 operations. * * @since 1.2 */ public Collectiond94943c0b4933ad8cac500132f64757f values() { if (values==null) values = Collections.synchronizedCollection(new ValueCollection(), this); return values; } private class ValueCollection extends AbstractCollectiond94943c0b4933ad8cac500132f64757f { public Iteratord94943c0b4933ad8cac500132f64757f iterator() { return getIterator(VALUES); } public int size() { return count; } public boolean contains(Object o) { return containsValue(o); } public void clear() { Hashtable.this.clear(); } } // Comparison and hashing /** * Compares the specified Object with this Map for equality, * as per the definition in the Map interface. * * @param o object to be compared for equality with this hashtable * @return true if the specified Object is equal to this Map * @see Map#equals(Object) * @since 1.2 */ public synchronized boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Map)) return false; Mapc3f2d894ed311a524f031af7191b9ddc t = (Mapc3f2d894ed311a524f031af7191b9ddc) o; if (t.size() != size()) return false; try { Iterator<Map.Entryb77a8d9c3c319e50d4b02a976b347910> i = entrySet().iterator(); while (i.hasNext()) { Map.Entryb77a8d9c3c319e50d4b02a976b347910 e = i.next(); K key = e.getKey(); V value = e.getValue(); if (value == null) { if (!(t.get(key)==null && t.containsKey(key))) return false; } else { if (!value.equals(t.get(key))) return false; } } } catch (ClassCastException unused) { return false; } catch (NullPointerException unused) { return false; } return true; } /** * Returns the hash code value for this Map as per the definition in the * Map interface. * * @see Map#hashCode() * @since 1.2 */ public synchronized int hashCode() { /* * This code detects the recursion caused by computing the hash code * of a self-referential hash table and prevents the stack overflow * that would otherwise result. This allows certain 1.1-era * applets with self-referential hash tables to work. This code * abuses the loadFactor field to do double-duty as a hashCode * in progress flag, so as not to worsen the space performance. * A negative load factor indicates that hash code computation is * in progress. */ int h = 0; if (count == 0 || loadFactor < 0) return h; // Returns zero loadFactor = -loadFactor; // Mark hashCode computation in progress Entryc3f2d894ed311a524f031af7191b9ddc[] tab = table; for (Entryc3f2d894ed311a524f031af7191b9ddc entry : tab) { while (entry != null) { h += entry.hashCode(); entry = entry.next; } } loadFactor = -loadFactor; // Mark hashCode computation complete return h; } @Override public synchronized V getOrDefault(Object key, V defaultValue) { V result = get(key); return (null == result) ? defaultValue : result; } @SuppressWarnings("unchecked") @Override public synchronized void forEach(BiConsumer8d7aa65c8046027ea338ee53f830d46e action) { Objects.requireNonNull(action); // explicit check required in case // table is empty. final int expectedModCount = modCount; Entryd43304c59d62d300b67a59666cfd3cea[] tab = table; for (Entryd43304c59d62d300b67a59666cfd3cea entry : tab) { while (entry != null) { action.accept((K)entry.key, (V)entry.value); entry = entry.next; if (expectedModCount != modCount) { throw new ConcurrentModificationException(); } } } } @SuppressWarnings("unchecked") @Override public synchronized void replaceAll(BiFunction0a1b16d994d218d93e3e331534b14776 function) { Objects.requireNonNull(function); // explicit check required in case // table is empty. final int expectedModCount = modCount; Entry81079595401ce1162abe0d5a660013d8[] tab = (Entry81079595401ce1162abe0d5a660013d8[])table; for (Entry81079595401ce1162abe0d5a660013d8 entry : tab) { while (entry != null) { entry.value = Objects.requireNonNull( function.apply(entry.key, entry.value)); entry = entry.next; if (expectedModCount != modCount) { throw new ConcurrentModificationException(); } } } } @Override public synchronized V putIfAbsent(K key, V value) { Objects.requireNonNull(value); // Makes sure the key is not already in the hashtable. Entryc3f2d894ed311a524f031af7191b9ddc tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entryb77a8d9c3c319e50d4b02a976b347910 entry = (Entryb77a8d9c3c319e50d4b02a976b347910)tab[index]; for (; entry != null; entry = entry.next) { if ((entry.hash == hash) && entry.key.equals(key)) { V old = entry.value; if (old == null) { entry.value = value; } return old; } } addEntry(hash, key, value, index); return null; } @Override public synchronized boolean remove(Object key, Object value) { Objects.requireNonNull(value); Entryc3f2d894ed311a524f031af7191b9ddc tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entryb77a8d9c3c319e50d4b02a976b347910 e = (Entryb77a8d9c3c319e50d4b02a976b347910)tab[index]; for (Entryb77a8d9c3c319e50d4b02a976b347910 prev = null; e != null; prev = e, e = e.next) { if ((e.hash == hash) && e.key.equals(key) && e.value.equals(value)) { modCount++; if (prev != null) { prev.next = e.next; } else { tab[index] = e.next; } count--; e.value = null; return true; } } return false; } @Override public synchronized boolean replace(K key, V oldValue, V newValue) { Objects.requireNonNull(oldValue); Objects.requireNonNull(newValue); Entryc3f2d894ed311a524f031af7191b9ddc tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entryb77a8d9c3c319e50d4b02a976b347910 e = (Entryb77a8d9c3c319e50d4b02a976b347910)tab[index]; for (; e != null; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { if (e.value.equals(oldValue)) { e.value = newValue; return true; } else { return false; } } } return false; } @Override public synchronized V replace(K key, V value) { Objects.requireNonNull(value); Entryc3f2d894ed311a524f031af7191b9ddc tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entryb77a8d9c3c319e50d4b02a976b347910 e = (Entryb77a8d9c3c319e50d4b02a976b347910)tab[index]; for (; e != null; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { V oldValue = e.value; e.value = value; return oldValue; } } return null; } @Override public synchronized V computeIfAbsent(K key, Functionb68c911977e56dedeb85de82f5d80518 mappingFunction) { Objects.requireNonNull(mappingFunction); Entryc3f2d894ed311a524f031af7191b9ddc tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entryb77a8d9c3c319e50d4b02a976b347910 e = (Entryb77a8d9c3c319e50d4b02a976b347910)tab[index]; for (; e != null; e = e.next) { if (e.hash == hash && e.key.equals(key)) { // Hashtable not accept null value return e.value; } } V newValue = mappingFunction.apply(key); if (newValue != null) { addEntry(hash, key, newValue, index); } return newValue; } @Override public synchronized V computeIfPresent(K key, BiFunction0a1b16d994d218d93e3e331534b14776 remappingFunction) { Objects.requireNonNull(remappingFunction); Entryc3f2d894ed311a524f031af7191b9ddc tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entryb77a8d9c3c319e50d4b02a976b347910 e = (Entryb77a8d9c3c319e50d4b02a976b347910)tab[index]; for (Entryb77a8d9c3c319e50d4b02a976b347910 prev = null; e != null; prev = e, e = e.next) { if (e.hash == hash && e.key.equals(key)) { V newValue = remappingFunction.apply(key, e.value); if (newValue == null) { modCount++; if (prev != null) { prev.next = e.next; } else { tab[index] = e.next; } count--; } else { e.value = newValue; } return newValue; } } return null; } @Override public synchronized V compute(K key, BiFunction0a1b16d994d218d93e3e331534b14776 remappingFunction) { Objects.requireNonNull(remappingFunction); Entryc3f2d894ed311a524f031af7191b9ddc tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entryb77a8d9c3c319e50d4b02a976b347910 e = (Entryb77a8d9c3c319e50d4b02a976b347910)tab[index]; for (Entryb77a8d9c3c319e50d4b02a976b347910 prev = null; e != null; prev = e, e = e.next) { if (e.hash == hash && Objects.equals(e.key, key)) { V newValue = remappingFunction.apply(key, e.value); if (newValue == null) { modCount++; if (prev != null) { prev.next = e.next; } else { tab[index] = e.next; } count--; } else { e.value = newValue; } return newValue; } } V newValue = remappingFunction.apply(key, null); if (newValue != null) { addEntry(hash, key, newValue, index); } return newValue; } @Override public synchronized V merge(K key, V value, BiFunctiona9451a6954651d01dcb928f097d6d988 remappingFunction) { Objects.requireNonNull(remappingFunction); Entryc3f2d894ed311a524f031af7191b9ddc tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entryb77a8d9c3c319e50d4b02a976b347910 e = (Entryb77a8d9c3c319e50d4b02a976b347910)tab[index]; for (Entryb77a8d9c3c319e50d4b02a976b347910 prev = null; e != null; prev = e, e = e.next) { if (e.hash == hash && e.key.equals(key)) { V newValue = remappingFunction.apply(e.value, value); if (newValue == null) { modCount++; if (prev != null) { prev.next = e.next; } else { tab[index] = e.next; } count--; } else { e.value = newValue; } return newValue; } } if (value != null) { addEntry(hash, key, value, index); } return value; } /** * Save the state of the Hashtable to a stream (i.e., serialize it). * * @serialData The 5a8028ccc7a7e27417bff9f05adf5932capacity72ac96585ae54b6ae11f849d2649d9e6 of the Hashtable (the length of the * bucket array) is emitted (int), followed by the * 5a8028ccc7a7e27417bff9f05adf5932size72ac96585ae54b6ae11f849d2649d9e6 of the Hashtable (the number of key-value * mappings), followed by the key (Object) and value (Object) * for each key-value mapping represented by the Hashtable * The key-value mappings are emitted in no particular order. */ private void writeObject(java.io.ObjectOutputStream s) throws IOException { Entry9c529c9823c5e43e5785844691736ac0 entryStack = null; synchronized (this) { // Write out the length, threshold, loadfactor s.defaultWriteObject(); // Write out length, count of elements s.writeInt(table.length); s.writeInt(count); // Stack copies of the entries in the table for (int index = 0; index < table.length; index++) { Entryc3f2d894ed311a524f031af7191b9ddc entry = table[index]; while (entry != null) { entryStack = new Entrya8093152e673feb7aba1828c43532094(0, entry.key, entry.value, entryStack); entry = entry.next; } } } // Write out the key/value objects from the stacked entries while (entryStack != null) { s.writeObject(entryStack.key); s.writeObject(entryStack.value); entryStack = entryStack.next; } } /** * Reconstitute the Hashtable from a stream (i.e., deserialize it). */ private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { // Read in the length, threshold, and loadfactor s.defaultReadObject(); // Read the original length of the array and number of elements int origlength = s.readInt(); int elements = s.readInt(); // Compute new size with a bit of room 5% to grow but // no larger than the original size. Make the length // odd if it's large enough, this helps distribute the entries. // Guard against the length ending up zero, that's not valid. int length = (int)(elements * loadFactor) + (elements / 20) + 3; if (length > elements && (length & 1) == 0) length--; if (origlength > 0 && length > origlength) length = origlength; table = new Entryc3f2d894ed311a524f031af7191b9ddc[length]; threshold = (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1); count = 0; // Read the number of elements and then all the key/value objects for (; elements > 0; elements--) { @SuppressWarnings("unchecked") K key = (K)s.readObject(); @SuppressWarnings("unchecked") V value = (V)s.readObject(); // synch could be eliminated for performance reconstitutionPut(table, key, value); } } /** * The put method used by readObject. This is provided because put * is overridable and should not be called in readObject since the * subclass will not yet be initialized. * * e388a4556c0f65e1904146cc1a846beeThis differs from the regular put method in several ways. No * checking for rehashing is necessary since the number of elements * initially in the table is known. The modCount is not incremented * because we are creating a new instance. Also, no return value * is needed. */ private void reconstitutionPut(Entryc3f2d894ed311a524f031af7191b9ddc[] tab, K key, V value) throws StreamCorruptedException { if (value == null) { throw new java.io.StreamCorruptedException(); } // Makes sure the key is not already in the hashtable. // This should not happen in deserialized version. int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; for (Entryc3f2d894ed311a524f031af7191b9ddc e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { throw new java.io.StreamCorruptedException(); } } // Creates the new entry. @SuppressWarnings("unchecked") Entryb77a8d9c3c319e50d4b02a976b347910 e = (Entryb77a8d9c3c319e50d4b02a976b347910)tab[index]; tab[index] = new Entrya8093152e673feb7aba1828c43532094(hash, key, value, e); count++; } /** * Hashtable bucket collision list entry */ private static class Entryb77a8d9c3c319e50d4b02a976b347910 implements Map.Entryb77a8d9c3c319e50d4b02a976b347910 { final int hash; final K key; V value; Entryb77a8d9c3c319e50d4b02a976b347910 next; protected Entry(int hash, K key, V value, Entryb77a8d9c3c319e50d4b02a976b347910 next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } @SuppressWarnings("unchecked") protected Object clone() { return new Entrya8093152e673feb7aba1828c43532094(hash, key, value, (next==null ? null : (Entryb77a8d9c3c319e50d4b02a976b347910) next.clone())); } // Map.Entry Ops public K getKey() { return key; } public V getValue() { return value; } public V setValue(V value) { if (value == null) throw new NullPointerException(); V oldValue = this.value; this.value = value; return oldValue; } public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entryc3f2d894ed311a524f031af7191b9ddc e = (Map.Entryc3f2d894ed311a524f031af7191b9ddc)o; return (key==null ? e.getKey()==null : key.equals(e.getKey())) && (value==null ? e.getValue()==null : value.equals(e.getValue())); } public int hashCode() { return hash ^ Objects.hashCode(value); } public String toString() { return key.toString()+"="+value.toString(); } } // Types of Enumerations/Iterations private static final int KEYS = 0; private static final int VALUES = 1; private static final int ENTRIES = 2; /** * A hashtable enumerator class. This class implements both the * Enumeration and Iterator interfaces, but inpidual instances * can be created with the Iterator methods disabled. This is necessary * to avoid unintentionally increasing the capabilities granted a user * by passing an Enumeration. */ private class Enumerator8742468051c85b06f0a0af9e3e506b5c implements Enumeration8742468051c85b06f0a0af9e3e506b5c, Iterator8742468051c85b06f0a0af9e3e506b5c { Entryc3f2d894ed311a524f031af7191b9ddc[] table = Hashtable.this.table; int index = table.length; Entryc3f2d894ed311a524f031af7191b9ddc entry; Entryc3f2d894ed311a524f031af7191b9ddc lastReturned; int type; /** * Indicates whether this Enumerator is serving as an Iterator * or an Enumeration. (true -> Iterator). */ boolean iterator; /** * The modCount value that the iterator believes that the backing * Hashtable should have. If this expectation is violated, the iterator * has detected concurrent modification. */ protected int expectedModCount = modCount; Enumerator(int type, boolean iterator) { this.type = type; this.iterator = iterator; } public boolean hasMoreElements() { Entryc3f2d894ed311a524f031af7191b9ddc e = entry; int i = index; Entryc3f2d894ed311a524f031af7191b9ddc[] t = table; /* Use locals for faster loop iteration */ while (e == null && i > 0) { e = t[--i]; } entry = e; index = i; return e != null; } @SuppressWarnings("unchecked") public T nextElement() { Entryc3f2d894ed311a524f031af7191b9ddc et = entry; int i = index; Entryc3f2d894ed311a524f031af7191b9ddc[] t = table; /* Use locals for faster loop iteration */ while (et == null && i > 0) { et = t[--i]; } entry = et; index = i; if (et != null) { Entryc3f2d894ed311a524f031af7191b9ddc e = lastReturned = entry; entry = e.next; return type == KEYS ? (T)e.key : (type == VALUES ? (T)e.value : (T)e); } throw new NoSuchElementException("Hashtable Enumerator"); } // Iterator methods public boolean hasNext() { return hasMoreElements(); } public T next() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); return nextElement(); } public void remove() { if (!iterator) throw new UnsupportedOperationException(); if (lastReturned == null) throw new IllegalStateException("Hashtable Enumerator"); if (modCount != expectedModCount) throw new ConcurrentModificationException(); synchronized(Hashtable.this) { Entryc3f2d894ed311a524f031af7191b9ddc[] tab = Hashtable.this.table; int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entryb77a8d9c3c319e50d4b02a976b347910 e = (Entryb77a8d9c3c319e50d4b02a976b347910)tab[index]; for(Entryb77a8d9c3c319e50d4b02a976b347910 prev = null; e != null; prev = e, e = e.next) { if (e == lastReturned) { modCount++; expectedModCount++; if (prev == null) tab[index] = e.next; else prev.next = e.next; count--; lastReturned = null; return; } } throw new ConcurrentModificationException(); } } } }
위 내용은 Java 컬렉션 해시테이블(그림 및 텍스트)에 대한 자세한 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!