/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java.util;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* HashSet is an implementation of a Set. All optional operations (adding and
* removing) are supported. The elements can be any objects.
*/
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable,
Serializable {
private static final long serialVersionUID = -5024744406713321676L;
transient HashMap<E, HashSet<E>> backingMap;
/**
* Constructs a new empty instance of {@code HashSet}.
*/
public HashSet() {
this(new HashMap<E, HashSet<E>>());
}
/**
* Constructs a new instance of {@code HashSet} with the specified capacity.
*
* @param capacity
* the initial capacity of this {@code HashSet}.
*/
public HashSet(int capacity) {
this(new HashMap<E, HashSet<E>>(capacity));
}
/**
* Constructs a new instance of {@code HashSet} with the specified capacity
* and load factor.
*
* @param capacity
* the initial capacity.
* @param loadFactor
* the initial load factor.
*/
public HashSet(int capacity, float loadFactor) {
this(new HashMap<E, HashSet<E>>(capacity, loadFactor));
}
/**
* Constructs a new instance of {@code HashSet} containing the unique
* elements in the specified collection.
*
* @param collection
* the collection of elements to add.
*/
public HashSet(Collection<? extends E> collection) {
this(new HashMap<E, HashSet<E>>(collection.size() < 6 ? 11 : collection
.size() * 2));
for (E e : collection) {
add(e);
}
}
HashSet(HashMap<E, HashSet<E>> backingMap) {
this.backingMap = backingMap;
}
/**
* Adds the specified object to this {@code HashSet} if not already present.
*
* @param object
* the object to add.
* @return {@code true} when this {@code HashSet} did not already contain
* the object, {@code false} otherwise
*/
@Override
public boolean add(E object) {
return backingMap.put(object, this) == null;
}
/**
* Removes all elements from this {@code HashSet}, leaving it empty.
*
* @see #isEmpty
* @see #size
*/
@Override
public void clear() {
backingMap.clear();
}
/**
* Returns a new {@code HashSet} with the same elements and size as this
* {@code HashSet}.
*
* @return a shallow copy of this {@code HashSet}.
* @see java.lang.Cloneable
*/
@Override
@SuppressWarnings("unchecked")
public Object clone() {
try {
HashSet<E> clone = (HashSet<E>) super.clone();
clone.backingMap = (HashMap<E, HashSet<E>>) backingMap.clone();
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError(e);
}
}
/**
* Searches this {@code HashSet} for the specified object.
*
* @param object
* the object to search for.
* @return {@code true} if {@code object} is an element of this
* {@code HashSet}, {@code false} otherwise.
*/
@Override
public boolean contains(Object object) {
return backingMap.containsKey(object);
}
/**
* Returns true if this {@code HashSet} has no elements, false otherwise.
*
* @return {@code true} if this {@code HashSet} has no elements,
* {@code false} otherwise.
* @see #size
*/
@Override
public boolean isEmpty() {
return backingMap.isEmpty();
}
/**
* Returns an Iterator on the elements of this {@code HashSet}.
*
* @return an Iterator on the elements of this {@code HashSet}.
* @see Iterator
*/
@Override
public Iterator<E> iterator() {
return backingMap.keySet().iterator();
}
/**
* Removes the specified object from this {@code HashSet}.
*
* @param object
* the object to remove.
* @return {@code true} if the object was removed, {@code false} otherwise.
*/
@Override
public boolean remove(Object object) {
return backingMap.remove(object) != null;
}
/**
* Returns the number of elements in this {@code HashSet}.
*
* @return the number of elements in this {@code HashSet}.
*/
@Override
public int size() {
return backingMap.size();
}
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
stream.writeInt(backingMap.table.length);
stream.writeFloat(HashMap.DEFAULT_LOAD_FACTOR);
stream.writeInt(size());
for (E e : this) {
stream.writeObject(e);
}
}
@SuppressWarnings("unchecked")
private void readObject(ObjectInputStream stream) throws IOException,
ClassNotFoundException {
stream.defaultReadObject();
int length = stream.readInt();
float loadFactor = stream.readFloat();
backingMap = createBackingMap(length, loadFactor);
int elementCount = stream.readInt();
for (int i = elementCount; --i >= 0;) {
E key = (E) stream.readObject();
backingMap.put(key, this);
}
}
HashMap<E, HashSet<E>> createBackingMap(int capacity, float loadFactor) {
return new HashMap<E, HashSet<E>>(capacity, loadFactor);
}
}