com.google.gwt.dev.util.collect.IdentitySets Maven / Gradle / Ivy
/*
* Copyright 2009 Google Inc.
*
* Licensed 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 com.google.gwt.dev.util.collect;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.AbstractSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
/**
* Utility methods for operating on memory-efficient identity sets. All sets of
* size 0 or 1 are assumed to be immutable. All sets of size greater than 1 are
* assumed to be mutable.
*/
public class IdentitySets {
private static class IdentitySingletonSet extends AbstractSet implements
Serializable {
private final E item;
IdentitySingletonSet(E item) {
this.item = item;
}
@Override
public boolean contains(Object o) {
return o == item;
}
@Override
public Iterator iterator() {
return new SingletonIterator(item);
}
@Override
public int size() {
return 1;
}
@Override
public Object[] toArray() {
return toArray(new Object[1]);
}
@SuppressWarnings("unchecked")
@Override
public T[] toArray(T[] a) {
if (a.length < 1) {
a = (T[]) Array.newInstance(a.getClass().getComponentType(), 1);
}
a[0] = (T) item;
int i = 1;
while (i < a.length) {
a[i++] = null;
}
return a;
}
}
private static final class SingletonIterator implements Iterator {
/**
* Sentinel value to mark that this iterator's single item was consumed.
*/
private static final Object EMPTY = new Object();
private T item;
SingletonIterator(T item) {
this.item = item;
}
public boolean hasNext() {
return item != EMPTY;
}
@SuppressWarnings("unchecked")
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
T toReturn = item;
item = (T) EMPTY;
return toReturn;
}
public void remove() {
throw new UnsupportedOperationException();
}
}
private static final Class> MULTI_SET_CLASS = IdentityHashSet.class;
private static final Class> SINGLETON_SET_CLASS = IdentitySingletonSet.class;
public static Set add(Set set, T toAdd) {
switch (set.size()) {
case 0:
// Empty -> Singleton
return new IdentitySingletonSet(toAdd);
case 1: {
if (set.contains(toAdd)) {
return set;
}
// Singleton -> IdentityHashSet
Set result = new IdentityHashSet();
result.add(set.iterator().next());
result.add(toAdd);
return result;
}
default:
// IdentityHashSet
set.add(toAdd);
return set;
}
}
public static Set create() {
return Collections.emptySet();
}
public static Set create(T item) {
return new IdentitySingletonSet(item);
}
public static Set create(T... items) {
switch (items.length) {
case 0:
return create();
case 1:
return create(items[0]);
default:
IdentityHashSet result = new IdentityHashSet();
result.addAll(items);
return result;
}
}
public static Set normalize(Set set) {
switch (set.size()) {
case 0:
return create();
case 1: {
if (set.getClass() == SINGLETON_SET_CLASS) {
return set;
}
return create(set.iterator().next());
}
default:
if (set.getClass() == MULTI_SET_CLASS) {
return set;
}
IdentityHashSet result = new IdentityHashSet();
result.addAll(set);
return result;
}
}
public static Set normalizeUnmodifiable(Set set) {
if (set.size() < 2) {
return normalize(set);
} else {
// TODO: implement an UnmodifiableIdentityHashSet?
return Collections.unmodifiableSet(normalize(set));
}
}
public static Set remove(Set set, T toRemove) {
switch (set.size()) {
case 0:
// Empty
return set;
case 1:
// Singleton -> Empty
if (set.contains(toRemove)) {
return create();
}
return set;
case 2:
// IdentityHashSet -> Singleton
if (set.remove(toRemove)) {
return create(set.iterator().next());
}
return set;
default:
// IdentityHashSet
set.remove(toRemove);
return set;
}
}
}