Description redacted.
--
MOS_MIGRATED_REVID=125013752
diff --git a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
index 975c292..b3b8682 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
@@ -147,6 +147,7 @@
     }
   };
 
+  private final int hashCode;
   private final Path path;
   private final Root root;
   private final PathFragment execPath;
@@ -180,6 +181,7 @@
       throw new IllegalArgumentException(execPath + ": illegal execPath for " + path
           + " (root: " + root + ")");
     }
+    this.hashCode = path.hashCode();
     this.path = path;
     this.root = root;
     this.execPath = execPath;
@@ -572,7 +574,10 @@
 
   @Override
   public final int hashCode() {
-    return path.hashCode();
+    // This is just path.hashCode().  We cache a copy in the Artifact object to reduce LLC misses
+    // during operations which build a HashSet out of many Artifacts.  This is a slight loss for
+    // memory but saves ~1% overall CPU in some real builds.
+    return hashCode;
   }
 
   @Override
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/CompileOrderExpander.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/CompileOrderExpander.java
deleted file mode 100644
index aceebcc65..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/CompileOrderExpander.java
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-import com.google.common.collect.ImmutableCollection;
-
-/**
- * A nested set expander that implements left-to-right postordering.
- *
- * <p>For example, for the nested set {B, D, {A, C}}, the iteration order is "A C B D"
- * (child-first).
- *
- * <p>This type of set would typically be used for artifacts where elements of nested sets go before
- * the direct members of a set, for example in the case of Javascript dependencies.
- */
-final class CompileOrderExpander<E> implements NestedSetExpander<E> {
-
-  // We suppress unchecked warning so that we can access the internal raw structure of the
-  // NestedSet.
-  @SuppressWarnings("unchecked")
-  @Override
-  public void expandInto(NestedSet<E> set, Uniqueifier uniqueifier,
-      ImmutableCollection.Builder<E> builder) {
-    for (NestedSet<E> subset : set.transitiveSets()) {
-      if (!subset.isEmpty() && uniqueifier.isUnique(subset)) {
-        expandInto(subset, uniqueifier, builder);
-      }
-    }
-
-    // This switch is here to compress the memo used by the uniqueifier
-    for (Object e : set.directMembers()) {
-      if (uniqueifier.isUnique(e)) {
-        builder.add((E) e);
-      }
-    }
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/CompileOrderNestedSetFactory.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/CompileOrderNestedSetFactory.java
deleted file mode 100644
index 382d54e..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/CompileOrderNestedSetFactory.java
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-import com.google.common.collect.ImmutableList;
-
-/**
- * Compile order {@code NestedSet} factory.
- */
-final class CompileOrderNestedSetFactory implements NestedSetFactory {
-
-  @Override
-  public <E> NestedSet<E> onlyDirects(Object[] directs) {
-    return new CompileOnlyDirectsNestedSet<>(directs);
-  }
-
-  @Override
-  public <E> NestedSet<E> onlyDirects(ImmutableList<E> directs) {
-    return new CompileOrderImmutableListDirectsNestedSet<>(directs);
-  }
-
-  @Override
-  public <E> NestedSet<E> oneDirectOneTransitive(E direct, NestedSet<E> transitive) {
-    return new CompileOneDirectOneTransitiveNestedSet<>(direct, transitive);
-  }
-
-  @Override
-  public <E> NestedSet<E> manyDirectsOneTransitive(Object[] direct,
-      NestedSet<E> transitive) {
-    return new CompileManyDirectOneTransitiveNestedSet<>(direct, transitive);
-  }
-
-  @Override
-  public <E> NestedSet<E> onlyOneTransitive(NestedSet<E> transitive) {
-    return new CompileOnlyOneTransitiveNestedSet<>(transitive);
-  }
-
-  @Override
-  public <E> NestedSet<E> onlyManyTransitives(NestedSet[] transitives) {
-    return new CompileOnlyTransitivesNestedSet<>(transitives);
-  }
-
-  @Override
-  public <E> NestedSet<E> oneDirectManyTransitive(Object direct, NestedSet[] transitives) {
-    return new CompileOneDirectManyTransitive<>(direct, transitives);
-  }
-
-  @Override
-  public <E> NestedSet<E> manyDirectManyTransitive(Object[] directs, NestedSet[] transitives) {
-    return new CompileManyDirectManyTransitive<>(directs, transitives);
-  }
-
-  @Override
-  public <E> NestedSet<E> oneDirect(E element) {
-    return new CompileSingleDirectNestedSet<>(element);
-  }
-
-  private static class CompileOnlyDirectsNestedSet<E> extends OnlyDirectsNestedSet<E> {
-
-    CompileOnlyDirectsNestedSet(Object[] directs) { super(directs); }
-
-    @Override
-    public Order getOrder() { return Order.COMPILE_ORDER; }
-  }
-
-  private static class CompileOneDirectOneTransitiveNestedSet<E> extends
-      OneDirectOneTransitiveNestedSet<E> {
-
-    private CompileOneDirectOneTransitiveNestedSet(E direct, NestedSet<E> transitive) {
-      super(direct, transitive);
-    }
-
-    @Override
-    public Order getOrder() { return Order.COMPILE_ORDER; }
-  }
-
-  private static class CompileOneDirectManyTransitive<E> extends OneDirectManyTransitive<E> {
-
-    private CompileOneDirectManyTransitive(Object direct, NestedSet[] transitive) {
-      super(direct, transitive);
-    }
-
-    @Override
-    public Order getOrder() { return Order.COMPILE_ORDER; }
-  }
-
-  private static class CompileManyDirectManyTransitive<E> extends ManyDirectManyTransitive<E> {
-
-    private CompileManyDirectManyTransitive(Object[] directs, NestedSet[] transitives) {
-      super(directs, transitives);
-    }
-
-    @Override
-    public Order getOrder() { return Order.COMPILE_ORDER; }
-  }
-
-  private static class CompileOnlyOneTransitiveNestedSet<E> extends OnlyOneTransitiveNestedSet<E> {
-
-    private CompileOnlyOneTransitiveNestedSet(NestedSet<E> transitive) { super(transitive); }
-
-    @Override
-    public Order getOrder() { return Order.COMPILE_ORDER; }
-  }
-
-  private static class CompileManyDirectOneTransitiveNestedSet<E> extends
-      ManyDirectOneTransitiveNestedSet<E> {
-
-    private CompileManyDirectOneTransitiveNestedSet(Object[] direct,
-        NestedSet<E> transitive) { super(direct, transitive); }
-
-    @Override
-    public Order getOrder() { return Order.COMPILE_ORDER; }
-  }
-
-  private static class CompileOnlyTransitivesNestedSet<E> extends OnlyTransitivesNestedSet<E> {
-
-    private CompileOnlyTransitivesNestedSet(NestedSet[] transitives) { super(transitives); }
-
-    @Override
-    public Order getOrder() { return Order.COMPILE_ORDER; }
-  }
-
-  private static class CompileOrderImmutableListDirectsNestedSet<E> extends
-      ImmutableListDirectsNestedSet<E> {
-
-    private CompileOrderImmutableListDirectsNestedSet(ImmutableList<E> directs) { super(directs); }
-
-    @Override
-    public Order getOrder() {
-      return Order.COMPILE_ORDER;
-    }
-  }
-
-  private static class CompileSingleDirectNestedSet<E> extends SingleDirectNestedSet<E> {
-
-    private CompileSingleDirectNestedSet(E element) { super(element); }
-
-    @Override
-    public Order getOrder() { return Order.COMPILE_ORDER; }
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/EmptyNestedSet.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/EmptyNestedSet.java
deleted file mode 100644
index 5359a9f..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/EmptyNestedSet.java
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-
-import java.util.Iterator;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-
-import javax.annotation.Nullable;
-
-/**
- * An empty nested set.
- */
-final class EmptyNestedSet<E> extends NestedSet<E> {
-  private static final NestedSet[] EMPTY_NESTED_SET = new NestedSet[0];
-  private static final Object[] EMPTY_ELEMENTS = new Object[0];
-  private final Order order;
-
-  EmptyNestedSet(Order type) {
-    this.order = type;
-  }
-
-  @Override
-  public Iterator<E> iterator() {
-    return ImmutableList.<E>of().iterator();
-  }
-
-  @Override
-  Object[] directMembers() {
-    return EMPTY_ELEMENTS;
-  }
-
-  @Override
-  NestedSet[] transitiveSets() {
-    return EMPTY_NESTED_SET;
-  }
-
-  @Override
-  public boolean isEmpty() {
-    return true;
-  }
-
-  @Override
-  public List<E> toList() {
-    return ImmutableList.of();
-  }
-
-  @Override
-  public Set<E> toSet() {
-    return ImmutableSet.of();
-  }
-
-  @Override
-  public String toString() {
-    return "{}";
-  }
-
-  @Override
-  public Order getOrder() {
-    return order;
-  }
-
-  @Override
-  public boolean shallowEquals(@Nullable NestedSet<? extends E> other) {
-    return other != null && getOrder() == other.getOrder() && other.isEmpty();
-  }
-
-  @Override
-  public int shallowHashCode() {
-    return Objects.hashCode(getOrder());
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/ImmutableListDirectsNestedSet.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/ImmutableListDirectsNestedSet.java
deleted file mode 100644
index 24454a7..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/ImmutableListDirectsNestedSet.java
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-
-import java.util.List;
-import java.util.Set;
-
-import javax.annotation.Nullable;
-
-/**
- * Memory-optimized NestedSet implementation for NestedSets without transitive dependencies that
- * allows us to share an ImmutableList.
- */
-abstract class ImmutableListDirectsNestedSet<E> extends NestedSet<E> {
-
-  @SuppressWarnings("rawtypes")
-  private static final NestedSet[] EMPTY = new NestedSet[0];
-
-  private final ImmutableList<E> directDeps;
-
-  public ImmutableListDirectsNestedSet(ImmutableList<E> directDeps) {
-    this.directDeps = directDeps;
-  }
-
-  @Override
-  public abstract Order getOrder();
-
-  @Override
-  Object[] directMembers() {
-    return directDeps.toArray();
-  }
-
-  @SuppressWarnings({"cast", "unchecked"})
-  @Override
-  NestedSet<? extends E>[] transitiveSets() {
-    return (NestedSet<? extends E>[]) EMPTY;
-  }
-
-  @Override
-  public boolean isEmpty() {
-    return directDeps.isEmpty();
-  }
-
-  /**
-   * Currently all the Order implementations return the direct elements in the same order if they do
-   * not have transitive elements. So we skip calling order.getExpander().
-   */
-  @SuppressWarnings("unchecked")
-  @Override
-  public List<E> toList() {
-    return directDeps;
-  }
-
-  @SuppressWarnings("unchecked")
-  @Override
-  public Set<E> toSet() {
-    return ImmutableSet.copyOf(directDeps);
-  }
-
-  @Override
-  public boolean shallowEquals(@Nullable NestedSet<? extends E> other) {
-    if (this == other) {
-      return true;
-    }
-    if (other == null) {
-      return false;
-    }
-    return getOrder().equals(other.getOrder())
-        && other instanceof ImmutableListDirectsNestedSet
-        && directDeps.equals(((ImmutableListDirectsNestedSet<? extends E>) other).directDeps);
-  }
-
-  @Override
-  public int shallowHashCode() {
-    return directDeps.hashCode();
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/LinkOrderExpander.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/LinkOrderExpander.java
deleted file mode 100644
index a4c1f3e..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/LinkOrderExpander.java
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-import com.google.common.collect.ImmutableCollection;
-import com.google.common.collect.ImmutableList;
-
-/**
- * A nested set expander that implements a variation of left-to-right preordering.
- *
- * <p>For example, for the nested set {A, C, {B, D}}, the iteration order is "A C B D"
- * (parent-first).
- *
- * <p>This type of set would typically be used for artifacts where elements of
- * nested sets go after the direct members of a set, for example when providing
- * a list of libraries to the C++ compiler.
- *
- * <p>The custom ordering has the property that elements of nested sets always come
- * before elements of descendant nested sets. Left-to-right order is preserved if
- * possible, both for items and for references to nested sets.
- *
- * <p>The left-to-right pre-order-like ordering is implemented by running a
- * right-to-left postorder traversal and then reversing the result.
- *
- * <p>The reason naive left-to left-to-right preordering is not used here is that
- * it does not handle diamond-like structures properly. For example, take the
- * following structure (nesting downwards):
- *
- * <pre>
- *    A
- *   / \
- *  B   C
- *   \ /
- *    D
- * </pre>
- *
- * <p>Naive preordering would produce "A B D C", which does not preserve the
- * "parent before child" property: C is a parent of D, so C should come before
- * D. Either "A B C D" or "A C B D" would be acceptable. This implementation
- * returns the first option of the two so that left-to-right order is preserved.
- *
- * <p>In case the nested sets form a tree, the ordering algorithm is equivalent to
- * standard left-to-right preorder.
- *
- * <p>Sometimes it may not be possible to preserve left-to-right order:
- *
- * <pre>
- *      A
- *    /   \
- *   B     C
- *  / \   / \
- *  \   E   /
- *   \     /
- *    \   /
- *      D
- * </pre>
- *
- * <p>The left branch (B) would indicate "D E" ordering and the right branch (C)
- * dictates "E D". In such cases ordering is decided by the rightmost branch
- * because of the list reversing behind the scenes, so the ordering in the final
- * enumeration will be "E D".
- */
-
-final class LinkOrderExpander<E> implements NestedSetExpander<E> {
-  @Override
-  public void expandInto(NestedSet<E> nestedSet, Uniqueifier uniqueifier,
-      ImmutableCollection.Builder<E> builder) {
-    ImmutableList.Builder<E> result = ImmutableList.builder();
-    internalEnumerate(nestedSet, uniqueifier, result);
-    builder.addAll(result.build().reverse());
-  }
-
-  // We suppress unchecked warning so that we can access the internal raw structure of the
-  // NestedSet.
-  @SuppressWarnings("unchecked")
-  private void internalEnumerate(NestedSet<E> set, Uniqueifier uniqueifier,
-      ImmutableCollection.Builder<E> builder) {
-    NestedSet[] transitiveSets = set.transitiveSets();
-    for (int i = transitiveSets.length - 1; i >= 0; i--) {
-      NestedSet<E> subset = transitiveSets[i];
-      if (!subset.isEmpty() && uniqueifier.isUnique(subset)) {
-        internalEnumerate(subset, uniqueifier, builder);
-      }
-    }
-
-    Object[] directMembers = set.directMembers();
-    for (int i = directMembers.length - 1; i >= 0; i--) {
-      Object e = directMembers[i];
-      if (uniqueifier.isUnique(e)) {
-        builder.add((E) e);
-      }
-    }
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/LinkOrderNestedSetFactory.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/LinkOrderNestedSetFactory.java
deleted file mode 100644
index a6e78d0..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/LinkOrderNestedSetFactory.java
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-import com.google.common.collect.ImmutableList;
-
-/**
- * Link order {@code NestedSet} factory.
- */
-final class LinkOrderNestedSetFactory implements NestedSetFactory {
-
-  @Override
-  public <E> NestedSet<E> onlyDirects(Object[] directs) {
-    return new LinkOnlyDirectsNestedSet<>(directs);
-  }
-
-  @Override
-  public <E> NestedSet<E> onlyDirects(ImmutableList<E> directs) {
-    return new LinkImmutableListDirectsNestedSet<>(directs);
-  }
-
-  @Override
-  public <E> NestedSet<E> oneDirectOneTransitive(E direct, NestedSet<E> transitive) {
-    return new LinkOneDirectOneTransitiveNestedSet<>(direct, transitive);
-  }
-
-  @Override
-  public <E> NestedSet<E> manyDirectsOneTransitive(Object[] direct,
-      NestedSet<E> transitive) {
-    return new LinkManyDirectOneTransitiveNestedSet<>(direct, transitive);
-  }
-
-  @Override
-  public <E> NestedSet<E> onlyOneTransitive(NestedSet<E> transitive) {
-    return new LinkOnlyOneTransitiveNestedSet<>(transitive);
-  }
-
-  @Override
-  public <E> NestedSet<E> onlyManyTransitives(NestedSet[] transitives) {
-    return new LinkOnlyTransitivesNestedSet<>(transitives);
-  }
-
-  @Override
-  public <E> NestedSet<E> oneDirectManyTransitive(Object direct, NestedSet[] transitives) {
-    return new LinkOneDirectManyTransitive<>(direct, transitives);
-  }
-
-  @Override
-  public <E> NestedSet<E> manyDirectManyTransitive(Object[] directs, NestedSet[] transitives) {
-    return new LinkManyDirectManyTransitive<>(directs, transitives);
-  }
-
-  @Override
-  public <E> NestedSet<E> oneDirect(E element) {
-    return new LinkSingleDirectNestedSet<>(element);
-  }
-
-  private static class LinkOnlyDirectsNestedSet<E> extends OnlyDirectsNestedSet<E> {
-
-    LinkOnlyDirectsNestedSet(Object[] directs) { super(directs); }
-
-    @Override
-    public Order getOrder() { return Order.LINK_ORDER; }
-  }
-
-  private static class LinkOneDirectOneTransitiveNestedSet<E> extends
-      OneDirectOneTransitiveNestedSet<E> {
-
-    private LinkOneDirectOneTransitiveNestedSet(E direct, NestedSet<E> transitive) {
-      super(direct, transitive);
-    }
-
-    @Override
-    public Order getOrder() { return Order.LINK_ORDER; }
-  }
-
-  private static class LinkOneDirectManyTransitive<E> extends OneDirectManyTransitive<E> {
-
-    private LinkOneDirectManyTransitive(Object direct, NestedSet[] transitive) {
-      super(direct, transitive);
-    }
-
-    @Override
-    public Order getOrder() { return Order.LINK_ORDER; }
-  }
-
-  private static class LinkManyDirectManyTransitive<E> extends ManyDirectManyTransitive<E> {
-
-    private LinkManyDirectManyTransitive(Object[] directs, NestedSet[] transitives) {
-      super(directs, transitives);
-    }
-
-    @Override
-    public Order getOrder() { return Order.LINK_ORDER; }
-  }
-
-  private static class LinkOnlyOneTransitiveNestedSet<E> extends OnlyOneTransitiveNestedSet<E> {
-
-    private LinkOnlyOneTransitiveNestedSet(NestedSet<E> transitive) { super(transitive); }
-
-    @Override
-    public Order getOrder() { return Order.LINK_ORDER; }
-  }
-
-  private static class LinkManyDirectOneTransitiveNestedSet<E> extends
-      ManyDirectOneTransitiveNestedSet<E> {
-
-    private LinkManyDirectOneTransitiveNestedSet(Object[] direct,
-        NestedSet<E> transitive) { super(direct, transitive); }
-
-    @Override
-    public Order getOrder() { return Order.LINK_ORDER; }
-  }
-
-  private static class LinkOnlyTransitivesNestedSet<E> extends OnlyTransitivesNestedSet<E> {
-
-    private LinkOnlyTransitivesNestedSet(NestedSet[] transitives) { super(transitives); }
-
-    @Override
-    public Order getOrder() { return Order.LINK_ORDER; }
-  }
-
-  private static class LinkImmutableListDirectsNestedSet<E> extends
-      ImmutableListDirectsNestedSet<E> {
-
-    private LinkImmutableListDirectsNestedSet(ImmutableList<E> directs) { super(directs); }
-
-    @Override
-    public Order getOrder() {
-      return Order.LINK_ORDER;
-    }
-  }
-
-  private static class LinkSingleDirectNestedSet<E> extends SingleDirectNestedSet<E> {
-
-    private LinkSingleDirectNestedSet(E element) { super(element); }
-
-    @Override
-    public Order getOrder() { return Order.LINK_ORDER; }
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/ManyDirectManyTransitive.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/ManyDirectManyTransitive.java
deleted file mode 100644
index e96b76b..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/ManyDirectManyTransitive.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-import java.util.Arrays;
-import java.util.Objects;
-
-import javax.annotation.Nullable;
-
-/**
- * NestedSet implementation that can have many direct elements and many transitive
- * {@code NestedSet}s.
- */
-abstract class ManyDirectManyTransitive<E> extends MemoizedUniquefierNestedSet<E> {
-
-  private final Object[] directs;
-  private final NestedSet[] transitives;
-  private Object memo;
-
-  ManyDirectManyTransitive(Object[] directs, NestedSet[] transitives) {
-    this.directs = directs;
-    this.transitives = transitives;
-  }
-
-  @Override
-  Object getMemo() { return memo; }
-
-  @Override
-  void setMemo(Object memo) { this.memo = memo; }
-
-  @Override
-  Object[] directMembers() { return directs; }
-
-  @Override
-  NestedSet[] transitiveSets() { return transitives; }
-
-  @Override
-  public boolean shallowEquals(@Nullable NestedSet<? extends E> other) {
-    if (this == other) {
-      return true;
-    }
-    return other != null
-        && getOrder().equals(other.getOrder())
-        && other instanceof ManyDirectManyTransitive
-        && Arrays.equals(directs, ((ManyDirectManyTransitive<? extends E>) other).directs)
-        && Arrays.equals(transitives, ((ManyDirectManyTransitive<? extends E>) other).transitives);
-  }
-
-  @Override
-  public int shallowHashCode() {
-    return Objects.hash(getOrder(), Arrays.hashCode(directs), Arrays.hashCode(transitives)); }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/ManyDirectOneTransitiveNestedSet.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/ManyDirectOneTransitiveNestedSet.java
deleted file mode 100644
index a98edac..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/ManyDirectOneTransitiveNestedSet.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-import java.util.Arrays;
-import java.util.Objects;
-
-import javax.annotation.Nullable;
-
-/**
- * Memory-efficient implementation for the case where we have many direct elements and one
- * transitive NestedSet.
- */
-abstract class ManyDirectOneTransitiveNestedSet<E> extends MemoizedUniquefierNestedSet<E> {
-
-  private final Object[] directs;
-  private final NestedSet<E> transitive;
-  private Object memo;
-
-  public ManyDirectOneTransitiveNestedSet(Object[] directs, NestedSet<E> transitive) {
-    this.directs = directs;
-    this.transitive = transitive;
-  }
-
-  @Override
-  Object getMemo() { return memo; }
-
-  @Override
-  void setMemo(Object memo) { this.memo = memo; }
-
-  @Override
-  Object[] directMembers() { return directs; }
-
-  @Override
-  NestedSet[] transitiveSets() { return new NestedSet[]{transitive}; }
-
-  @Override
-  public boolean shallowEquals(@Nullable NestedSet<? extends E> other) {
-    if (this == other) {
-      return true;
-    }
-    return other != null
-        && getOrder().equals(other.getOrder())
-        && other instanceof ManyDirectOneTransitiveNestedSet
-        && Arrays.equals(directs, ((ManyDirectOneTransitiveNestedSet<? extends E>) other).directs)
-        && transitive == ((ManyDirectOneTransitiveNestedSet<? extends E>) other).transitive;
-  }
-
-  @Override
-  public int shallowHashCode() {
-    return Objects.hash(getOrder(), Arrays.hashCode(directs), transitive); }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/MemoizedUniquefierNestedSet.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/MemoizedUniquefierNestedSet.java
deleted file mode 100644
index 5ef0cd6..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/MemoizedUniquefierNestedSet.java
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-import com.google.common.collect.ImmutableCollection;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-
-import java.util.List;
-import java.util.Set;
-
-/**
- * A NestedSet that keeps a memoized uniquifier so that it is faster to fill a set.
- *
- * <p>This class does not keep the memoized object itself so that we can take advantage of the
- * memory field alignment (Memory alignment does not put in the same structure the fields of a
- * class and its extensions).
- */
-public abstract class MemoizedUniquefierNestedSet<E> extends NestedSet<E> {
-
-  @Override
-  public List<E> toList() {
-    ImmutableList.Builder<E> builder = new ImmutableList.Builder<>();
-    memoizedFill(builder);
-    return builder.build();
-  }
-
-  @Override
-  public Set<E> toSet() {
-    ImmutableSet.Builder<E> builder = new ImmutableSet.Builder<>();
-    memoizedFill(builder);
-    return builder.build();
-  }
-
-  /**
-   * It does not make sense to have a {@code MemoizedUniquefierNestedSet} if it is empty.
-   */
-  @Override
-  public boolean isEmpty() { return false; }
-
-  abstract Object getMemo();
-
-  abstract void setMemo(Object object);
-
-  /**
-   * Fill a collection builder by using a memoized {@code Uniqueifier} for faster uniqueness check.
-   */
-  final void memoizedFill(ImmutableCollection.Builder<E> builder) {
-    Uniqueifier memoed;
-    synchronized (this) {
-      Object memo = getMemo();
-      if (memo == null) {
-        RecordingUniqueifier uniqueifier = new RecordingUniqueifier();
-        getOrder().<E>expander().expandInto(this, uniqueifier, builder);
-        setMemo(uniqueifier.getMemo());
-        return;
-      } else {
-        memoed = RecordingUniqueifier.createReplayUniqueifier(memo);
-      }
-    }
-    getOrder().<E>expander().expandInto(this, memoed, builder);
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NaiveLinkOrderExpander.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/NaiveLinkOrderExpander.java
deleted file mode 100644
index ec8bd87..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NaiveLinkOrderExpander.java
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-import com.google.common.collect.ImmutableCollection;
-
-/**
- * A nested set expander that implements naive left-to-right preordering.
- *
- * <p>For example, for the nested set {B, D, {A, C}}, the iteration order is "B D A C".
- *
- * <p>This implementation is intended for backwards-compatible nested set replacements of code that
- * uses naive preordering.
- *
- * <p>The implementation is called naive because it does no special treatment of dependency graphs
- * that are not trees. For such graphs the property of parent-before-dependencies in the iteration
- * order will not be upheld. For example, the diamond-shape graph A->{B, C}, B->{D}, C->{D} will be
- * enumerated as "A B D C" rather than "A B C D" or "A C B D".
- *
- * <p>The difference from {@link LinkOrderNestedSet} is that this implementation gives priority to
- * left-to-right order over dependencies-after-parent ordering. Note that the latter is usually more
- * important, so please use {@link LinkOrderNestedSet} whenever possible.
- */
-final class NaiveLinkOrderExpander<E> implements NestedSetExpander<E> {
-
-  @SuppressWarnings("unchecked")
-  @Override
-  public void expandInto(NestedSet<E> set, Uniqueifier uniqueifier,
-      ImmutableCollection.Builder<E> builder) {
-
-    for (Object e : set.directMembers()) {
-      if (uniqueifier.isUnique(e)) {
-        builder.add((E) e);
-      }
-    }
-
-    for (NestedSet<E> subset : set.transitiveSets()) {
-      if (!subset.isEmpty() && uniqueifier.isUnique(subset)) {
-        expandInto(subset, uniqueifier, builder);
-      }
-    }
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NaiveLinkOrderNestedSetFactory.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/NaiveLinkOrderNestedSetFactory.java
deleted file mode 100644
index 7305bce..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NaiveLinkOrderNestedSetFactory.java
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-import com.google.common.collect.ImmutableList;
-
-/**
- * NaiveLink order {@code NestedSet} factory.
- */
-final class NaiveLinkOrderNestedSetFactory implements NestedSetFactory {
-
-  @Override
-  public <E> NestedSet<E> onlyDirects(Object[] directs) {
-    return new NaiveLinkOnlyDirectsNestedSet<>(directs);
-  }
-
-  @Override
-  public <E> NestedSet<E> onlyDirects(ImmutableList<E> directs) {
-    return new NaiveLinkImmutableListDirectsNestedSet<>(directs);
-  }
-
-  @Override
-  public <E> NestedSet<E> oneDirectOneTransitive(E direct, NestedSet<E> transitive) {
-    return new NaiveLinkOneDirectOneTransitiveNestedSet<>(direct, transitive);
-  }
-
-  @Override
-  public <E> NestedSet<E> manyDirectsOneTransitive(Object[] direct,
-      NestedSet<E> transitive) {
-    return new NaiveLinkManyDirectOneTransitiveNestedSet<>(direct, transitive);
-  }
-
-  @Override
-  public <E> NestedSet<E> onlyOneTransitive(NestedSet<E> transitive) {
-    return new NaiveLinkOnlyOneTransitiveNestedSet<>(transitive);
-  }
-
-  @Override
-  public <E> NestedSet<E> onlyManyTransitives(NestedSet[] transitives) {
-    return new NaiveLinkOnlyTransitivesNestedSet<>(transitives);
-  }
-
-  @Override
-  public <E> NestedSet<E> oneDirectManyTransitive(Object direct, NestedSet[] transitives) {
-    return new NaiveLinkOneDirectManyTransitive<>(direct, transitives);
-  }
-
-  @Override
-  public <E> NestedSet<E> manyDirectManyTransitive(Object[] directs, NestedSet[] transitives) {
-    return new NaiveLinkManyDirectManyTransitive<>(directs, transitives);
-  }
-
-  @Override
-  public <E> NestedSet<E> oneDirect(final E element) {
-    return new NaiveLinkSingleDirectNestedSet<>(element);
-  }
-
-  private static class NaiveLinkOnlyDirectsNestedSet<E> extends OnlyDirectsNestedSet<E> {
-
-    NaiveLinkOnlyDirectsNestedSet(Object[] directs) { super(directs); }
-
-    @Override
-    public Order getOrder() { return Order.NAIVE_LINK_ORDER; }
-  }
-
-  private static class NaiveLinkOneDirectOneTransitiveNestedSet<E> extends
-      OneDirectOneTransitiveNestedSet<E> {
-
-    private NaiveLinkOneDirectOneTransitiveNestedSet(E direct, NestedSet<E> transitive) {
-      super(direct, transitive);
-    }
-
-    @Override
-    public Order getOrder() { return Order.NAIVE_LINK_ORDER; }
-  }
-
-  private static class NaiveLinkOneDirectManyTransitive<E> extends OneDirectManyTransitive<E> {
-
-    private NaiveLinkOneDirectManyTransitive(Object direct, NestedSet[] transitive) {
-      super(direct, transitive);
-    }
-
-    @Override
-    public Order getOrder() { return Order.NAIVE_LINK_ORDER; }
-  }
-
-  private static class NaiveLinkManyDirectManyTransitive<E> extends ManyDirectManyTransitive<E> {
-
-    private NaiveLinkManyDirectManyTransitive(Object[] directs, NestedSet[] transitives) {
-      super(directs, transitives);
-    }
-
-    @Override
-    public Order getOrder() { return Order.NAIVE_LINK_ORDER; }
-  }
-
-  private static class NaiveLinkOnlyOneTransitiveNestedSet<E>
-      extends OnlyOneTransitiveNestedSet<E> {
-
-    private NaiveLinkOnlyOneTransitiveNestedSet(NestedSet<E> transitive) { super(transitive); }
-
-    @Override
-    public Order getOrder() { return Order.NAIVE_LINK_ORDER; }
-  }
-
-  private static class NaiveLinkManyDirectOneTransitiveNestedSet<E> extends
-      ManyDirectOneTransitiveNestedSet<E> {
-
-    private NaiveLinkManyDirectOneTransitiveNestedSet(Object[] direct,
-        NestedSet<E> transitive) { super(direct, transitive); }
-
-    @Override
-    public Order getOrder() { return Order.NAIVE_LINK_ORDER; }
-  }
-
-  private static class NaiveLinkOnlyTransitivesNestedSet<E> extends OnlyTransitivesNestedSet<E> {
-
-    private NaiveLinkOnlyTransitivesNestedSet(NestedSet[] transitives) { super(transitives); }
-
-    @Override
-    public Order getOrder() { return Order.NAIVE_LINK_ORDER; }
-  }
-
-  private static class NaiveLinkImmutableListDirectsNestedSet<E> extends
-      ImmutableListDirectsNestedSet<E> {
-
-    private NaiveLinkImmutableListDirectsNestedSet(ImmutableList<E> directs) { super(directs); }
-
-    @Override
-    public Order getOrder() {
-      return Order.NAIVE_LINK_ORDER;
-    }
-  }
-
-  private static class NaiveLinkSingleDirectNestedSet<E> extends SingleDirectNestedSet<E> {
-
-    private NaiveLinkSingleDirectNestedSet(E element) { super(element); }
-
-    @Override
-    public Order getOrder() { return Order.NAIVE_LINK_ORDER; }
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSet.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSet.java
index f7e3f57..39265a6 100644
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSet.java
+++ b/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSet.java
@@ -13,12 +13,19 @@
 // limitations under the License.
 package com.google.devtools.build.lib.collect.nestedset;
 
+import com.google.common.base.Function;
 import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.collect.CompactHashSet;
 
-import java.io.Serializable;
+import java.util.AbstractCollection;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 import javax.annotation.Nullable;
@@ -28,41 +35,134 @@
  *
  * @see NestedSetBuilder
  */
-public abstract class NestedSet<E> implements Iterable<E>, Serializable {
+@SuppressWarnings("unchecked")
+public final class NestedSet<E> implements Iterable<E> {
 
-  NestedSet() {}
+  private final Order order;
+  private final Object children;
+  private byte[] memo;
+
+  private static final byte[] LEAF_MEMO = {};
+  private static final Object[] EMPTY_CHILDREN = {};
+
+  /**
+   * Construct an empty NestedSet.  Should only be called by Order's class initializer.
+   */
+  NestedSet(Order order) {
+    this.order = order;
+    this.children = EMPTY_CHILDREN;
+    this.memo = LEAF_MEMO;
+  }
+
+  NestedSet(Order order, Set<E> direct, Set<NestedSet<E>> transitive) {
+    this.order = order;
+
+    // The iteration order of these collections is the order in which we add the items.
+    Collection<E> directOrder = direct;
+    Collection<NestedSet<E>> transitiveOrder = transitive;
+    // True if we visit the direct members before the transitive members.
+    boolean preorder;
+
+    switch(order) {
+      case LINK_ORDER:
+        directOrder = ImmutableList.copyOf(direct).reverse();
+        transitiveOrder = ImmutableList.copyOf(transitive).reverse();
+        preorder = false;
+        break;
+      case STABLE_ORDER:
+      case COMPILE_ORDER:
+        preorder = false;
+        break;
+      case NAIVE_LINK_ORDER:
+        preorder = true;
+        break;
+      default:
+        throw new AssertionError(order);
+    }
+
+    // Remember children we extracted from one-element subsets.
+    // Otherwise we can end up with two of the same child, which is a
+    // problem for the fast path in toList().
+    Set<E> alreadyInserted = ImmutableSet.of();
+    // The candidate array of children.
+    Object[] children = new Object[direct.size() + transitive.size()];
+    int n = 0;  // current position in children
+    boolean leaf = true;  // until we find otherwise
+
+    for (int pass = 0; pass <= 1; ++pass) {
+      if ((pass == 0) == preorder && !direct.isEmpty()) {
+        for (E member : directOrder) {
+          if (member instanceof Object[]) {
+            throw new IllegalArgumentException("cannot store Object[] in NestedSet");
+          }
+          if (!alreadyInserted.contains(member)) {
+            children[n++] = member;
+          }
+        }
+        alreadyInserted = direct;
+      } else if ((pass == 1) == preorder && !transitive.isEmpty()) {
+        CompactHashSet<E> hoisted = CompactHashSet.create();
+        for (NestedSet<E> subset : transitiveOrder) {
+          Object c = subset.children;
+          if (c instanceof Object[]) {
+            Object[] a = (Object[]) c;
+            if (a.length < 2) {
+              throw new AssertionError(a.length);
+            }
+            children[n++] = a;
+            leaf = false;
+          } else {
+            if (!alreadyInserted.contains((E) c) && hoisted.add((E) c)) {
+              children[n++] = c;
+            }
+          }
+        }
+        alreadyInserted = hoisted;
+      }
+    }
+
+    // If we ended up wrapping exactly one item or one other set, dereference it.
+    if (n == 1) {
+      this.children = children[0];
+    } else if (n == 0) {
+      this.children = EMPTY_CHILDREN;
+    } else if (n < children.length) {
+      this.children = Arrays.copyOf(children, n);
+    } else {
+      this.children = children;
+    }
+    if (leaf) {
+      this.memo = LEAF_MEMO;
+    }
+  }
 
   /**
    * Returns the ordering of this nested set.
    */
-  public abstract Order getOrder();
+  public Order getOrder() {
+    return order;
+  }
 
   /**
-   * Returns a collection of elements added to this specific set in an implementation-specified
-   * order.
-   *
-   * <p>Elements from subsets are not taken into account.
-   *
-   * <p>The reason for using Object[] instead of E[] is that when we build the NestedSet we
-   * would need to have access to the specific class that E represents in order to create an E
-   * array. Since this method is only designed to be used internally it is fine to keep it as
-   * Object[].
-   *
-   * <p>Callers of this method should only consume the objects and not modify the array.
+   * Returns the internal item or array.  For use by NestedSetVisitor.
    */
-  abstract Object[] directMembers();
-
-  /**
-   * Returns the collection of sets included as subsets in this set.
-   *
-   * <p>Callers of this method should only consume the objects and not modify the array.
-   */
-  abstract NestedSet[] transitiveSets();
+  Object rawChildren() {
+    return children;
+  }
 
   /**
    * Returns true if the set is empty.
    */
-  public abstract boolean isEmpty();
+  public boolean isEmpty() {
+    return children == EMPTY_CHILDREN;
+  }
+
+  /**
+   * Returns true if the set has exactly one element.
+   */
+  private boolean isSingleton() {
+    return !(children instanceof Object[]);
+  }
 
   /**
    * Returns a collection of all unique elements of this set (including subsets)
@@ -81,7 +181,15 @@
    *
    * <p>Use {@link #toCollection} when possible for better efficiency.
    */
-  public abstract List<E> toList();
+  public List<E> toList() {
+    if (isSingleton()) {
+      return ImmutableList.of((E) children);
+    }
+    if (isEmpty()) {
+      return ImmutableList.of();
+    }
+    return order == Order.LINK_ORDER ? expand().reverse() : expand();
+  }
 
   /**
    * Returns a collection of all unique elements of this set (including subsets)
@@ -89,7 +197,9 @@
    *
    * <p>Use {@link #toCollection} when possible for better efficiency.
    */
-  public abstract Set<E> toSet();
+  public Set<E> toSet() {
+    return ImmutableSet.copyOf(toList());
+  }
 
   /**
    * Returns true if this set is equal to {@code other} based on the top-level
@@ -100,7 +210,16 @@
    *
    * @param other the {@code NestedSet} to compare against.
    */
-  public abstract boolean shallowEquals(@Nullable NestedSet<? extends E> other);
+  public boolean shallowEquals(@Nullable NestedSet<? extends E> other) {
+    if (this == other) {
+      return true;
+    }
+    return other != null
+        && order == other.order
+        && (children.equals(other.children)
+            || (!isSingleton() && !other.isSingleton()
+                && Arrays.equals((Object[]) children, (Object[]) other.children)));
+  }
 
   /**
    * Returns a hash code that produces a notion of identity that is consistent with
@@ -111,17 +230,165 @@
    * the standard equals/hashCode is to minimize accidental use, since they are
    * different from both standard Java objects and collection-like objects.
    */
-  public abstract int shallowHashCode();
-
-  @Override
-  public String toString() {
-    String members = Joiner.on(", ").join(directMembers());
-    String nestedSets = Joiner.on(", ").join(transitiveSets());
-    String separator = members.length() > 0 && nestedSets.length() > 0 ? ", " : "";
-    return "{" + members + separator + nestedSets + "}";
+  public int shallowHashCode() {
+    if (isSingleton()) {
+      return Objects.hash(order, children);
+    } else {
+      return Objects.hash(order, Arrays.hashCode((Object[]) children));
+    }
   }
 
   @Override
-  public Iterator<E> iterator() { return new NestedSetLazyIterator<>(this); }
+  public String toString() {
+    return isSingleton() ? "{" + children + "}" : childrenToString(children);
+  }
 
+  // TODO:  this leaves LINK_ORDER backwards
+  private static String childrenToString(Object children) {
+    if (children instanceof Object[]) {
+      return "{" + Joiner.on(", ").join(Iterables.transform(
+          Arrays.asList((Object[]) children), Stringer.INSTANCE)) + "}";
+    } else {
+      return children.toString();
+    }
+  }
+
+  private static enum Stringer implements Function<Object, String> {
+    INSTANCE;
+    @Override public String apply(Object o) {
+      return childrenToString(o);
+    }
+  };
+
+  @Override
+  public Iterator<E> iterator() {
+    // TODO: would it help to have a proper lazy iterator?  seems like it might reduce garbage.
+    return toCollection().iterator();
+  }
+
+  /**
+   * Implementation of {@link #toList}.  Uses one of three strategies based on the value of
+   * {@code this.memo}: wrap our direct items in a list, call {@link #lockedExpand} to perform
+   * the initial {@link #walk}, or call {@link #replay} if we have a nontrivial memo.
+   */
+  private ImmutableList<E> expand() {
+    // This value is only set in the constructor, so safe to test here with no lock.
+    if (memo == LEAF_MEMO) {
+      return ImmutableList.<E>copyOf(new ArraySharingCollection<E>((Object[]) children));
+    }
+    CompactHashSet<E> members = lockedExpand();
+    if (members != null) {
+      return ImmutableList.copyOf(members);
+    }
+    Object[] children = (Object[]) this.children;
+    // TODO:  We could record the exact size (inside memo, or by making order an int with two bits
+    // for Order.ordinal()) and avoid an array copy here.  It's not directly visible in profiles but
+    // it would reduce garbage generated.
+    ImmutableList.Builder<E> output = ImmutableList.builder();
+    replay(output, children, memo, 0);
+    return output.build();
+  }
+
+  // Hack to share our internal array with ImmutableList/ImmutableSet, or avoid
+  // a copy in cases where we can preallocate an array of the correct size.
+  private static final class ArraySharingCollection<E> extends AbstractCollection<E> {
+    private final Object[] array;
+    ArraySharingCollection(Object[] array) {
+      this.array = array;
+    }
+    @Override public Object[] toArray() {
+      return array;
+    }
+    @Override public int size() {
+      return array.length;
+    }
+    @Override public Iterator<E> iterator() {
+      throw new UnsupportedOperationException();
+    }
+  }
+
+  /**
+   * If this is the first call for this object, fills {@code this.memo} and returns a set from
+   * {@link #walk}.  Otherwise returns null; the caller should use {@link #replay} instead.
+   */
+  private synchronized CompactHashSet<E> lockedExpand() {
+    if (memo != null) {
+      return null;
+    }
+    Object[] children = (Object[]) this.children;
+    CompactHashSet<E> members = CompactHashSet.createWithExpectedSize(128);
+    CompactHashSet<Object> sets = CompactHashSet.createWithExpectedSize(128);
+    sets.add(children);
+    memo = new byte[Math.min((children.length + 7) / 8, 8)];
+    int pos = walk(sets, members, children, 0);
+    int bytes = (pos + 7) / 8;
+    if (bytes <= memo.length - 16) {
+      memo = Arrays.copyOf(memo, bytes);
+    }
+    return members;
+  }
+
+  /**
+   * Perform a depth-first traversal of {@code children}, tracking visited
+   * arrays in {@code sets} and visited leaves in {@code members}.  We also
+   * record which edges were taken in {@code this.memo} starting at {@code pos}.
+   *
+   * Returns the final value of {@code pos}.
+   */
+  private int walk(CompactHashSet<Object> sets, CompactHashSet<E> members,
+                   Object[] children, int pos) {
+    int n = children.length;
+    for (int i = 0; i < n; ++i) {
+      if ((pos>>3) >= memo.length) {
+        memo = Arrays.copyOf(memo, memo.length * 2);
+      }
+      Object c = children[i];
+      if (c instanceof Object[]) {
+        if (sets.add(c)) {
+          int prepos = pos;
+          int presize = members.size();
+          pos = walk(sets, members, (Object[]) c, pos + 1);
+          if (presize < members.size()) {
+            memo[prepos>>3] |= 1<<(prepos&7);
+          } else {
+            // We didn't find any new nodes, so don't mark this branch as taken.
+            // Rewind pos.  The rest of the array is still zeros because no one
+            // deeper in the traversal set any bits.
+            pos = prepos + 1;
+          }
+        } else {
+          ++pos;
+        }
+      } else {
+        if (members.add((E) c)) {
+          memo[pos>>3] |= 1<<(pos&7);
+        }
+        ++pos;
+      }
+    }
+    return pos;
+  }
+
+  /**
+   * Repeat a previous traversal of {@code children} performed by {@link #walk}
+   * and recorded in {@code memo}, appending leaves to {@code output}.
+   */
+  private static <E> int replay(ImmutableList.Builder<E> output, Object[] children,
+                                byte[] memo, int pos) {
+    int n = children.length;
+    for (int i = 0; i < n; ++i) {
+      Object c = children[i];
+      if ((memo[pos>>3] & (1<<(pos&7))) != 0) {
+        if (c instanceof Object[]) {
+          pos = replay(output, (Object[]) c, memo, pos + 1);
+        } else {
+          output.add((E) c);
+          ++pos;
+        }
+      } else {
+        ++pos;
+      }
+    }
+    return pos;
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetBuilder.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetBuilder.java
index 9cd15c2..e767789 100644
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetBuilder.java
@@ -17,22 +17,24 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.MapMaker;
+import com.google.devtools.build.lib.collect.CompactHashSet;
 import com.google.devtools.build.lib.util.Preconditions;
 
-import java.util.LinkedHashSet;
+import java.util.concurrent.ConcurrentMap;
 
 /**
  * A builder for nested sets.
  *
  * <p>The builder supports the standard builder interface (that is, {@code #add}, {@code #addAll}
- * and {@code #addTransitive} followed by {@code build}), in addition to shortcut methods
- * {@code #wrap} and {@code #of}.
+ * and {@code #addTransitive} followed by {@code build}), in addition to shortcut method
+ * {@code #wrap}.
  */
 public final class NestedSetBuilder<E> {
 
   private final Order order;
-  private final LinkedHashSet<E> items = new LinkedHashSet<>();
-  private final LinkedHashSet<NestedSet<? extends E>> transitiveSets = new LinkedHashSet<>();
+  private final CompactHashSet<E> items = CompactHashSet.create();
+  private final CompactHashSet<NestedSet<? extends E>> transitiveSets = CompactHashSet.create();
 
   public NestedSetBuilder(Order order) {
     this.order = order;
@@ -105,7 +107,7 @@
    * addTransitive calls matters, but the order between add/addAll and addTransitive does not.
    *
    * <p>An error will be thrown if the ordering of {@code subset} is incompatible with the ordering
-   * of this set. Either they must match or this set must be a {@code STABLE_ORDER} set.
+   * of this set. Either they must match or one must be a {@code STABLE_ORDER} set.
    *
    * @return the builder.
    */
@@ -132,8 +134,8 @@
    * <p>This method may be called multiple times with interleaved {@link #add}, {@link #addAll} and
    * {@link #addTransitive} calls.
    */
-  // Casting from LinkedHashSet<NestedSet<? extends E>> to LinkedHashSet<NestedSet<E>> by way of
-  // LinkedHashSet<?>.
+  // Casting from CompactHashSet<NestedSet<? extends E>> to CompactHashSet<NestedSet<E>> by way of
+  // CompactHashSet<?>.
   @SuppressWarnings("unchecked")
   public NestedSet<E> build() {
     if (isEmpty()) {
@@ -143,74 +145,45 @@
     // This cast is safe because NestedSets are immutable -- we will never try to add an element to
     // these nested sets, only to retrieve elements from them. Thus, treating them as NestedSet<E>
     // is safe.
-    LinkedHashSet<NestedSet<E>> transitiveSetsCast =
-        (LinkedHashSet<NestedSet<E>>) (LinkedHashSet<?>) transitiveSets;
+    CompactHashSet<NestedSet<E>> transitiveSetsCast =
+        (CompactHashSet<NestedSet<E>>) (CompactHashSet<?>) transitiveSets;
     if (items.isEmpty() && (transitiveSetsCast.size() == 1)) {
       NestedSet<E> candidate = getOnlyElement(transitiveSetsCast);
       if (candidate.getOrder().equals(order)) {
         return candidate;
       }
     }
-    int transitiveSize = transitiveSets.size();
-    int directSize = items.size();
-
-    switch (transitiveSize) {
-      case 0:
-        switch (directSize) {
-          case 0:
-            return order.emptySet();
-          case 1:
-            return order.factory.oneDirect(getOnlyElement(items));
-          default:
-            return order.factory.onlyDirects(items.toArray());
-        }
-      case 1:
-        switch (directSize) {
-          case 0:
-            return order.factory.onlyOneTransitive(getOnlyElement(transitiveSetsCast));
-          case 1:
-            return order.factory.oneDirectOneTransitive(getOnlyElement(items),
-                getOnlyElement(transitiveSetsCast));
-          default:
-            return order.factory.manyDirectsOneTransitive(items.toArray(),
-                getOnlyElement(transitiveSetsCast));
-        }
-      default:
-        switch (directSize) {
-          case 0:
-            return order.factory.onlyManyTransitives(
-                transitiveSetsCast.toArray(new NestedSet[transitiveSize]));
-          case 1:
-            return order.factory.oneDirectManyTransitive(getOnlyElement(items), transitiveSetsCast
-                .toArray(new NestedSet[transitiveSize]));
-          default:
-            return order.factory.manyDirectManyTransitive(items.toArray(),
-                transitiveSetsCast.toArray(new NestedSet[transitiveSize]));
-        }
-    }
+    return new NestedSet<E>(order, items, transitiveSetsCast);
   }
 
+  private static final ConcurrentMap<ImmutableList<?>, NestedSet<?>> immutableListCache =
+      new MapMaker().weakKeys().makeMap();
+
   /**
    * Creates a nested set from a given list of items.
-   *
-   * <p>If the list of items is an {@link ImmutableList}, reuses the list as the backing store for
-   * the nested set.
    */
+  @SuppressWarnings("unchecked")
   public static <E> NestedSet<E> wrap(Order order, Iterable<E> wrappedItems) {
     ImmutableList<E> wrappedList = ImmutableList.copyOf(wrappedItems);
     if (wrappedList.isEmpty()) {
       return order.emptySet();
-    } else if (wrappedList.size() == 1) {
-      return order.factory.oneDirect(getOnlyElement(wrappedItems));
+    } else if (order == Order.STABLE_ORDER
+               && wrappedList == wrappedItems && wrappedList.size() > 1) {
+      NestedSet<?> cached = immutableListCache.get(wrappedList);
+      if (cached != null) {
+        return (NestedSet<E>) cached;
+      }
+      NestedSet<E> built = new NestedSetBuilder<E>(order).addAll(wrappedList).build();
+      immutableListCache.putIfAbsent(wrappedList, built);
+      return built;
     } else {
-      return order.factory.onlyDirects(wrappedList);
+      return new NestedSetBuilder<E>(order).addAll(wrappedList).build();
     }
   }
 
-
-    /**
-     * Creates a nested set with the given list of items as its elements.
-     */
+  /**
+   * Creates a nested set with the given list of items as its elements.
+   */
   @SuppressWarnings("unchecked")
   public static <E> NestedSet<E> create(Order order, E... elems) {
     return wrap(order, ImmutableList.copyOf(elems));
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetExpander.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetExpander.java
deleted file mode 100644
index d79fb98..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetExpander.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-import com.google.common.collect.ImmutableCollection;
-
-/**
- * An expander that converts a nested set into a flattened collection.
- *
- * <p>Expanders are initialized statically (there is one for each order), so they should
- * contain no state and all methods must be threadsafe.
- */
-interface NestedSetExpander<E> {
-  /**
-   * Flattens the NestedSet into the builder.
-   */
-  void expandInto(NestedSet<E> nestedSet, Uniqueifier uniqueifier,
-      ImmutableCollection.Builder<E> builder);
-}
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetFactory.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetFactory.java
deleted file mode 100644
index 29a3a52..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetFactory.java
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-import com.google.common.collect.ImmutableList;
-
-/**
- * Factory methods for creating {@link NestedSet}s of specific shapes. This allows the
- * implementation to be memory efficient (e.g. a specialized implementation for the case where
- * there are only direct elements, etc).
- *
- * <p>It's intended for each {@link Order} to have its own factory implementation. That way we can
- * be even more efficient since the {@link NestedSet}s instances don't need to store their
- * {@link Order}.
- */
-interface NestedSetFactory {
-
-  /** Create a NestedSet with just one direct element and not transitive elements. */
-  <E> NestedSet<E> oneDirect(E element);
-
-  /** Create a NestedSet with only direct elements. */
-  <E> NestedSet<E> onlyDirects(Object[] directs);
-
-  /** Create a NestedSet with only direct elements potentially sharing the ImmutableList. */
-  <E> NestedSet<E> onlyDirects(ImmutableList<E> directs);
-
-  /** Create a NestedSet with one direct element and one transitive {@code NestedSet}. */
-  <E> NestedSet<E> oneDirectOneTransitive(E direct, NestedSet<E> transitive);
-
-  /** Create a NestedSet with many direct elements and one transitive {@code NestedSet}. */
-  <E> NestedSet<E> manyDirectsOneTransitive(Object[] direct, NestedSet<E> transitive);
-
-  /** Create a NestedSet with no direct elements and one transitive {@code NestedSet.} */
-  <E> NestedSet<E> onlyOneTransitive(NestedSet<E> transitive);
-
-  /** Create a NestedSet with no direct elements and many transitive {@code NestedSet}s. */
-  <E> NestedSet<E> onlyManyTransitives(NestedSet[] transitives);
-
-  /** Create a NestedSet with one direct elements and many transitive {@code NestedSet}s. */
-  <E> NestedSet<E> oneDirectManyTransitive(Object direct, NestedSet[] transitive);
-
-  /** Create a NestedSet with many direct elements and many transitive {@code NestedSet}s. */
-  <E> NestedSet<E> manyDirectManyTransitive(Object[] directs, NestedSet[] transitive);
-}
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetLazyIterator.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetLazyIterator.java
deleted file mode 100644
index 8caffbb..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetLazyIterator.java
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-import java.util.Iterator;
-
-/**
- * A NestedSet iterator that only expands the NestedSet when the first element is requested. This
- * allows code that calls unconditionally to {@code hasNext} to check if the iterator is empty
- * to not expand the nested set.
- */
-final class NestedSetLazyIterator<E> implements Iterator<E> {
-
-  private NestedSet<E> nestedSet;
-  private Iterator<E> delegate = null;
-
-  NestedSetLazyIterator(NestedSet<E> nestedSet) {
-    this.nestedSet = nestedSet;
-  }
-
-  @Override
-  public boolean hasNext() {
-    if (delegate == null) {
-      return !nestedSet.isEmpty();
-    }
-    return delegate.hasNext();
-  }
-
-  @Override
-  public E next() {
-    if (delegate == null) {
-      delegate = nestedSet.toCollection().iterator();
-      nestedSet = null;
-    }
-    return delegate.next();
-  }
-
-  @Override
-  public void remove() {
-    throw new UnsupportedOperationException();
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetVisitor.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetVisitor.java
index 935212a..705653f 100644
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetVisitor.java
+++ b/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetVisitor.java
@@ -34,7 +34,7 @@
 public final class NestedSetVisitor<E> {
 
   /**
-   * For each element of the NestedSet the {@code Reciver} will receive one element during the
+   * For each element of the NestedSet the {@code Receiver} will receive one element during the
    * visitation.
    */
   public interface Receiver<E> {
@@ -56,41 +56,34 @@
    * @param nestedSet the nested set to visit transitively.
    *
    */
-  @SuppressWarnings("unchecked")
   public void visit(NestedSet<E> nestedSet) {
-    // This method suppresses the unchecked warning so that it can access the internal NestedSet
-    // raw structure.
     Preconditions.checkArgument(nestedSet.getOrder() == Order.STABLE_ORDER);
-    if (!visited.add(nestedSet)) {
-      return;
-    }
+    visitRaw(nestedSet.rawChildren());
+  }
 
-    for (NestedSet<E> subset : nestedSet.transitiveSets()) {
-      visit(subset);
-    }
-    for (Object member : nestedSet.directMembers()) {
-      if (visited.add((E) member)) {
-        callback.accept((E) member);
+  @SuppressWarnings("unchecked")
+  private void visitRaw(Object node) {
+    if (visited.add(node)) {
+      if (node instanceof Object[]) {
+        for (Object child : (Object[]) node) {
+          visitRaw(child);
+        }
+      } else {
+        callback.accept((E) node);
       }
     }
   }
 
   /** A class that allows us to keep track of the seen nodes and transitive sets. */
   public static class VisitedState<E> {
-    private final Set<NestedSet<E>> seenSets = Sets.newConcurrentHashSet();
-    private final Set<E> seenNodes = Sets.newConcurrentHashSet();
+    private final Set<Object> seenNodes = Sets.newConcurrentHashSet();
 
     public void clear() {
-      seenSets.clear();
       seenNodes.clear();
     }
 
-    private boolean add(E node) {
+    private boolean add(Object node) {
       return seenNodes.add(node);
     }
-
-    private boolean add(NestedSet<E> set) {
-      return !set.isEmpty() && seenSets.add(set);
-    }
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/OneDirectManyTransitive.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/OneDirectManyTransitive.java
deleted file mode 100644
index 5469420..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/OneDirectManyTransitive.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-import java.util.Arrays;
-import java.util.Objects;
-
-import javax.annotation.Nullable;
-
-/**
- * Memory-optimized NestedSet implementation for NestedSets with one direct element and
- * many transitive dependencies.
- */
-abstract class OneDirectManyTransitive<E> extends MemoizedUniquefierNestedSet<E> {
-
-  private final Object direct;
-  private final NestedSet[] transitives;
-  private Object memo;
-
-  OneDirectManyTransitive(Object direct, NestedSet[] transitives) {
-    this.direct = direct;
-    this.transitives = transitives;
-  }
-
-  @Override
-  Object getMemo() { return memo; }
-
-  @Override
-  void setMemo(Object memo) { this.memo = memo; }
-
-  @Override
-  Object[] directMembers() { return new Object[]{direct}; }
-
-  @Override
-  NestedSet[] transitiveSets() { return transitives; }
-
-  @Override
-  public boolean shallowEquals(@Nullable NestedSet<? extends E> other) {
-    if (this == other) {
-      return true;
-    }
-    return other != null
-        && getOrder().equals(other.getOrder())
-        && other instanceof OneDirectManyTransitive
-        && direct.equals(((OneDirectManyTransitive) other).direct)
-        && Arrays.equals(transitives, ((OneDirectManyTransitive) other).transitives);
-  }
-
-  @Override
-  public int shallowHashCode() {
-    return Objects.hash(getOrder(), direct, Arrays.hashCode(transitives)); }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/OneDirectOneTransitiveNestedSet.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/OneDirectOneTransitiveNestedSet.java
deleted file mode 100644
index 802963d..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/OneDirectOneTransitiveNestedSet.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-import java.util.Objects;
-
-import javax.annotation.Nullable;
-
-/**
- * Memory-efficient implementation for the case where we have one direct element and one
- * transitive NestedSet.
- */
-abstract class OneDirectOneTransitiveNestedSet<E> extends MemoizedUniquefierNestedSet<E> {
-
-  private final E direct;
-  private final NestedSet<E> transitive;
-  private Object memo;
-
-  OneDirectOneTransitiveNestedSet(E direct, NestedSet<E> transitive) {
-    this.direct = direct;
-    this.transitive = transitive;
-  }
-
-  @Override
-  Object getMemo() { return memo; }
-
-  @Override
-  void setMemo(Object memo) { this.memo = memo; }
-
-  @Override
-  Object[] directMembers() { return new Object[]{direct}; }
-
-  @Override
-  NestedSet[] transitiveSets() { return new NestedSet[]{transitive}; }
-
-  @Override
-  public boolean shallowEquals(@Nullable NestedSet<? extends E> other) {
-    if (this == other) {
-      return true;
-    }
-    return other != null
-        && getOrder().equals(other.getOrder())
-        && other instanceof OneDirectOneTransitiveNestedSet
-        && direct.equals(((OneDirectOneTransitiveNestedSet) other).direct)
-        && transitive == ((OneDirectOneTransitiveNestedSet) other).transitive;
-  }
-
-  @Override
-  public int shallowHashCode() { return Objects.hash(getOrder(), direct, transitive); }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/OnlyDirectsNestedSet.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/OnlyDirectsNestedSet.java
deleted file mode 100644
index 1c9cf33..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/OnlyDirectsNestedSet.java
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
-
-import javax.annotation.Nullable;
-
-/**
- * Memory-optimized NestedSet implementation for NestedSets without transitive dependencies.
- *
- */
-abstract class OnlyDirectsNestedSet<E> extends NestedSet<E> {
-
-  private static final NestedSet[] EMPTY = new NestedSet[0];
-  private final Object[] directDeps;
-
-  public OnlyDirectsNestedSet(Object[] directDeps) { this.directDeps = directDeps; }
-
-  @Override
-  public abstract Order getOrder();
-
-  @Override
-  Object[] directMembers() {
-    return directDeps;
-  }
-
-  @Override
-  NestedSet[] transitiveSets() {
-    return EMPTY;
-  }
-
-  @Override
-  public boolean isEmpty() {
-    return false;
-  }
-
-  /**
-   * Currently all the Order implementations return the direct elements in the same order if they
-   * do not have transitive elements. So we skip calling order.getExpander()... and return a view
-   * of the array.
-   */
-  @SuppressWarnings("unchecked")
-  @Override
-  public List<E> toList() {
-    return (List<E>) ImmutableList.copyOf(directDeps);
-  }
-
-  @SuppressWarnings("unchecked")
-  @Override
-  public Set<E> toSet() {
-    return (Set<E>) ImmutableSet.copyOf(directDeps);
-  }
-
-  @Override
-  public boolean shallowEquals(@Nullable NestedSet<? extends E> other) {
-    if (this == other) {
-      return true;
-    }
-    if (other == null) {
-      return false;
-    }
-    return getOrder().equals(other.getOrder())
-        && other instanceof OnlyDirectsNestedSet
-        && Arrays.equals(directDeps, ((OnlyDirectsNestedSet) other).directDeps);
-  }
-
-  @Override
-  public int shallowHashCode() {
-    return Arrays.hashCode(directDeps);
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/OnlyOneTransitiveNestedSet.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/OnlyOneTransitiveNestedSet.java
deleted file mode 100644
index c7daaf8..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/OnlyOneTransitiveNestedSet.java
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-import java.util.Objects;
-
-import javax.annotation.Nullable;
-
-/**
- * Memory-efficient implementation for the case where we only have one transitive NestedSet.
- *
- * <p>Note that we cannot simply delegate to the inner NestedSet because the order could be
- * different and the top-level order is the correct one.
- */
-abstract class OnlyOneTransitiveNestedSet<E> extends MemoizedUniquefierNestedSet<E> {
-
-  private static final Object[] EMPTY = new Object[0];
-
-  private final NestedSet<E> transitive;
-  private Object memo;
-
-  public OnlyOneTransitiveNestedSet(NestedSet<E> transitive) {
-    this.transitive = transitive;
-  }
-
-  @Override
-  Object getMemo() { return memo; }
-
-  @Override
-  void setMemo(Object memo) { this.memo = memo; }
-
-  @Override
-  Object[] directMembers() {
-    return EMPTY;
-  }
-
-  @Override
-  NestedSet[] transitiveSets() {
-    return new NestedSet[]{transitive};
-  }
-
-  @Override
-  public boolean shallowEquals(@Nullable NestedSet<? extends E> other) {
-    if (this == other) {
-      return true;
-    }
-    return other != null
-        && getOrder().equals(other.getOrder())
-        && other instanceof OnlyOneTransitiveNestedSet
-        && transitive == ((OnlyOneTransitiveNestedSet) other).transitive;
-  }
-
-  @Override
-  public int shallowHashCode() {
-    return Objects.hash(getOrder(), transitive);
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/OnlyTransitivesNestedSet.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/OnlyTransitivesNestedSet.java
deleted file mode 100644
index b813e0b..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/OnlyTransitivesNestedSet.java
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-import java.util.Arrays;
-import java.util.Objects;
-
-import javax.annotation.Nullable;
-
-/**
- * Memory-efficient implementation for the case where we have one direct element and one
- * transitive NestedSet.
- */
-abstract class OnlyTransitivesNestedSet<E> extends MemoizedUniquefierNestedSet<E> {
-
-  private static final NestedSet[] EMPTY = new NestedSet[0];
-
-  private final NestedSet[] transitives;
-  private Object memo;
-
-  OnlyTransitivesNestedSet(NestedSet[] transitives) {
-    this.transitives = transitives;
-  }
-
-  @Override
-  Object getMemo() { return memo; }
-
-  @Override
-  void setMemo(Object memo) { this.memo = memo; }
-
-  @Override
-  Object[] directMembers() { return EMPTY; }
-
-  @Override
-  NestedSet[] transitiveSets() { return transitives; }
-
-  @Override
-  public boolean shallowEquals(@Nullable NestedSet<? extends E> other) {
-    if (this == other) {
-      return true;
-    }
-    return other != null
-        && getOrder().equals(other.getOrder())
-        && other instanceof OnlyTransitivesNestedSet
-        && Arrays.equals(transitives, ((OnlyTransitivesNestedSet) other).transitives);
-  }
-
-  @Override
-  public int shallowHashCode() {
-    return Objects.hash(getOrder(), Arrays.hashCode(transitives)); }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/Order.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/Order.java
index 2b41969..175ea3e 100644
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/Order.java
+++ b/src/main/java/com/google/devtools/build/lib/collect/nestedset/Order.java
@@ -23,25 +23,19 @@
  * see CompileOrderExpander, LinkOrderExpander, NaiveLinkOrderExpander.
  */
 public enum Order {
-
-  STABLE_ORDER("stable", new CompileOrderExpander<>(), new StableOrderNestedSetFactory()),
-  COMPILE_ORDER("compile", new CompileOrderExpander<>(), new CompileOrderNestedSetFactory()),
-  LINK_ORDER("link", new LinkOrderExpander<>(), new LinkOrderNestedSetFactory()),
-  NAIVE_LINK_ORDER("naive_link", new NaiveLinkOrderExpander<>(),
-      new NaiveLinkOrderNestedSetFactory());
+  STABLE_ORDER("stable"),
+  COMPILE_ORDER("compile"),
+  LINK_ORDER("link"),
+  NAIVE_LINK_ORDER("naive_link");
 
   private static final ImmutableMap<String, Order> VALUES;
 
   private final String name;
-  private final NestedSetExpander<?> expander;
-  final NestedSetFactory factory;
   private final NestedSet<?> emptySet;
 
-  private Order(String name, NestedSetExpander<?> expander, NestedSetFactory factory) {
+  private Order(String name) {
     this.name = name;
-    this.expander = expander;
-    this.factory = factory;
-    this.emptySet = new EmptyNestedSet<>(this);
+    this.emptySet = new NestedSet<>(this);
   }
 
   /**
@@ -52,14 +46,6 @@
     return (NestedSet<E>) emptySet;
   }
 
-  /**
-   * Returns an empty set of the given ordering.
-   */
-  @SuppressWarnings("unchecked")  // Nested set expanders contain no data themselves.
-  <E> NestedSetExpander<E> expander() {
-    return (NestedSetExpander<E>) expander;
-  }
-
   public String getName() {
     return name;
   }
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/RecordingUniqueifier.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/RecordingUniqueifier.java
deleted file mode 100644
index 7a46606..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/RecordingUniqueifier.java
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.devtools.build.lib.collect.CompactHashSet;
-import com.google.devtools.build.lib.util.Preconditions;
-
-import java.util.BitSet;
-import java.util.Set;
-
-/**
- * A Uniqueifier that records a transcript of its interactions with the underlying Set.  A memo can
- * then be retrieved in the form of another Uniqueifier, and given the same sequence of isUnique
- * calls, the Set interactions can be avoided.
- */
-class RecordingUniqueifier implements Uniqueifier {
-  /**
-   * Unshared byte memos under this length are not constructed.
-   */
-  @VisibleForTesting static final int LENGTH_THRESHOLD = 4096 / 8; // bits -> bytes
-
-  /**
-   * Returned as a marker that memoization was not worthwhile.
-   */
-  private static final Object NO_MEMO = new Object();
-
-  /**
-   * Shared one-byte memos.
-   */
-  private static final byte[][] SHARED_SMALL_MEMOS_1;
-  
-  /**
-   * Shared two-byte memos.
-   */
-  private static final byte[][] SHARED_SMALL_MEMOS_2;
-
-  static {
-    // Create interned arrays for one and two byte memos
-    // The memos always start with 0x3, so some one and two byte arrays can be skipped.
-
-    byte[][] memos1 = new byte[64][1];
-    for (int i = 0; i < 64; i++) {
-      memos1[i][0] = (byte) ((i << 2) | 0x3);
-    }
-    SHARED_SMALL_MEMOS_1 = memos1;
-
-    byte[][] memos2 = new byte[16384][2];
-    for (int i = 0; i < 64; i++) {
-      byte iAdj = (byte) (0x3 | (i << 2));
-      for (int j = 0; j < 256; j++) {
-        int idx = i | (j << 6);
-        memos2[idx][0] = iAdj;
-        memos2[idx][1] = (byte) j;
-      }
-    }
-    SHARED_SMALL_MEMOS_2 = memos2;
-  }
-
-  private final Set<Object> witnessed;
-  private final BitSet memo = new BitSet();
-  private int idx = 0;
-
-  RecordingUniqueifier() {
-    this(256);
-  }
-
-  RecordingUniqueifier(int expectedSize) {
-    this.witnessed = CompactHashSet.createWithExpectedSize(expectedSize);
-  }
-
-  static Uniqueifier createReplayUniqueifier(Object memo) {
-    if (memo == NO_MEMO) {
-      // We receive NO_MEMO for nested sets that are under a size threshold. Therefore, use a
-      // smaller initial size than the default.
-      return new RecordingUniqueifier(64);
-    } else if (memo instanceof Integer) {
-      BitSet bs = new BitSet();
-      bs.set(0, (Integer) memo);
-      return new ReplayUniqueifier(bs);
-    }
-    return new ReplayUniqueifier(BitSet.valueOf((byte[]) memo));
-  }
-
-  @Override
-  public boolean isUnique(Object o) {
-    boolean firstInstance = witnessed.add(o);
-    memo.set(idx++, firstInstance);
-    return firstInstance;
-  }
-
-  /**
-   * Gets the memo of the set interactions.  Do not call isUnique after this point.
-   */
-  Object getMemo() {
-    this.idx = -1; // will cause failures if isUnique is called after getMemo.
-
-    // If the bitset is just a contiguous block of ones, use a length memo
-    int length = memo.length();
-    if (memo.cardinality() == length) {
-      return length; // length-based memo
-    }
-
-    byte[] ba = memo.toByteArray();
-
-    Preconditions.checkState(
-        (length < 2) || ((ba[0] & 3) == 3),
-        "The memo machinery expects memos to always begin with two 1 bits, "
-            + "but instead, this memo starts with %s.", ba[0]);
-    
-    // For short memos, use an interned array for the memo
-    if (ba.length == 1) {
-      return SHARED_SMALL_MEMOS_1[(0xFF & ba[0]) >>> 2]; // shared memo
-    } else if (ba.length == 2) {
-      return SHARED_SMALL_MEMOS_2[((ba[1] & 0xFF) << 6) | ((0xFF & ba[0]) >>> 2)];
-    }
-
-    // For mid-sized cases, skip the memo since it is not worthwhile
-    if (ba.length < LENGTH_THRESHOLD) {
-      return NO_MEMO; // skipped memo
-    }
-
-    return ba; // normal memo
-  }
-
-  private static final class ReplayUniqueifier implements Uniqueifier {
-    private final BitSet memo;
-    private int idx = 0;
-
-    ReplayUniqueifier(BitSet memo) {
-      this.memo = memo;
-    }
-
-    @Override
-    public boolean isUnique(Object o) {
-      return memo.get(idx++);
-    }
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/SingleDirectNestedSet.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/SingleDirectNestedSet.java
deleted file mode 100644
index 8f49e4b..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/SingleDirectNestedSet.java
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterators;
-import com.google.devtools.build.lib.util.Preconditions;
-
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-
-import javax.annotation.Nullable;
-
-/**
- * Memory-efficient implementation for nested sets with one element.
- */
-public abstract class SingleDirectNestedSet<E> extends NestedSet<E> {
-
-  private static final NestedSet[] EMPTY = new NestedSet[0];
-  private final E e;
-
-  public SingleDirectNestedSet(E e) { this.e = Preconditions.checkNotNull(e); }
-
-  @Override
-  public Iterator<E> iterator() { return Iterators.singletonIterator(e); }
-
-  @Override
-  Object[] directMembers() { return new Object[]{e}; }
-
-  @Override
-  NestedSet[] transitiveSets() { return EMPTY; }
-
-  @Override
-  public boolean isEmpty() { return false; }
-
-    @Override
-  public List<E> toList() { return ImmutableList.of(e); }
-
-  @Override
-  public Set<E> toSet() { return ImmutableSet.of(e); }
-
-  @Override
-  public boolean shallowEquals(@Nullable NestedSet<? extends E> other) {
-    if (this == other) {
-      return true;
-    }
-    return other instanceof SingleDirectNestedSet
-        && e.equals(((SingleDirectNestedSet) other).e);
-  }
-
-  @Override
-  public int shallowHashCode() {
-    return e.hashCode();
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/StableOrderNestedSetFactory.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/StableOrderNestedSetFactory.java
deleted file mode 100644
index 56bfd37..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/StableOrderNestedSetFactory.java
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-import com.google.common.collect.ImmutableList;
-
-/**
- * Stable order {@code NestedSet} factory.
- */
-final class StableOrderNestedSetFactory implements NestedSetFactory {
-
-  @Override
-  public <E> NestedSet<E> onlyDirects(Object[] directs) {
-    return new StableOnlyDirectsNestedSet<>(directs);
-  }
-
-  @Override
-  public <E> NestedSet<E> onlyDirects(ImmutableList<E> directs) {
-    return new StableImmutableListDirectsNestedSet<>(directs);
-  }
-
-  @Override
-  public <E> NestedSet<E> oneDirectOneTransitive(E direct, NestedSet<E> transitive) {
-    return new StableOneDirectOneTransitiveNestedSet<>(direct, transitive);
-  }
-
-  @Override
-  public <E> NestedSet<E> manyDirectsOneTransitive(Object[] direct,
-      NestedSet<E> transitive) {
-    return new StableManyDirectOneTransitiveNestedSet<>(direct, transitive);
-  }
-
-  @Override
-  public <E> NestedSet<E> onlyOneTransitive(NestedSet<E> transitive) {
-    return new StableOnlyOneTransitiveNestedSet<>(transitive);
-  }
-
-  @Override
-  public <E> NestedSet<E> onlyManyTransitives(NestedSet[] transitives) {
-    return new StableOnlyTransitivesNestedSet<>(transitives);
-  }
-
-  @Override
-  public <E> NestedSet<E> oneDirectManyTransitive(Object direct, NestedSet[] transitives) {
-    return new StableOneDirectManyTransitive<>(direct, transitives);
-  }
-
-  @Override
-  public <E> NestedSet<E> manyDirectManyTransitive(Object[] directs, NestedSet[] transitives) {
-    return new StableManyDirectManyTransitive<>(directs, transitives);
-  }
-
-  @Override
-  public <E> NestedSet<E> oneDirect(final E element) {
-    return new StableSingleDirectNestedSet<>(element);
-  }
-
-  private static class StableOnlyDirectsNestedSet<E> extends OnlyDirectsNestedSet<E> {
-
-    StableOnlyDirectsNestedSet(Object[] directs) { super(directs); }
-
-    @Override
-    public Order getOrder() { return Order.STABLE_ORDER; }
-  }
-
-  private static class StableOneDirectOneTransitiveNestedSet<E> extends
-      OneDirectOneTransitiveNestedSet<E> {
-
-    private StableOneDirectOneTransitiveNestedSet(E direct, NestedSet<E> transitive) {
-      super(direct, transitive);
-    }
-
-    @Override
-    public Order getOrder() { return Order.STABLE_ORDER; }
-  }
-
-  private static class StableOneDirectManyTransitive<E> extends OneDirectManyTransitive<E> {
-
-    private StableOneDirectManyTransitive(Object direct, NestedSet[] transitive) {
-      super(direct, transitive);
-    }
-
-    @Override
-    public Order getOrder() { return Order.STABLE_ORDER; }
-  }
-
-  private static class StableManyDirectManyTransitive<E> extends ManyDirectManyTransitive<E> {
-
-    private StableManyDirectManyTransitive(Object[] directs, NestedSet[] transitives) {
-      super(directs, transitives);
-    }
-
-    @Override
-    public Order getOrder() { return Order.STABLE_ORDER; }
-  }
-
-  private static class StableOnlyOneTransitiveNestedSet<E> extends OnlyOneTransitiveNestedSet<E> {
-
-    private StableOnlyOneTransitiveNestedSet(NestedSet<E> transitive) { super(transitive); }
-
-    @Override
-    public Order getOrder() { return Order.STABLE_ORDER; }
-  }
-
-  private static class StableManyDirectOneTransitiveNestedSet<E> extends
-      ManyDirectOneTransitiveNestedSet<E> {
-
-    private StableManyDirectOneTransitiveNestedSet(Object[] direct,
-        NestedSet<E> transitive) { super(direct, transitive); }
-
-    @Override
-    public Order getOrder() { return Order.STABLE_ORDER; }
-  }
-
-  private static class StableOnlyTransitivesNestedSet<E> extends OnlyTransitivesNestedSet<E> {
-
-    private StableOnlyTransitivesNestedSet(NestedSet[] transitives) { super(transitives); }
-
-    @Override
-    public Order getOrder() { return Order.STABLE_ORDER; }
-  }
-
-  private static class StableImmutableListDirectsNestedSet<E> extends
-      ImmutableListDirectsNestedSet<E> {
-
-    private StableImmutableListDirectsNestedSet(ImmutableList<E> directs) { super(directs); }
-
-    @Override
-    public Order getOrder() {
-      return Order.STABLE_ORDER;
-    }
-  }
-
-  private static class StableSingleDirectNestedSet<E> extends SingleDirectNestedSet<E> {
-
-    private StableSingleDirectNestedSet(E element) { super(element); }
-
-    @Override
-    public Order getOrder() { return Order.STABLE_ORDER; }
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/Uniqueifier.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/Uniqueifier.java
deleted file mode 100644
index ef17173..0000000
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/Uniqueifier.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-/**
- * Helps reduce a sequence of potentially duplicated elements to a sequence of unique elements.
- */
-interface Uniqueifier {
-
-  /**
-   * Returns true if-and-only-if this is the first time that this {@link Uniqueifier}'s method has
-   * been called with this Object.  This uses Object.equals-type equality for the comparison.
-   */
-  public boolean isUnique(Object o);
-}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ProtoSupport.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ProtoSupport.java
index 715920b..d53d37d 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/ProtoSupport.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ProtoSupport.java
@@ -26,6 +26,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Ordering;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.PrerequisiteArtifacts;
 import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
@@ -401,7 +402,10 @@
   }
 
   private String getProtoInputListFileContents() {
-    return Artifact.joinExecPaths("\n", getFilteredProtoSources());
+    // Sort the file names to make the remote action key independent of the precise deps structure.
+    // compile_protos.py will sort the input list anyway.
+    Iterable<Artifact> sorted = Ordering.natural().immutableSortedCopy(getFilteredProtoSources());
+    return Artifact.joinExecPaths("\n", sorted);
   }
 
   private PathFragment getWorkspaceRelativeOutputDir() {
diff --git a/src/test/java/com/google/devtools/build/lib/collect/nestedset/ExpanderTestBase.java b/src/test/java/com/google/devtools/build/lib/collect/nestedset/ExpanderTestBase.java
index aff8e2c..8877f71 100644
--- a/src/test/java/com/google/devtools/build/lib/collect/nestedset/ExpanderTestBase.java
+++ b/src/test/java/com/google/devtools/build/lib/collect/nestedset/ExpanderTestBase.java
@@ -23,7 +23,6 @@
 
 import org.junit.Test;
 
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -45,7 +44,7 @@
   public void simple() {
     NestedSet<String> s = prepareBuilder("c", "a", "b").build();
 
-    assertTrue(Arrays.equals(simpleResult().toArray(), s.directMembers()));
+    assertEquals(simpleResult(), s.toList());
     assertSetContents(simpleResult(), s);
   }
 
@@ -53,7 +52,7 @@
   public void simpleNoDuplicates() {
     NestedSet<String> s = prepareBuilder("c", "a", "a", "a", "b").build();
 
-    assertTrue(Arrays.equals(simpleResult().toArray(), s.directMembers()));
+    assertEquals(simpleResult(), s.toList());
     assertSetContents(simpleResult(), s);
   }
 
@@ -94,7 +93,6 @@
     NestedSet<String> s1 = prepareBuilder().add("a").add("c").add("b").build();
     NestedSet<String> s2 = prepareBuilder().addAll(ImmutableList.of("a", "c", "b")).build();
 
-    assertTrue(Arrays.equals(s1.directMembers(), s2.directMembers()));
     assertCollectionsEqual(s1.toCollection(), s2.toCollection());
     assertCollectionsEqual(s1.toList(), s2.toList());
     assertCollectionsEqual(Lists.newArrayList(s1), Lists.newArrayList(s2));
@@ -106,7 +104,6 @@
     NestedSet<String> s2 = prepareBuilder().add("a").addAll(ImmutableList.of("b", "c")).add("d")
         .build();
 
-    assertTrue(Arrays.equals(s1.directMembers(), s2.directMembers()));
     assertCollectionsEqual(s1.toCollection(), s2.toCollection());
     assertCollectionsEqual(s1.toList(), s2.toList());
     assertCollectionsEqual(Lists.newArrayList(s1), Lists.newArrayList(s2));
@@ -140,7 +137,6 @@
     NestedSet<String> b = prepareBuilder("b").addTransitive(c).build();
     NestedSet<String> a = prepareBuilder("a").addTransitive(b).build();
 
-    assertTrue(Arrays.equals(new String[]{"a"}, a.directMembers()));
     assertSetContents(chainResult(), a);
   }
 
@@ -151,7 +147,6 @@
     NestedSet<String> b = prepareBuilder("b").addTransitive(d).build();
     NestedSet<String> a = prepareBuilder("a").addTransitive(b).addTransitive(c).build();
 
-    assertTrue(Arrays.equals(new String[]{"a"}, a.directMembers()));
     assertSetContents(diamondResult(), a);
   }
 
@@ -208,20 +203,6 @@
     assertEquals(expanderOrder(), s.getOrder());
   }
 
-  /**
-   * In case we have inner NestedSets with different order (allowed by the builder). We should
-   * maintain the order of the top-level NestedSet.
-   */
-  @Test
-  public void regressionOnOneTransitiveDep() {
-    NestedSet<String> subsub = NestedSetBuilder.<String>stableOrder().add("c").add("a").add("e")
-        .build();
-    NestedSet<String> sub = NestedSetBuilder.<String>stableOrder().add("b").add("d")
-        .addTransitive(subsub).build();
-    NestedSet<String> top = prepareBuilder().addTransitive(sub).build();
-    assertSetContents(nestedResult(), top);
-  }
-
   @Test
   public void nestingValidation() {
     for (Order ordering : Order.values()) {
diff --git a/src/test/java/com/google/devtools/build/lib/collect/nestedset/NestedSetImplTest.java b/src/test/java/com/google/devtools/build/lib/collect/nestedset/NestedSetImplTest.java
index 5ffc7f1..b72f89c 100644
--- a/src/test/java/com/google/devtools/build/lib/collect/nestedset/NestedSetImplTest.java
+++ b/src/test/java/com/google/devtools/build/lib/collect/nestedset/NestedSetImplTest.java
@@ -13,7 +13,6 @@
 // limitations under the License.
 package com.google.devtools.build.lib.collect.nestedset;
 
-import static com.google.common.truth.Truth.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -27,8 +26,6 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import java.util.Arrays;
-
 /**
  * Tests for {@link com.google.devtools.build.lib.collect.nestedset.NestedSet}.
  */
@@ -45,8 +42,7 @@
   public void simple() {
     NestedSet<String> set = nestedSetBuilder("a").build();
 
-    assertTrue(Arrays.equals(new String[]{"a"}, set.directMembers()));
-    assertThat(set.transitiveSets()).isEmpty();
+    assertEquals(ImmutableList.of("a"), set.toList());
     assertFalse(set.isEmpty());
   }
 
@@ -59,15 +55,15 @@
 
   @Test
   public void nestedToString() {
-    NestedSet<String> b = nestedSetBuilder("b").build();
-    NestedSet<String> c = nestedSetBuilder("c").build();
+    NestedSet<String> b = nestedSetBuilder("b1", "b2").build();
+    NestedSet<String> c = nestedSetBuilder("c1", "c2").build();
 
-    assertEquals("{a, {b}}",
+    assertEquals("{{b1, b2}, a}",
       nestedSetBuilder("a").addTransitive(b).build().toString());
-    assertEquals("{a, {b}, {c}}",
+    assertEquals("{{b1, b2}, {c1, c2}, a}",
       nestedSetBuilder("a").addTransitive(b).addTransitive(c).build().toString());
 
-    assertEquals("{b}", nestedSetBuilder().addTransitive(b).build().toString());
+    assertEquals("{b1, b2}", nestedSetBuilder().addTransitive(b).build().toString());
   }
 
   @Test
@@ -148,13 +144,6 @@
     return new SetWrapper<E>(builder.build());
   }
 
-  // Same as flat(), but allows duplicate elements.
-  @SafeVarargs
-  private static <E> SetWrapper<E> flatWithDuplicates(E... directMembers) {
-    return new SetWrapper<E>(
-        NestedSetBuilder.wrap(Order.STABLE_ORDER, ImmutableList.copyOf(directMembers)));
-  }
-
   @SafeVarargs
   private static <E> SetWrapper<E> nest(SetWrapper<E>... nested) {
     NestedSetBuilder<E> builder = NestedSetBuilder.stableOrder();
@@ -191,16 +180,16 @@
       .addEqualityGroup(flat(3),
                         flat(3),
                         flat(3, 3))  // Element de-duplication.
-      .addEqualityGroup(flatWithDuplicates(3, 3))
       .addEqualityGroup(flat(4),
                         nest(flat(4))) // Automatic elision of one-element nested sets.
       .addEqualityGroup(NestedSetBuilder.<Integer>linkOrder().add(4).build())
       .addEqualityGroup(nestedSetBuilder("4").build())  // Like flat("4").
       .addEqualityGroup(flat(3, 4),
                         flat(3, 4))
-      // Shallow equality means that {{3},{5}} != {{3},{5}}.
-      .addEqualityGroup(nest(flat(3), flat(5)))
-      .addEqualityGroup(nest(flat(3), flat(5)))
+      // Make a couple sets deep enough that shallowEquals() fails.
+      // If this test case fails because you improve the representation, just delete it.
+      .addEqualityGroup(nest(nest(flat(3, 4), flat(5)), nest(flat(6, 7), flat(8))))
+      .addEqualityGroup(nest(nest(flat(3, 4), flat(5)), nest(flat(6, 7), flat(8))))
       .addEqualityGroup(nest(myRef),
                         nest(myRef),
                         nest(myRef, myRef))  // Set de-duplication.
diff --git a/src/test/java/com/google/devtools/build/lib/collect/nestedset/RecordingUniqueifierTest.java b/src/test/java/com/google/devtools/build/lib/collect/nestedset/RecordingUniqueifierTest.java
deleted file mode 100644
index 13b363b..0000000
--- a/src/test/java/com/google/devtools/build/lib/collect/nestedset/RecordingUniqueifierTest.java
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// 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.devtools.build.lib.collect.nestedset;
-
-import static org.junit.Assert.assertEquals;
-
-import com.google.common.collect.ImmutableList;
-import com.google.devtools.build.lib.util.Preconditions;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Random;
-
-/**
- * Tests for {@link RecordingUniqueifier}.
- */
-@RunWith(JUnit4.class)
-public class RecordingUniqueifierTest {
-
-  private static final Random RANDOM = new Random();
-  
-  private static final int VERY_SMALL = 3; // one byte
-  private static final int SMALL = 11;     // two bytes
-  private static final int MEDIUM = 18;    // three bytes -- unmemoed
-  // For this one, the "* 8" is a bytes to bits (1 memo is 1 bit)
-  private static final int LARGE = (RecordingUniqueifier.LENGTH_THRESHOLD * 8) + 3;
-
-  private static final int[] SIZES = new int[] {VERY_SMALL, SMALL, MEDIUM, LARGE};
-  
-  private void doTest(int uniqueInputs, int deterministicHeadSize) throws Exception {
-    Preconditions.checkArgument(deterministicHeadSize <= uniqueInputs,
-        "deterministicHeadSize must be smaller than uniqueInputs");
-
-      // Setup
-
-      List<Integer> inputList = new ArrayList<>(uniqueInputs);
-      Collection<Integer> inputsDeduped = new LinkedHashSet<>(uniqueInputs);
-
-      for (int i = 0; i < deterministicHeadSize; i++) { // deterministic head
-        inputList.add(i);
-        inputsDeduped.add(i);
-      }
-
-      while (inputsDeduped.size() < uniqueInputs) { // random selectees
-        Integer i = RANDOM.nextInt(uniqueInputs);
-        inputList.add(i);
-        inputsDeduped.add(i);
-      }
-
-      // Unmemoed run
-
-      List<Integer> firstList = new ArrayList<>(uniqueInputs);
-      RecordingUniqueifier recordingUniqueifier = new RecordingUniqueifier();
-      for (Integer i : inputList) {
-        if (recordingUniqueifier.isUnique(i)) {
-          firstList.add(i);
-        }
-      }
-
-      // Potentially memo'ed run
-
-      List<Integer> secondList = new ArrayList<>(uniqueInputs);
-      Object memo = recordingUniqueifier.getMemo();
-      Uniqueifier uniqueifier = RecordingUniqueifier.createReplayUniqueifier(memo);
-      for (Integer i : inputList) {
-        if (uniqueifier.isUnique(i)) {
-          secondList.add(i);
-        }
-      }
-
-      // Evaluate results
-
-      inputsDeduped = ImmutableList.copyOf(inputsDeduped);
-      assertEquals("Unmemo'ed run has unexpected contents", inputsDeduped, firstList);
-      assertEquals("Memo'ed run has unexpected contents", inputsDeduped, secondList);
-  }
-
-  private void doTestWithLucidException(int uniqueInputs, int deterministicHeadSize)
-      throws Exception {
-    try {
-      doTest(uniqueInputs, deterministicHeadSize);
-    } catch (Exception e) {
-      throw new Exception("Failure in size: " + uniqueInputs, e);
-    }
-  }
-
-  @Test
-  public void noInputs() throws Exception {
-    doTestWithLucidException(0, 0);
-  }
-  
-  @Test
-  public void allUnique() throws Exception {
-    for (int size : SIZES) {
-      doTestWithLucidException(size, size);
-    }
-  }
-
-  @Test
-  public void fuzzedWithDeterministic2() throws Exception {
-    // The way that it is used, we know that the first two additions are not equal.
-    // Optimizations were made for this case in small memos.
-    for (int size : SIZES) {
-      doTestWithLucidException(size, 2);
-    }
-  }
-
-  @Test
-  public void fuzzedWithDeterministic2_otherSizes() throws Exception {
-    for (int i = 0; i < 100; i++) {
-      int size = RANDOM.nextInt(10000) + 2;
-      doTestWithLucidException(size, 2);
-    }
-  }
-}
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
index 7679e39..73e5f1b 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
@@ -654,7 +654,7 @@
   public void testClassObjectCannotAccessNestedSet() throws Exception {
     new SkylarkTest()
         .update("mock", new MockClassObject())
-        .testIfExactError("Type is not allowed in Skylark: EmptyNestedSet", "v = mock.nset");
+        .testIfExactError("Type is not allowed in Skylark: NestedSet", "v = mock.nset");
   }
 
   @Test
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkNestedSetTest.java b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkNestedSetTest.java
index 979b945..dae2667 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkNestedSetTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkNestedSetTest.java
@@ -178,38 +178,6 @@
   }
 
   @Test
-  public void testSetOuterOrderWins() throws Exception {
-    // The order of the outer set should define the final iteration order,
-    // no matter what the order of nested sets is
-    /*
-     * Set:     {4, 44, {1, 11, {2, 22}}}
-     * PRE:     4, 44, 1, 11, 2, 22     (Link)
-     * POST:    2, 22, 1, 11, 4, 44     (Stable)
-     *
-     */
-    Order[] orders = {Order.STABLE_ORDER, Order.LINK_ORDER};
-    String[] expected = {
-        "set([\"2\", \"22\", \"1\", \"11\", \"4\", \"44\"])",
-        "set([\"4\", \"44\", \"1\", \"11\", \"2\", \"22\"], order = \"link\")"};
-
-    for (int i = 0; i < 2; ++i) {
-      Order outerOrder = orders[i];
-      Order innerOrder = orders[1 - i];
-
-      SkylarkNestedSet inner1 =
-          new SkylarkNestedSet(innerOrder, Tuple.of("1", "11"), null);
-      SkylarkNestedSet inner2 =
-          new SkylarkNestedSet(innerOrder, Tuple.of("2", "22"), null);
-      SkylarkNestedSet innerUnion = new SkylarkNestedSet(inner1, inner2, null);
-      SkylarkNestedSet result =
-          new SkylarkNestedSet(outerOrder, Tuple.of("4", "44"), null);
-      result = new SkylarkNestedSet(result, innerUnion, null);
-
-      assertThat(result.toString()).isEqualTo(expected[i]);
-    }
-  }
-
-  @Test
   public void testSetOrderCompatibility() throws Exception {
     // Two sets are compatible if
     //  (a) both have the same order or