Speed up JRealClassType.getSubTypes() by implementing an optimized toArray() in our set classes.
Review by: spoon (desk)
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5341 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java
index 8430ccd..baec501 100644
--- a/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java
+++ b/dev/core/src/com/google/gwt/core/ext/typeinfo/JRealClassType.java
@@ -15,8 +15,8 @@
*/
package com.google.gwt.core.ext.typeinfo;
+import com.google.gwt.dev.util.collect.IdentitySets;
import com.google.gwt.dev.util.collect.Lists;
-import com.google.gwt.dev.util.collect.Sets;
import java.lang.annotation.Annotation;
import java.util.List;
@@ -28,7 +28,7 @@
*/
public class JRealClassType extends JClassType {
- private Set<JClassType> allSubtypes = Sets.create();
+ private Set<JClassType> allSubtypes = IdentitySets.create();
private final Annotations annotations = new Annotations();
@@ -410,8 +410,7 @@
}
protected void acceptSubtype(JClassType me) {
- // TODO(scottb): revisit
- allSubtypes = Sets.add(allSubtypes, me);
+ allSubtypes = IdentitySets.add(allSubtypes, me);
notifySuperTypesOf(me);
}
@@ -477,8 +476,7 @@
}
protected void removeSubtype(JClassType me) {
- // TODO(scottb): revisit
- allSubtypes = Sets.remove(allSubtypes, me);
+ allSubtypes = IdentitySets.remove(allSubtypes, me);
if (superclass != null) {
superclass.removeSubtype(me);
diff --git a/dev/core/src/com/google/gwt/dev/util/collect/HashMap.java b/dev/core/src/com/google/gwt/dev/util/collect/HashMap.java
index c643d9f..0e7845d 100644
--- a/dev/core/src/com/google/gwt/dev/util/collect/HashMap.java
+++ b/dev/core/src/com/google/gwt/dev/util/collect/HashMap.java
@@ -408,11 +408,11 @@
}
};
- private static Object maskNullKey(Object k) {
+ static Object maskNullKey(Object k) {
return (k == null) ? NULL_KEY : k;
}
- private static Object unmaskNullKey(Object k) {
+ static Object unmaskNullKey(Object k) {
return (k == NULL_KEY) ? null : k;
}
diff --git a/dev/core/src/com/google/gwt/dev/util/collect/HashSet.java b/dev/core/src/com/google/gwt/dev/util/collect/HashSet.java
index 58dcc29..8387a35 100644
--- a/dev/core/src/com/google/gwt/dev/util/collect/HashSet.java
+++ b/dev/core/src/com/google/gwt/dev/util/collect/HashSet.java
@@ -19,6 +19,7 @@
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
+import java.lang.reflect.Array;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
@@ -31,14 +32,6 @@
*/
public class HashSet<E> extends AbstractSet<E> implements Serializable {
- /**
- * In the interest of memory-savings, we start with the smallest feasible
- * power-of-two table size that can hold three items without rehashing. If we
- * started with a size of 2, we'd have to expand as soon as the second item
- * was added.
- */
- private static final int INITIAL_TABLE_SIZE = 4;
-
private class SetIterator implements Iterator<E> {
private int index = 0;
private int last = -1;
@@ -82,17 +75,25 @@
}
}
+ /**
+ * In the interest of memory-savings, we start with the smallest feasible
+ * power-of-two table size that can hold three items without rehashing. If we
+ * started with a size of 2, we'd have to expand as soon as the second item
+ * was added.
+ */
+ private static final int INITIAL_TABLE_SIZE = 4;
+
private static final Object NULL_ITEM = new Serializable() {
Object readResolve() {
return NULL_ITEM;
}
};
- private static Object maskNull(Object o) {
+ static Object maskNull(Object o) {
return (o == null) ? NULL_ITEM : o;
}
- private static Object unmaskNull(Object o) {
+ static Object unmaskNull(Object o) {
return (o == NULL_ITEM) ? null : o;
}
@@ -172,6 +173,30 @@
return size;
}
+ @Override
+ public Object[] toArray() {
+ return toArray(new Object[size]);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> T[] toArray(T[] a) {
+ if (a.length < size) {
+ a = (T[]) Array.newInstance(a.getClass().getComponentType(), size);
+ }
+ int index = 0;
+ for (int i = 0; i < table.length; ++i) {
+ Object e = table[i];
+ if (e != null) {
+ a[index++] = (T) unmaskNull(e);
+ }
+ }
+ while (index < a.length) {
+ a[index++] = null;
+ }
+ return a;
+ }
+
/**
* Adapted from {@link org.apache.commons.collections.map.AbstractHashedMap}.
*/
diff --git a/dev/core/src/com/google/gwt/dev/util/collect/IdentitySets.java b/dev/core/src/com/google/gwt/dev/util/collect/IdentitySets.java
index 6c1f989..8791f28 100644
--- a/dev/core/src/com/google/gwt/dev/util/collect/IdentitySets.java
+++ b/dev/core/src/com/google/gwt/dev/util/collect/IdentitySets.java
@@ -16,6 +16,7 @@
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;
@@ -29,12 +30,12 @@
*/
public class IdentitySets {
- private static class IdentitySingletonSet<T> extends AbstractSet<T> implements
+ private static class IdentitySingletonSet<E> extends AbstractSet<E> implements
Serializable {
- private final T item;
+ private final E item;
- IdentitySingletonSet(T item) {
+ IdentitySingletonSet(E item) {
this.item = item;
}
@@ -42,15 +43,33 @@
return o == item;
}
- public Iterator<T> iterator() {
- return new SingletonIterator<T>(item);
+ public Iterator<E> iterator() {
+ return new SingletonIterator<E>(item);
}
public int size() {
return 1;
}
- }
+ @Override
+ public Object[] toArray() {
+ return toArray(new Object[1]);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> 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<T> implements Iterator<T> {
/**
@@ -83,6 +102,10 @@
}
}
+ private static final Class<?> MULTI_SET_CLASS = IdentityHashSet.class;
+
+ private static final Class<?> SINGLETON_SET_CLASS = IdentitySingletonSet.class;
+
public static <T> Set<T> add(Set<T> set, T toAdd) {
switch (set.size()) {
case 0:
@@ -105,17 +128,76 @@
}
}
+ public static <T> Set<T> create() {
+ return Collections.emptySet();
+ }
+
+ public static <T> Set<T> create(T item) {
+ return new IdentitySingletonSet<T>(item);
+ }
+
+ public static <T> Set<T> create(T... items) {
+ switch (items.length) {
+ case 0:
+ return create();
+ case 1:
+ return create(items[0]);
+ default:
+ IdentityHashSet<T> result = new IdentityHashSet<T>();
+ result.addAll(items);
+ return result;
+ }
+ }
+
public static <T> Set<T> normalize(Set<T> set) {
switch (set.size()) {
case 0:
- return Collections.emptySet();
+ return create();
case 1: {
- if (set.getClass() != IdentitySingletonSet.class) {
- return new IdentitySingletonSet<T>(set.iterator().next());
+ if (set.getClass() == SINGLETON_SET_CLASS) {
+ return set;
}
- return set;
+ return create(set.iterator().next());
}
default:
+ if (set.getClass() == MULTI_SET_CLASS) {
+ return set;
+ }
+ IdentityHashSet<T> result = new IdentityHashSet<T>();
+ result.addAll(set);
+ return result;
+ }
+ }
+
+ public static <T> Set<T> normalizeUnmodifiable(Set<T> set) {
+ if (set.size() < 2) {
+ return normalize(set);
+ } else {
+ // TODO: implement an UnmodifiableIdentityHashSet?
+ return Collections.unmodifiableSet(normalize(set));
+ }
+ }
+
+ public static <T> Set<T> remove(Set<T> 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;
}
}
diff --git a/dev/core/src/com/google/gwt/dev/util/collect/Sets.java b/dev/core/src/com/google/gwt/dev/util/collect/Sets.java
index be9006c..b98d3d8 100644
--- a/dev/core/src/com/google/gwt/dev/util/collect/Sets.java
+++ b/dev/core/src/com/google/gwt/dev/util/collect/Sets.java
@@ -119,7 +119,7 @@
}
return set;
default:
- // ArrayList
+ // HashSet
set.remove(toRemove);
return set;
}