之前对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 extends E> 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 extends E> 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();
}
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();
之前的时候,我一直有疑问,为什么set只能用Iterator去遍历,而不能通过get(key)的方式去获得,通过上面的分析解除了这个疑惑。
想要更加熟练的应用,还需要自己平时多加练习,比如说Hashset里面的map对象,有些是hashmap,有些是Linkedhashmap,何时用hashmap,何时用linkedhashmap呢?
请在实践中证明!
如果你觉得本文章对你有用,请点个赞+关注哦~
谢谢支持!