JAVA容器之-SET

之前对set了解甚少,只知道他可以去重,今天来研究一下:

首先最基本的,用来除重

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;



public class Test {
	
	public static void main(String[] args) {
		Set set = new HashSet();
		set.add(1);
		set.add(2);
		set.add(1);
		Iterator iterator = set.iterator();
		while(iterator.hasNext()){
			System.out.println(iterator.next());
		}
	}

}

输出:

1
2

结论:添加多个重复值,不会添加(除重)

那么,set是如何实现除重的呢?看源码(以hashset为例):

/*
 * @(#)HashSet.java	1.37 06/04/21
 *
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package java.util;

/**
 * This class implements the Set interface, backed by a hash table
 * (actually a HashMap instance).  It makes no guarantees as to the
 * iteration order of the set; in particular, it does not guarantee that the
 * order will remain constant over time.  This class permits the null
 * element.
 *
 * 

This class offers constant time performance for the basic operations * (add, remove, contains and size), * assuming the hash function disperses the elements properly among the * buckets. Iterating over this set requires time proportional to the sum of * the HashSet instance's size (the number of elements) plus the * "capacity" of the backing HashMap instance (the number of * buckets). Thus, it's very important not to set the initial capacity too * high (or the load factor too low) if iteration performance is important. * *

Note that this implementation is not synchronized. * If multiple threads access a hash set concurrently, and at least one of * the threads modifies the set, it must be synchronized externally. * This is typically accomplished by synchronizing on some object that * naturally encapsulates the set. * * If no such object exists, the set should be "wrapped" using the * {@link Collections#synchronizedSet Collections.synchronizedSet} * method. This is best done at creation time, to prevent accidental * unsynchronized access to the set:

 *   Set s = Collections.synchronizedSet(new HashSet(...));
* *

The iterators returned by this class's iterator method are * fail-fast: if the set is modified at any time after the iterator is * created, in any way except through the iterator's own remove * method, the Iterator throws a {@link ConcurrentModificationException}. * Thus, in the face of concurrent modification, the iterator fails quickly * and cleanly, rather than risking arbitrary, non-deterministic behavior at * an undetermined time in the future. * *

Note that the fail-fast behavior of an iterator cannot be guaranteed * as it is, generally speaking, impossible to make any hard guarantees in the * presence of unsynchronized concurrent modification. Fail-fast iterators * throw ConcurrentModificationException on a best-effort basis. * Therefore, it would be wrong to write a program that depended on this * exception for its correctness: the fail-fast behavior of iterators * should be used only to detect bugs. * *

This class is a member of the * * Java Collections Framework. * * @param the type of elements maintained by this set * * @author Josh Bloch * @author Neal Gafter * @version 1.37, 04/21/06 * @see Collection * @see Set * @see TreeSet * @see HashMap * @since 1.2 */ public class HashSet extends AbstractSet implements Set, Cloneable, java.io.Serializable { static final long serialVersionUID = -5024744406713321676L; private transient HashMap map; // Dummy value to associate with an Object in the backing Map private static final Object PRESENT = new Object(); /** * Constructs a new, empty set; the backing HashMap instance has * default initial capacity (16) and load factor (0.75). */ public HashSet() { map = new HashMap(); } /** * Constructs a new set containing the elements in the specified * collection. The HashMap is created with default load factor * (0.75) and an initial capacity sufficient to contain the elements in * the specified collection. * * @param c the collection whose elements are to be placed into this set * @throws NullPointerException if the specified collection is null */ public HashSet(Collection c) { map = new HashMap(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); } /** * Constructs a new, empty set; the backing HashMap instance has * the specified initial capacity and the specified load factor. * * @param initialCapacity the initial capacity of the hash map * @param loadFactor the load factor of the hash map * @throws IllegalArgumentException if the initial capacity is less * than zero, or if the load factor is nonpositive */ public HashSet(int initialCapacity, float loadFactor) { map = new HashMap(initialCapacity, loadFactor); } /** * Constructs a new, empty set; the backing HashMap instance has * the specified initial capacity and default load factor (0.75). * * @param initialCapacity the initial capacity of the hash table * @throws IllegalArgumentException if the initial capacity is less * than zero */ public HashSet(int initialCapacity) { map = new HashMap(initialCapacity); } /** * Constructs a new, empty linked hash set. (This package private * constructor is only used by LinkedHashSet.) The backing * HashMap instance is a LinkedHashMap with the specified initial * capacity and the specified load factor. * * @param initialCapacity the initial capacity of the hash map * @param loadFactor the load factor of the hash map * @param dummy ignored (distinguishes this * constructor from other int, float constructor.) * @throws IllegalArgumentException if the initial capacity is less * than zero, or if the load factor is nonpositive */ HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap(initialCapacity, loadFactor); } /** * Returns an iterator over the elements in this set. The elements * are returned in no particular order. * * @return an Iterator over the elements in this set * @see ConcurrentModificationException */ public Iterator iterator() { return map.keySet().iterator(); } /** * Returns the number of elements in this set (its cardinality). * * @return the number of elements in this set (its cardinality) */ public int size() { return map.size(); } /** * Returns true if this set contains no elements. * * @return true if this set contains no elements */ public boolean isEmpty() { return map.isEmpty(); } /** * Returns true if this set contains the specified element. * More formally, returns true if and only if this set * contains an element e such that * (o==null ? e==null : o.equals(e)). * * @param o element whose presence in this set is to be tested * @return true if this set contains the specified element */ public boolean contains(Object o) { return map.containsKey(o); } /** * Adds the specified element to this set if it is not already present. * More formally, adds the specified element e to this set if * this set contains no element e2 such that * (e==null ? e2==null : e.equals(e2)). * If this set already contains the element, the call leaves the set * unchanged and returns false. * * @param e element to be added to this set * @return true if this set did not already contain the specified * element */ public boolean add(E e) { return map.put(e, PRESENT)==null; } /** * Removes the specified element from this set if it is present. * More formally, removes an element e such that * (o==null ? e==null : o.equals(e)), * if this set contains such an element. Returns true if * this set contained the element (or equivalently, if this set * changed as a result of the call). (This set will not contain the * element once the call returns.) * * @param o object to be removed from this set, if present * @return true if the set contained the specified element */ public boolean remove(Object o) { return map.remove(o)==PRESENT; } /** * Removes all of the elements from this set. * The set will be empty after this call returns. */ public void clear() { map.clear(); } /** * Returns a shallow copy of this HashSet instance: the elements * themselves are not cloned. * * @return a shallow copy of this set */ public Object clone() { try { HashSet newSet = (HashSet) super.clone(); newSet.map = (HashMap) map.clone(); return newSet; } catch (CloneNotSupportedException e) { throw new InternalError(); } } /** * Save the state of this HashSet instance to a stream (that is, * serialize it). * * @serialData The capacity of the backing HashMap instance * (int), and its load factor (float) are emitted, followed by * the size of the set (the number of elements it contains) * (int), followed by all of its elements (each an Object) in * no particular order. */ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { // Write out any hidden serialization magic s.defaultWriteObject(); // Write out HashMap capacity and load factor s.writeInt(map.capacity()); s.writeFloat(map.loadFactor()); // Write out size s.writeInt(map.size()); // Write out all elements in the proper order. for (Iterator i=map.keySet().iterator(); i.hasNext(); ) s.writeObject(i.next()); } /** * Reconstitute the HashSet instance from a stream (that is, * deserialize it). */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in any hidden serialization magic s.defaultReadObject(); // Read in HashMap capacity and load factor and create backing HashMap int capacity = s.readInt(); float loadFactor = s.readFloat(); map = (((HashSet)this) instanceof LinkedHashSet ? new LinkedHashMap(capacity, loadFactor) : new HashMap(capacity, loadFactor)); // Read in size int size = s.readInt(); // Read in all elements in the proper order. for (int i=0; i



public class HashSet
    extends AbstractSet
    implements Set, Cloneable, java.io.Serializable


从这里入手,实现了SET接口,进入SET看看:

/*
 * @(#)Set.java	1.39 06/04/21
 *
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package java.util;

/**
 * A collection that contains no duplicate elements.  More formally, sets
 * contain no pair of elements e1 and e2 such that
 * e1.equals(e2), and at most one null element.  As implied by
 * its name, this interface models the mathematical set abstraction.
 *
 * 

The Set interface places additional stipulations, beyond those * inherited from the Collection interface, on the contracts of all * constructors and on the contracts of the add, equals and * hashCode methods. Declarations for other inherited methods are * also included here for convenience. (The specifications accompanying these * declarations have been tailored to the Set interface, but they do * not contain any additional stipulations.) * *

The additional stipulation on constructors is, not surprisingly, * that all constructors must create a set that contains no duplicate elements * (as defined above). * *

Note: Great care must be exercised if mutable objects are used as set * elements. The behavior of a set is not specified if the value of an object * is changed in a manner that affects equals comparisons while the * object is an element in the set. A special case of this prohibition is * that it is not permissible for a set to contain itself as an element. * *

Some set implementations have restrictions on the elements that * they may contain. For example, some implementations prohibit null elements, * and some have restrictions on the types of their elements. Attempting to * add an ineligible element throws an unchecked exception, typically * NullPointerException or ClassCastException. Attempting * to query the presence of an ineligible element may throw an exception, * or it may simply return false; some implementations will exhibit the former * behavior and some will exhibit the latter. More generally, attempting an * operation on an ineligible element whose completion would not result in * the insertion of an ineligible element into the set may throw an * exception or it may succeed, at the option of the implementation. * Such exceptions are marked as "optional" in the specification for this * interface. * *

This interface is a member of the * * Java Collections Framework. * * @param the type of elements maintained by this set * * @author Josh Bloch * @author Neal Gafter * @version 1.39, 04/21/06 * @see Collection * @see List * @see SortedSet * @see HashSet * @see TreeSet * @see AbstractSet * @see Collections#singleton(java.lang.Object) * @see Collections#EMPTY_SET * @since 1.2 */ public interface Set extends Collection { // Query Operations /** * Returns the number of elements in this set (its cardinality). If this * set contains more than Integer.MAX_VALUE elements, returns * Integer.MAX_VALUE. * * @return the number of elements in this set (its cardinality) */ int size(); /** * Returns true if this set contains no elements. * * @return true if this set contains no elements */ boolean isEmpty(); /** * Returns true if this set contains the specified element. * More formally, returns true if and only if this set * contains an element e such that * (o==null ? e==null : o.equals(e)). * * @param o element whose presence in this set is to be tested * @return true if this set contains the specified element * @throws ClassCastException if the type of the specified element * is incompatible with this set (optional) * @throws NullPointerException if the specified element is null and this * set does not permit null elements (optional) */ boolean contains(Object o); /** * Returns an iterator over the elements in this set. The elements are * returned in no particular order (unless this set is an instance of some * class that provides a guarantee). * * @return an iterator over the elements in this set */ Iterator iterator(); /** * Returns an array containing all of the elements in this set. * If this set makes any guarantees as to what order its elements * are returned by its iterator, this method must return the * elements in the same order. * *

The returned array will be "safe" in that no references to it * are maintained by this set. (In other words, this method must * allocate a new array even if this set is backed by an array). * The caller is thus free to modify the returned array. * *

This method acts as bridge between array-based and collection-based * APIs. * * @return an array containing all the elements in this set */ Object[] toArray(); /** * Returns an array containing all of the elements in this set; the * runtime type of the returned array is that of the specified array. * If the set fits in the specified array, it is returned therein. * Otherwise, a new array is allocated with the runtime type of the * specified array and the size of this set. * *

If this set fits in the specified array with room to spare * (i.e., the array has more elements than this set), the element in * the array immediately following the end of the set is set to * null. (This is useful in determining the length of this * set only if the caller knows that this set does not contain * any null elements.) * *

If this set makes any guarantees as to what order its elements * are returned by its iterator, this method must return the elements * in the same order. * *

Like the {@link #toArray()} method, this method acts as bridge between * array-based and collection-based APIs. Further, this method allows * precise control over the runtime type of the output array, and may, * under certain circumstances, be used to save allocation costs. * *

Suppose x is a set known to contain only strings. * The following code can be used to dump the set into a newly allocated * array of String: * *

     *     String[] y = x.toArray(new String[0]);
* * Note that toArray(new Object[0]) is identical in function to * toArray(). * * @param a the array into which the elements of this set are to be * stored, if it is big enough; otherwise, a new array of the same * runtime type is allocated for this purpose. * @return an array containing all the elements in this set * @throws ArrayStoreException if the runtime type of the specified array * is not a supertype of the runtime type of every element in this * set * @throws NullPointerException if the specified array is null */ T[] toArray(T[] a); // Modification Operations /** * Adds the specified element to this set if it is not already present * (optional operation). More formally, adds the specified element * e to this set if the set contains no element e2 * such that * (e==null ? e2==null : e.equals(e2)). * If this set already contains the element, the call leaves the set * unchanged and returns false. In combination with the * restriction on constructors, this ensures that sets never contain * duplicate elements. * *

The stipulation above does not imply that sets must accept all * elements; sets may refuse to add any particular element, including * null, and throw an exception, as described in the * specification for {@link Collection#add Collection.add}. * Individual set implementations should clearly document any * restrictions on the elements that they may contain. * * @param e element to be added to this set * @return true if this set did not already contain the specified * element * @throws UnsupportedOperationException if the add operation * is not supported by this set * @throws ClassCastException if the class of the specified element * prevents it from being added to this set * @throws NullPointerException if the specified element is null and this * set does not permit null elements * @throws IllegalArgumentException if some property of the specified element * prevents it from being added to this set */ boolean add(E e); /** * Removes the specified element from this set if it is present * (optional operation). More formally, removes an element e * such that * (o==null ? e==null : o.equals(e)), if * this set contains such an element. Returns true if this set * contained the element (or equivalently, if this set changed as a * result of the call). (This set will not contain the element once the * call returns.) * * @param o object to be removed from this set, if present * @return true if this set contained the specified element * @throws ClassCastException if the type of the specified element * is incompatible with this set (optional) * @throws NullPointerException if the specified element is null and this * set does not permit null elements (optional) * @throws UnsupportedOperationException if the remove operation * is not supported by this set */ boolean remove(Object o); // Bulk Operations /** * Returns true if this set contains all of the elements of the * specified collection. If the specified collection is also a set, this * method returns true if it is a subset of this set. * * @param c collection to be checked for containment in this set * @return true if this set contains all of the elements of the * specified collection * @throws ClassCastException if the types of one or more elements * in the specified collection are incompatible with this * set (optional) * @throws NullPointerException if the specified collection contains one * or more null elements and this set does not permit null * elements (optional), or if the specified collection is null * @see #contains(Object) */ boolean containsAll(Collection c); /** * Adds all of the elements in the specified collection to this set if * they're not already present (optional operation). If the specified * collection is also a set, the addAll operation effectively * modifies this set so that its value is the union of the two * sets. The behavior of this operation is undefined if the specified * collection is modified while the operation is in progress. * * @param c collection containing elements to be added to this set * @return true if this set changed as a result of the call * * @throws UnsupportedOperationException if the addAll operation * is not supported by this set * @throws ClassCastException if the class of an element of the * specified collection prevents it from being added to this set * @throws NullPointerException if the specified collection contains one * or more null elements and this set does not permit null * elements, or if the specified collection is null * @throws IllegalArgumentException if some property of an element of the * specified collection prevents it from being added to this set * @see #add(Object) */ boolean addAll(Collection c); /** * Retains only the elements in this set that are contained in the * specified collection (optional operation). In other words, removes * from this set all of its elements that are not contained in the * specified collection. If the specified collection is also a set, this * operation effectively modifies this set so that its value is the * intersection of the two sets. * * @param c collection containing elements to be retained in this set * @return true if this set changed as a result of the call * @throws UnsupportedOperationException if the retainAll operation * is not supported by this set * @throws ClassCastException if the class of an element of this set * is incompatible with the specified collection (optional) * @throws NullPointerException if this set contains a null element and the * specified collection does not permit null elements (optional), * or if the specified collection is null * @see #remove(Object) */ boolean retainAll(Collection c); /** * Removes from this set all of its elements that are contained in the * specified collection (optional operation). If the specified * collection is also a set, this operation effectively modifies this * set so that its value is the asymmetric set difference of * the two sets. * * @param c collection containing elements to be removed from this set * @return true if this set changed as a result of the call * @throws UnsupportedOperationException if the removeAll operation * is not supported by this set * @throws ClassCastException if the class of an element of this set * is incompatible with the specified collection (optional) * @throws NullPointerException if this set contains a null element and the * specified collection does not permit null elements (optional), * or if the specified collection is null * @see #remove(Object) * @see #contains(Object) */ boolean removeAll(Collection c); /** * Removes all of the elements from this set (optional operation). * The set will be empty after this call returns. * * @throws UnsupportedOperationException if the clear method * is not supported by this set */ void clear(); // Comparison and hashing /** * Compares the specified object with this set for equality. Returns * true if the specified object is also a set, the two sets * have the same size, and every member of the specified set is * contained in this set (or equivalently, every member of this set is * contained in the specified set). This definition ensures that the * equals method works properly across different implementations of the * set interface. * * @param o object to be compared for equality with this set * @return true if the specified object is equal to this set */ boolean equals(Object o); /** * Returns the hash code value for this set. The hash code of a set is * defined to be the sum of the hash codes of the elements in the set, * where the hash code of a null element is defined to be zero. * This ensures that s1.equals(s2) implies that * s1.hashCode()==s2.hashCode() for any two sets s1 * and s2, as required by the general contract of * {@link Object#hashCode}. * * @return the hash code value for this set * @see Object#equals(Object) * @see Set#equals(Object) */ int hashCode(); }


都是一些SET需要用到的方法。没什么可说的。

AbstractSet:

/*
 * @(#)AbstractSet.java	1.29 06/04/21
 *
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package java.util;

/**
 * This class provides a skeletal implementation of the Set
 * interface to minimize the effort required to implement this
 * interface. 

* * The process of implementing a set by extending this class is identical * to that of implementing a Collection by extending AbstractCollection, * except that all of the methods and constructors in subclasses of this * class must obey the additional constraints imposed by the Set * interface (for instance, the add method must not permit addition of * multiple instances of an object to a set).

* * Note that this class does not override any of the implementations from * the AbstractCollection class. It merely adds implementations * for equals and hashCode.

* * This class is a member of the * * Java Collections Framework. * * @param the type of elements maintained by this set * * @author Josh Bloch * @author Neal Gafter * @version 1.29, 04/21/06 * @see Collection * @see AbstractCollection * @see Set * @since 1.2 */ public abstract class AbstractSet extends AbstractCollection implements Set { /** * Sole constructor. (For invocation by subclass constructors, typically * implicit.) */ protected AbstractSet() { } // Comparison and hashing /** * Compares the specified object with this set for equality. Returns * true if the given object is also a set, the two sets have * the same size, and every member of the given set is contained in * this set. This ensures that the equals method works * properly across different implementations of the Set * interface.

* * This implementation first checks if the specified object is this * set; if so it returns true. Then, it checks if the * specified object is a set whose size is identical to the size of * this set; if not, it returns false. If so, it returns * containsAll((Collection) o). * * @param o object to be compared for equality with this set * @return true if the specified object is equal to this set */ public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Set)) return false; Collection c = (Collection) o; if (c.size() != size()) return false; try { return containsAll(c); } catch (ClassCastException unused) { return false; } catch (NullPointerException unused) { return false; } } /** * Returns the hash code value for this set. The hash code of a set is * defined to be the sum of the hash codes of the elements in the set, * where the hash code of a null element is defined to be zero. * This ensures that s1.equals(s2) implies that * s1.hashCode()==s2.hashCode() for any two sets s1 * and s2, as required by the general contract of * {@link Object#hashCode}. * *

This implementation iterates over the set, calling the * hashCode method on each element in the set, and adding up * the results. * * @return the hash code value for this set * @see Object#equals(Object) * @see Set#equals(Object) */ public int hashCode() { int h = 0; Iterator i = iterator(); while (i.hasNext()) { E obj = i.next(); if (obj != null) h += obj.hashCode(); } return h; } /** * Removes from this set all of its elements that are contained in the * specified collection (optional operation). If the specified * collection is also a set, this operation effectively modifies this * set so that its value is the asymmetric set difference of * the two sets. * *

This implementation determines which is the smaller of this set * and the specified collection, by invoking the size * method on each. If this set has fewer elements, then the * implementation iterates over this set, checking each element * returned by the iterator in turn to see if it is contained in * the specified collection. If it is so contained, it is removed * from this set with the iterator's remove method. If * the specified collection has fewer elements, then the * implementation iterates over the specified collection, removing * from this set each element returned by the iterator, using this * set's remove method. * *

Note that this implementation will throw an * UnsupportedOperationException if the iterator returned by the * iterator method does not implement the remove method. * * @param c collection containing elements to be removed from this set * @return true if this set changed as a result of the call * @throws UnsupportedOperationException if the removeAll operation * is not supported by this set * @throws ClassCastException if the class of an element of this set * is incompatible with the specified collection (optional) * @throws NullPointerException if this set contains a null element and the * specified collection does not permit null elements (optional), * or if the specified collection is null * @see #remove(Object) * @see #contains(Object) */ public boolean removeAll(Collection c) { boolean modified = false; if (size() > c.size()) { for (Iterator i = c.iterator(); i.hasNext(); ) modified |= remove(i.next()); } else { for (Iterator i = iterator(); i.hasNext(); ) { if (c.contains(i.next())) { i.remove(); modified = true; } } } return modified; } }

 AbstractSet也没什么可说的。



下面说重点,看HASHSET的add()方法:

    public boolean add(E e) {
	return map.put(e, PRESENT)==null;
    }


发现了没有,set是将值作为Key存在hashmap里面,正好利用了map 的KEY不能相等的特性,可以见得设计者是十分富有灵感的!

至于这个

PRESENT

   // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();

这就是一个普通的object实例,每一个set里面都是存得这个,节约内存。


之前的时候,我一直有疑问,为什么set只能用Iterator去遍历,而不能通过get(key)的方式去获得,通过上面的分析解除了这个疑惑。


想要更加熟练的应用,还需要自己平时多加练习,比如说Hashset里面的map对象,有些是hashmap,有些是Linkedhashmap,何时用hashmap,何时用linkedhashmap呢?

请在实践中证明!


如果你觉得本文章对你有用,请点个赞+关注哦~

谢谢支持!



你可能感兴趣的:(java)