Path: blob/master/src/java.desktop/share/classes/java/beans/WeakIdentityMap.java
41152 views
/*1* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.beans;2627import java.lang.ref.ReferenceQueue;28import java.lang.ref.WeakReference;2930/**31* Hash table based mapping, which uses weak references to store keys32* and reference-equality in place of object-equality to compare them.33* An entry will automatically be removed when its key is no longer34* in ordinary use. Both null values and the null key are supported.35* This class does not require additional synchronization.36* A thread-safety is provided by a fragile combination37* of synchronized blocks and volatile fields.38* Be very careful during editing!39*40* @see java.util.IdentityHashMap41* @see java.util.WeakHashMap42*/43abstract class WeakIdentityMap<T> {4445private static final int MAXIMUM_CAPACITY = 1 << 30; // it MUST be a power of two46private static final Object NULL = new Object(); // special object for null key4748private final ReferenceQueue<Object> queue = new ReferenceQueue<Object>();4950private volatile Entry<T>[] table = newTable(1<<3); // table's length MUST be a power of two51private int threshold = 6; // the next size value at which to resize52private int size = 0; // the number of key-value mappings5354public T get(Object key) {55removeStaleEntries();56if (key == null) {57key = NULL;58}59int hash = key.hashCode();60Entry<T>[] table = this.table;61// unsynchronized search improves performance62// the null value does not mean that there are no needed entry63int index = getIndex(table, hash);64for (Entry<T> entry = table[index]; entry != null; entry = entry.next) {65if (entry.isMatched(key, hash)) {66return entry.value;67}68}69synchronized (NULL) {70// synchronized search improves stability71// we must create and add new value if there are no needed entry72index = getIndex(this.table, hash);73for (Entry<T> entry = this.table[index]; entry != null; entry = entry.next) {74if (entry.isMatched(key, hash)) {75return entry.value;76}77}78T value = create(key);79this.table[index] = new Entry<T>(key, hash, value, this.queue, this.table[index]);80if (++this.size >= this.threshold) {81if (this.table.length == MAXIMUM_CAPACITY) {82this.threshold = Integer.MAX_VALUE;83}84else {85removeStaleEntries();86table = newTable(this.table.length * 2);87transfer(this.table, table);88// If ignoring null elements and processing ref queue caused massive89// shrinkage, then restore old table. This should be rare, but avoids90// unbounded expansion of garbage-filled tables.91if (this.size >= this.threshold / 2) {92this.table = table;93this.threshold *= 2;94}95else {96transfer(table, this.table);97}98}99}100return value;101}102}103104protected abstract T create(Object key);105106private void removeStaleEntries() {107Object ref = this.queue.poll();108if (ref != null) {109synchronized (NULL) {110do {111@SuppressWarnings("unchecked")112Entry<T> entry = (Entry<T>) ref;113int index = getIndex(this.table, entry.hash);114115Entry<T> prev = this.table[index];116Entry<T> current = prev;117while (current != null) {118Entry<T> next = current.next;119if (current == entry) {120if (prev == entry) {121this.table[index] = next;122}123else {124prev.next = next;125}126entry.value = null; // Help GC127entry.next = null; // Help GC128this.size--;129break;130}131prev = current;132current = next;133}134ref = this.queue.poll();135}136while (ref != null);137}138}139}140141private void transfer(Entry<T>[] oldTable, Entry<T>[] newTable) {142for (int i = 0; i < oldTable.length; i++) {143Entry<T> entry = oldTable[i];144oldTable[i] = null;145while (entry != null) {146Entry<T> next = entry.next;147Object key = entry.get();148if (key == null) {149entry.value = null; // Help GC150entry.next = null; // Help GC151this.size--;152}153else {154int index = getIndex(newTable, entry.hash);155entry.next = newTable[index];156newTable[index] = entry;157}158entry = next;159}160}161}162163164@SuppressWarnings("unchecked")165private Entry<T>[] newTable(int length) {166return (Entry<T>[]) new Entry<?>[length];167}168169private static int getIndex(Entry<?>[] table, int hash) {170return hash & (table.length - 1);171}172173private static class Entry<T> extends WeakReference<Object> {174private final int hash;175private volatile T value;176private volatile Entry<T> next;177178Entry(Object key, int hash, T value, ReferenceQueue<Object> queue, Entry<T> next) {179super(key, queue);180this.hash = hash;181this.value = value;182this.next = next;183}184185boolean isMatched(Object key, int hash) {186return (this.hash == hash) && (key == get());187}188}189}190191192