Java tutorial
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common * Development and Distribution License("CDDL") (collectively, the * "License"). You may not use this file except in compliance with the * License. You can obtain a copy of the License at * http://www.netbeans.org/cddl-gplv2.html * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the * specific language governing permissions and limitations under the * License. When distributing the software, include this License Header * Notice in each file and include the License file at * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the GPL Version 2 section of the License file that * accompanied this code. If applicable, add the following below the * License Header, with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * Contributor(s): * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. * * If you wish your version of this file to be governed by only the CDDL * or only the GPL Version 2, indicate your decision by adding * "[Contributor] elects to include this software in this distribution * under the [CDDL or GPL Version 2] license." If you do not indicate a * single choice of license, a recipient has the option to distribute * your version of this file under either the CDDL, the GPL Version 2 or * to extend the choice of license to its licensees as provided above. * However, if you add GPL Version 2 code and therefore, elected the GPL * Version 2 license, then the option applies only if the new code is * made subject to such option by the copyright holder. */ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.util.AbstractSet; import java.util.ArrayList; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.logging.Level; import java.util.logging.Logger; /** Set which holds its members by using of WeakReferences. * MT level: unsafe. * <p><strong>Note:</strong> as of JDK 6.0 (b51), you can instead use * <pre> * Set<T> s = Collections.newSetFromMap(new WeakHashMap<T, Boolean>()); * </pre> * * @author Ales Novak */ public class WeakSet<E> extends AbstractSet<E> implements Cloneable, Serializable { static final long serialVersionUID = 3062376055928236721L; /** load factor */ private float loadFactor; /** Number of items. */ private int size; /** Modification count */ private long modcount; /** Reference queue of collected weak refs */ private transient ReferenceQueue<E> refq; /** Count of <tt>null</tt> in this set */ long nullCount; /** An array of Entries */ private transient Entry<E>[] entries; transient Entry<E> iterChain; /** Constructs a new set. */ public WeakSet() { this(11, 0.75f); } /** Constructs a new set containing the elements in the specified collection. * @param c a collection to add */ public WeakSet(Collection<? extends E> c) { this(); addAll(c); } /** Constructs a new, empty set; * @param initialCapacity initial capacity */ public WeakSet(int initialCapacity) { this(initialCapacity, 0.75f); } /** Constructs a new, empty set; * * @param initialCapacity initial capacity * @param loadFactor load factor */ public WeakSet(int initialCapacity, float loadFactor) { if ((initialCapacity <= 0) || (loadFactor <= 0)) { throw new IllegalArgumentException(); } size = 0; modcount = 0; this.loadFactor = loadFactor; nullCount = 0; refq = new ReferenceQueue<E>(); entries = Entry.createArray(initialCapacity); iterChain = null; } /** * logs iterator chain (for debugging) * @param msg */ void logIterChain(String msg) { Logger log = Logger.getLogger(WeakSet.class.getName()); log.log(Level.FINE, msg); if (iterChain == null) { log.log(Level.FINE, "Empty"); return; } StringBuilder str = new StringBuilder(); Entry<E> it = iterChain; str.append(size + ": "); while (it != null) { str.append(it.get() + "(" + it.hashcode + ")" + "->"); it = it.iterChainNext; } log.log(Level.FINE, str.toString()); } /** Adds the specified element to this set if it is not already present. * * @param o an Object to add */ public boolean add(E o) { if (o == null) { size++; nullCount++; modcount++; return true; } Entry e = object2Entry(o); if (e != null) { return false; } modcount++; size++; int hash = hashIt(o); Entry<E> next = entries[hash]; iterChain = entries[hash] = new Entry<E>(this, o, refq, next, iterChain); rehash(); return true; } /** Removes all of the elements from this set. */ public void clear() { for (int i = 0; i < entries.length; i++) { entries[i] = null; } nullCount = 0; modcount++; size = 0; iterChain = null; } /** Returns a shallow copy of this WeakSet instance: the elements themselves are not cloned. */ public Object clone() { WeakSet<E> nws = new WeakSet<E>(1, loadFactor); nws.size = size; nws.nullCount = nullCount; Entry<E>[] cloned = Entry.createArray(entries.length); nws.entries = cloned; for (int i = 0; i < cloned.length; i++) { Object ref; if ((entries[i] == null) || ((ref = entries[i].get()) == null)) { cloned[i] = null; } else { cloned[i] = ((entries[i] == null) ? null : entries[i].clone(nws.refq)); ref = null; } // chains into nws iterator chain Entry<E> entry = cloned[i]; while (entry != null) { entry.chainIntoIter(nws.iterChain); nws.iterChain = entry; entry = entry.next; } } return nws; } /** Returns true if this set contains the specified element. * * @param o an Object to examine */ public boolean contains(Object o) { if (o == null) { return nullCount > 0; } return object2Entry(o) != null; } /** Returns true if this set contains no elements. */ public boolean isEmpty() { return ((nullCount == 0) && (size() == 0)); } /** Returns an iterator over the elements in this set. */ public Iterator<E> iterator() { return new WeakSetIterator(); } /** Removes the given element from this set if it is present. * * @param o an Object to remove * @return <tt>true</tt> if and only if the Object was successfuly removed. */ public boolean remove(Object o) { if (o == null) { if (nullCount > 0) { nullCount--; modcount++; size--; } return true; } Entry e = object2Entry(o); if (e != null) { modcount++; size--; e.remove(); rehash(); return true; } return false; } /** @return the number of elements in this set (its cardinality). */ public int size() { checkRefQueue(); return size; } public <T> T[] toArray(T[] array) { ArrayList<E> list = new ArrayList<E>(array.length); Iterator<E> it = iterator(); while (it.hasNext()) { list.add(it.next()); } return list.toArray(array); } public Object[] toArray() { ArrayList<E> list = new ArrayList<E>(); Iterator<E> it = iterator(); while (it.hasNext()) { list.add(it.next()); } return list.toArray(); } // #14772 public String toString() { StringBuffer buf = new StringBuffer(); Iterator e = iterator(); buf.append("["); while (e.hasNext()) { buf.append(String.valueOf(e.next())); if (e.hasNext()) { buf.append(", "); } } buf.append("]"); return buf.toString(); } /** Checks if the queue is empty if not pending weak refs are removed. */ void checkRefQueue() { for (;;) { Entry entry = Entry.class.cast(refq.poll()); if (entry == null) { break; } entry.remove(); size--; } } /** @return modcount */ long modCount() { return modcount; } /** @return an index to entries array */ int hashIt(Object o) { return (o.hashCode() & 0x7fffffff) % entries.length; } /** rehashes this Set */ void rehash() { /* float currentLF = ((float) size) / ((float) entries.length); if (currentLF < loadFactor) { return; } */ } /** @return an Entry with given object */ private Entry object2Entry(Object o) { checkRefQueue(); // clear ref q int hash = hashIt(o); Entry e = entries[hash]; if (e == null) { return null; } while ((e != null) && !e.equals(o)) { e = e.next; } return e; } private void writeObject(ObjectOutputStream obtos) throws IOException { obtos.defaultWriteObject(); obtos.writeObject(toArray()); } @SuppressWarnings("unchecked") private void readObject(ObjectInputStream obtis) throws IOException, ClassNotFoundException { obtis.defaultReadObject(); Object[] arr = (Object[]) obtis.readObject(); entries = new Entry[(int) (size * 1.5)]; refq = new ReferenceQueue<E>(); for (int i = 0; i < arr.length; i++) { add((E) arr[i]); } } class WeakSetIterator implements Iterator<E> { Entry<E> current; Entry<E> next; E currentObj; E nextObj; final long myModcount; long myNullCount; WeakSetIterator() { myModcount = modCount(); myNullCount = nullCount; current = null; next = null; Entry<E> ee = iterChain; if (ee == null) { return; } E o = ee.get(); while (ee.isEnqueued()) { ee = ee.iterChainNext; if (ee == null) { return; } o = ee.get(); } nextObj = o; next = ee; } public boolean hasNext() { checkModcount(); return ((myNullCount > 0) || (next != null)); } public E next() { checkModcount(); checkRefQueue(); if (myNullCount > 0) { myNullCount--; return null; } else { if (next == null) { throw new java.util.NoSuchElementException(); } current = next; currentObj = nextObj; // move to next requested do { next = next.iterChainNext; if (next == null) { break; } nextObj = next.get(); } while (next.isEnqueued()); return currentObj; } } public void remove() { checkModcount(); if (current == null) { throw new IllegalStateException(); } current.remove(); size--; } void checkModcount() { if (myModcount != modCount()) { throw new ConcurrentModificationException(); } } } /** Entries of this set */ static class Entry<E> extends WeakReference<E> { /** reference to outer WeakSet */ private WeakSet<E> set; // double linked list Entry<E> prev; Entry<E> next; private final int hashcode; Entry<E> iterChainNext; Entry<E> iterChainPrev; Entry(WeakSet<E> set, E referenced, ReferenceQueue<E> q, Entry<E> next, Entry<E> nextInIter) { super(referenced, q); this.set = set; this.next = next; this.prev = null; if (next != null) { next.prev = this; } if (referenced != null) { hashcode = set.hashIt(referenced); } else { hashcode = 0; } chainIntoIter(nextInIter); } @SuppressWarnings("unchecked") static final <E> Entry<E>[] createArray(int size) { return new Entry[size]; } void chainIntoIter(Entry<E> nextInIter) { iterChainNext = nextInIter; if (nextInIter != null) { nextInIter.iterChainPrev = this; } } /** deques itself */ void remove() { if (prev != null) { prev.next = next; } if (next != null) { next.prev = prev; } if (iterChainNext != null) { iterChainNext.iterChainPrev = iterChainPrev; } if (iterChainPrev != null) { iterChainPrev.iterChainNext = iterChainNext; } else { // root set.iterChain = iterChainNext; } if (set.entries[hashcode] == this) { set.entries[hashcode] = next; } prev = null; next = null; iterChainNext = null; iterChainPrev = null; } public int hashCode() { return hashcode; } public boolean equals(Object o) { Object oo = get(); if (oo == null) { return false; } else { return oo.equals(o); } } public Entry<E> clone(ReferenceQueue<E> q) { return new Entry<E>(set, get(), q, next != null ? next.clone(q) : null, null); } } }