Change return type of directMethods() and virtualMethods() to Iterable

Bug: 155595879
Change-Id: Iee803b99651be79a9988ccbb250fdccb135791f7
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index bd4c6da..c4040a2 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -51,6 +51,7 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.DexVersion;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.LebUtils;
 import com.google.common.collect.Sets;
 import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
@@ -633,8 +634,8 @@
   }
 
   private void writeEncodedMethods(
-      List<DexEncodedMethod> unsortedMethods, boolean isSharedSynthetic) {
-    List<DexEncodedMethod> methods = new ArrayList<>(unsortedMethods);
+      Iterable<DexEncodedMethod> unsortedMethods, boolean isSharedSynthetic) {
+    List<DexEncodedMethod> methods = IterableUtils.toNewArrayList(unsortedMethods);
     methods.sort((a, b) -> a.method.slowCompareTo(b.method, namingLens));
     int currentOffset = 0;
     for (DexEncodedMethod method : methods) {
@@ -666,8 +667,8 @@
     mixedSectionOffsets.setOffsetFor(clazz, dest.position());
     dest.putUleb128(clazz.staticFields().size());
     dest.putUleb128(clazz.instanceFields().size());
-    dest.putUleb128(clazz.directMethods().size());
-    dest.putUleb128(clazz.virtualMethods().size());
+    dest.putUleb128(clazz.getMethodCollection().numberOfDirectMethods());
+    dest.putUleb128(clazz.getMethodCollection().numberOfVirtualMethods());
     writeEncodedFields(clazz.staticFields());
     writeEncodedFields(clazz.instanceFields());
     boolean isSharedSynthetic = clazz.getSynthesizedFrom().size() > 1;
diff --git a/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java b/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java
index c83010d..d24a75b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java
+++ b/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java
@@ -78,8 +78,7 @@
 
   private boolean anyMethodMatches(DexClass clazz) {
     return !options.hasMethodsFilter()
-        || clazz.virtualMethods().stream().anyMatch(options::methodMatchesFilter)
-        || clazz.directMethods().stream().anyMatch(options::methodMatchesFilter);
+        || clazz.getMethodCollection().hasMethods(options::methodMatchesFilter);
   }
 
   private void writeClass(DexProgramClass clazz, PrintStream ps) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 5bcb25c..c2cdc32 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -138,7 +138,7 @@
     throw new Unreachable();
   }
 
-  public List<DexEncodedMethod> directMethods() {
+  public Iterable<DexEncodedMethod> directMethods() {
     return methodCollection.directMethods();
   }
 
@@ -158,7 +158,7 @@
     methodCollection.setDirectMethods(methods);
   }
 
-  public List<DexEncodedMethod> virtualMethods() {
+  public Iterable<DexEncodedMethod> virtualMethods() {
     return methodCollection.virtualMethods();
   }
 
@@ -411,6 +411,11 @@
     return methodCollection.getMethod(method);
   }
 
+  /** Find method in this class matching {@param method}. */
+  public DexEncodedMethod lookupMethod(Predicate<DexEncodedMethod> predicate) {
+    return methodCollection.getMethod(predicate);
+  }
+
   public DexEncodedMethod lookupSignaturePolymorphicMethod(
       DexString methodName, DexItemFactory factory) {
     if (type != factory.methodHandleType && type != factory.varHandleType) {
@@ -817,7 +822,7 @@
 
   public boolean isValid(InternalOptions options) {
     assert verifyNoAbstractMethodsOnNonAbstractClasses(virtualMethods(), options);
-    assert !isInterface() || virtualMethods().stream().noneMatch(DexEncodedMethod::isFinal);
+    assert !isInterface() || !getMethodCollection().hasVirtualMethods(DexEncodedMethod::isFinal);
     assert verifyCorrectnessOfFieldHolders(fields());
     assert verifyNoDuplicateFields();
     assert methodCollection.verify();
diff --git a/src/main/java/com/android/tools/r8/graph/MethodCollection.java b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
index ac2685a..bad82e1 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
@@ -2,13 +2,11 @@
 
 import static com.google.common.base.Predicates.alwaysTrue;
 
-import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.TraversalContinuation;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 import java.util.function.Consumer;
@@ -50,6 +48,26 @@
     // Nothing to do.
   }
 
+  public boolean hasMethods(Predicate<DexEncodedMethod> predicate) {
+    return getMethod(predicate) != null;
+  }
+
+  public boolean hasDirectMethods() {
+    return hasDirectMethods(alwaysTrue());
+  }
+
+  public boolean hasDirectMethods(Predicate<DexEncodedMethod> predicate) {
+    return backing.getDirectMethod(predicate) != null;
+  }
+
+  public boolean hasVirtualMethods() {
+    return hasVirtualMethods(alwaysTrue());
+  }
+
+  public boolean hasVirtualMethods(Predicate<DexEncodedMethod> predicate) {
+    return backing.getVirtualMethod(predicate) != null;
+  }
+
   public int numberOfDirectMethods() {
     return backing.numberOfDirectMethods();
   }
@@ -123,17 +141,11 @@
     return sorted;
   }
 
-  public List<DexEncodedMethod> directMethods() {
-    if (InternalOptions.assertionsEnabled()) {
-      return Collections.unmodifiableList(backing.directMethods());
-    }
+  public Iterable<DexEncodedMethod> directMethods() {
     return backing.directMethods();
   }
 
-  public List<DexEncodedMethod> virtualMethods() {
-    if (InternalOptions.assertionsEnabled()) {
-      return Collections.unmodifiableList(backing.virtualMethods());
-    }
+  public Iterable<DexEncodedMethod> virtualMethods() {
     return backing.virtualMethods();
   }
 
@@ -141,6 +153,11 @@
     return backing.getMethod(method);
   }
 
+  public DexEncodedMethod getMethod(Predicate<DexEncodedMethod> predicate) {
+    DexEncodedMethod result = backing.getDirectMethod(predicate);
+    return result != null ? result : backing.getVirtualMethod(predicate);
+  }
+
   public DexEncodedMethod getDirectMethod(DexMethod method) {
     return backing.getDirectMethod(method);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/MethodCollectionBacking.java b/src/main/java/com/android/tools/r8/graph/MethodCollectionBacking.java
index 56ab228..2ce0cd8 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodCollectionBacking.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodCollectionBacking.java
@@ -7,7 +7,6 @@
 
 import com.android.tools.r8.utils.TraversalContinuation;
 import java.util.Collection;
-import java.util.List;
 import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -65,9 +64,9 @@
 
   abstract Iterable<DexEncodedMethod> methods();
 
-  abstract List<DexEncodedMethod> directMethods();
+  abstract Iterable<DexEncodedMethod> directMethods();
 
-  abstract List<DexEncodedMethod> virtualMethods();
+  abstract Iterable<DexEncodedMethod> virtualMethods();
 
   // Lookup methods.
 
diff --git a/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java b/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java
index d34ba36..4ba116d 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java
@@ -4,14 +4,13 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.IteratorUtils;
 import com.android.tools.r8.utils.MethodSignatureEquivalence;
 import com.android.tools.r8.utils.TraversalContinuation;
 import com.google.common.base.Equivalence.Wrapper;
 import it.unimi.dsi.fastutil.objects.Object2ReferenceLinkedOpenHashMap;
 import it.unimi.dsi.fastutil.objects.Object2ReferenceMap;
-import java.util.ArrayList;
 import java.util.Collection;
-import java.util.List;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.function.Function;
@@ -107,27 +106,13 @@
   }
 
   @Override
-  List<DexEncodedMethod> directMethods() {
-    List<DexEncodedMethod> methods = new ArrayList<>(size());
-    forEachMethod(
-        method -> {
-          if (belongsToDirectPool(method)) {
-            methods.add(method);
-          }
-        });
-    return methods;
+  Iterable<DexEncodedMethod> directMethods() {
+    return () -> IteratorUtils.filter(methodMap.values().iterator(), this::belongsToDirectPool);
   }
 
   @Override
-  List<DexEncodedMethod> virtualMethods() {
-    List<DexEncodedMethod> methods = new ArrayList<>(size());
-    forEachMethod(
-        method -> {
-          if (belongsToVirtualPool(method)) {
-            methods.add(method);
-          }
-        });
-    return methods;
+  Iterable<DexEncodedMethod> virtualMethods() {
+    return () -> IteratorUtils.filter(methodMap.values().iterator(), this::belongsToVirtualPool);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index 72f630b..b32ab3b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -28,11 +28,9 @@
 import com.google.common.base.Equivalence.Wrapper;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableList.Builder;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.IdentityHashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.BiConsumer;
@@ -234,15 +232,13 @@
     assert iface.superType == dexItemFactory.objectType;
     // Add non-library default methods as well as those for desugared library classes.
     if (!iface.isLibraryClass() || (needsLibraryInfo() && rewriter.isInDesugaredLibrary(iface))) {
-      List<DexEncodedMethod> methods = iface.virtualMethods();
-      List<Wrapper<DexMethod>> additions = new ArrayList<>(methods.size());
-      for (DexEncodedMethod method : methods) {
-        if (method.isDefaultMethod()) {
-          additions.add(equivalence.wrap(method.method));
-        }
+      Set<Wrapper<DexMethod>> additions =
+          new HashSet<>(iface.getMethodCollection().numberOfVirtualMethods());
+      for (DexEncodedMethod method : iface.virtualMethods(DexEncodedMethod::isDefaultMethod)) {
+        additions.add(equivalence.wrap(method.method));
       }
       if (!additions.isEmpty()) {
-        signatures = signatures.merge(MethodSignatures.create(new HashSet<>(additions)));
+        signatures = signatures.merge(MethodSignatures.create(additions));
       }
     }
     return signatures;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 1f6af29..878a5e0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -44,6 +44,7 @@
 import com.android.tools.r8.position.MethodPosition;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
@@ -888,6 +889,10 @@
     theInterface.setDirectMethods(renameHolder(theInterface.directMethods(), renamedInterface));
   }
 
+  private DexEncodedMethod[] renameHolder(Iterable<DexEncodedMethod> methods, DexType newName) {
+    return renameHolder(IterableUtils.toNewArrayList(methods), newName);
+  }
+
   private DexEncodedMethod[] renameHolder(List<DexEncodedMethod> methods, DexType newName) {
     DexEncodedMethod[] newMethods = new DexEncodedMethod[methods.size()];
     for (int i = 0; i < newMethods.length; i++) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index f1a499a..a277a71 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -115,7 +115,7 @@
     }
 
     // If at least one bridge method was removed then update the table.
-    if (remainingMethods.size() < iface.virtualMethods().size()) {
+    if (remainingMethods.size() < iface.getMethodCollection().numberOfVirtualMethods()) {
       iface.setVirtualMethods(remainingMethods.toArray(DexEncodedMethod.EMPTY_ARRAY));
     }
     remainingMethods.clear();
@@ -181,7 +181,7 @@
         }
       }
     }
-    if (remainingMethods.size() < iface.directMethods().size()) {
+    if (remainingMethods.size() < iface.getMethodCollection().numberOfDirectMethods()) {
       iface.setDirectMethods(remainingMethods.toArray(DexEncodedMethod.EMPTY_ARRAY));
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
index d138d95..7ae1ffa 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
@@ -71,7 +71,7 @@
       enumUnboxer.reportFailure(clazz.type, Reason.UNEXPECTED_STATIC_FIELD);
       return false;
     }
-    if (!clazz.virtualMethods().isEmpty()) {
+    if (clazz.getMethodCollection().hasVirtualMethods()) {
       enumUnboxer.reportFailure(clazz.type, Reason.VIRTUAL_METHOD);
       return false;
     }
@@ -89,7 +89,7 @@
     //     public static ** valueOf(java.lang.String);
     // }
     // In general there will be 4 methods, unless the enum keep rule is not present.
-    if (clazz.directMethods().size() > 4) {
+    if (clazz.getMethodCollection().numberOfDirectMethods() > 4) {
       enumUnboxer.reportFailure(clazz.type, Reason.UNEXPECTED_DIRECT_METHOD);
       return false;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index 1e7fb3c..5742de4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -812,28 +812,32 @@
     hostClass.setStaticFields(newFields);
 
     // Process static methods.
-    List<DexEncodedMethod> extraMethods = candidateClass.directMethods();
-    if (!extraMethods.isEmpty()) {
-      List<DexEncodedMethod> newMethods = new ArrayList<>(extraMethods.size());
-      for (DexEncodedMethod method : extraMethods) {
-        DexEncodedMethod newMethod = method.toTypeSubstitutedMethod(
-            factory().createMethod(hostType, method.method.proto, method.method.name));
-        newMethods.add(newMethod);
-        // If the old method from the candidate class has been staticized,
-        if (staticizedMethods.remove(method)) {
-          // Properly update staticized methods to reprocess, i.e., add the corresponding one that
-          // has just been migrated to the host class.
-          staticizedMethods.createAndAdd(hostClass, newMethod);
-        }
-        DexMethod originalMethod = methodMapping.inverse().get(method.method);
-        if (originalMethod == null) {
-          methodMapping.put(method.method, newMethod.method);
-        } else {
-          methodMapping.put(originalMethod, newMethod.method);
-        }
-      }
-      hostClass.addDirectMethods(newMethods);
+    if (!candidateClass.getMethodCollection().hasDirectMethods()) {
+      return;
     }
+
+    Iterable<DexEncodedMethod> extraMethods = candidateClass.directMethods();
+    List<DexEncodedMethod> newMethods =
+        new ArrayList<>(candidateClass.getMethodCollection().numberOfDirectMethods());
+    for (DexEncodedMethod method : extraMethods) {
+      DexEncodedMethod newMethod =
+          method.toTypeSubstitutedMethod(
+              factory().createMethod(hostType, method.method.proto, method.method.name));
+      newMethods.add(newMethod);
+      // If the old method from the candidate class has been staticized,
+      if (staticizedMethods.remove(method)) {
+        // Properly update staticized methods to reprocess, i.e., add the corresponding one that
+        // has just been migrated to the host class.
+        staticizedMethods.createAndAdd(hostClass, newMethod);
+      }
+      DexMethod originalMethod = methodMapping.inverse().get(method.method);
+      if (originalMethod == null) {
+        methodMapping.put(method.method, newMethod.method);
+      } else {
+        methodMapping.put(originalMethod, newMethod.method);
+      }
+    }
+    hostClass.addDirectMethods(newMethods);
   }
 
   private DexField mapCandidateField(DexField field, DexType candidateType, DexType hostType) {
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java b/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java
deleted file mode 100644
index 0fdeebc..0000000
--- a/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.naming;
-
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.utils.DescriptorUtils;
-import com.google.common.collect.Sets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Set;
-
-public class MinifiedNameMapPrinter {
-
-  private static final String NEW_LINE = "\n";
-  private final DexApplication application;
-  private final NamingLens namingLens;
-  private final Set<DexType> seenTypes = Sets.newIdentityHashSet();
-
-  public MinifiedNameMapPrinter(DexApplication application, NamingLens namingLens) {
-    this.application = application;
-    this.namingLens = namingLens;
-  }
-
-  private <T> T[] sortedCopy(T[] source, Comparator<? super T> comparator) {
-    T copy[] = Arrays.copyOf(source, source.length);
-    Arrays.sort(copy, comparator);
-    return copy;
-  }
-
-  private <T> List<T> sortedCopy(List<T> source, Comparator<? super T> comparator) {
-    List<T> copy = new ArrayList<>(source);
-    Collections.sort(copy, comparator);
-    return copy;
-  }
-
-  private void writeClass(DexProgramClass clazz, StringBuilder out) {
-    seenTypes.add(clazz.type);
-    DexString descriptor = namingLens.lookupDescriptor(clazz.type);
-    out.append(DescriptorUtils.descriptorToJavaType(clazz.type.descriptor.toSourceString()));
-    out.append(" -> ");
-    out.append(DescriptorUtils.descriptorToJavaType(descriptor.toSourceString()));
-    out.append(":").append(NEW_LINE);
-    writeFields(sortedCopy(
-        clazz.instanceFields(), Comparator.comparing(DexEncodedField::toSourceString)), out);
-    writeFields(sortedCopy(
-        clazz.staticFields(), Comparator.comparing(DexEncodedField::toSourceString)), out);
-    writeMethods(sortedCopy(
-        clazz.directMethods(), Comparator.comparing(DexEncodedMethod::toSourceString)), out);
-    writeMethods(sortedCopy(
-        clazz.virtualMethods(), Comparator.comparing(DexEncodedMethod::toSourceString)), out);
-  }
-
-  private void writeType(DexType type, StringBuilder out) {
-    if (type.isClassType() && seenTypes.add(type)) {
-      DexString descriptor = namingLens.lookupDescriptor(type);
-      out.append(DescriptorUtils.descriptorToJavaType(type.descriptor.toSourceString()));
-      out.append(" -> ");
-      out.append(DescriptorUtils.descriptorToJavaType(descriptor.toSourceString()));
-      out.append(":").append(NEW_LINE);
-    }
-  }
-
-  private void writeFields(List<DexEncodedField> fields, StringBuilder out) {
-    for (DexEncodedField encodedField : fields) {
-      DexField field = encodedField.field;
-      DexString renamed = namingLens.lookupName(field);
-      if (renamed != field.name) {
-        out.append("    ");
-        out.append(field.type.toSourceString());
-        out.append(" ");
-        out.append(field.name.toSourceString());
-        out.append(" -> ");
-        out.append(renamed.toSourceString()).append(NEW_LINE);
-      }
-    }
-  }
-
-  private void writeMethod(MethodSignature signature, String renamed, StringBuilder out) {
-    out.append("    ");
-    out.append(signature.toString());
-    out.append(" -> ");
-    out.append(renamed).append(NEW_LINE);
-  }
-
-  private void writeMethods(List<DexEncodedMethod> methods, StringBuilder out) {
-    for (DexEncodedMethod encodedMethod : methods) {
-      DexMethod method = encodedMethod.method;
-      DexString renamed = namingLens.lookupName(method);
-      if (renamed != method.name) {
-        MethodSignature signature = MethodSignature.fromDexMethod(method);
-        String renamedSourceString = renamed.toSourceString();
-        writeMethod(signature, renamedSourceString, out);
-      }
-    }
-  }
-
-  public void write(StringBuilder out) {
-    // First write out all classes that have been renamed.
-    List<DexProgramClass> classes = new ArrayList<>(application.classes());
-    classes.sort(Comparator.comparing(DexProgramClass::toSourceString));
-    classes.forEach(clazz -> writeClass(clazz, out));
-    // Now write out all types only mentioned in descriptors that have been renamed.
-    namingLens.forAllRenamedTypes(type -> writeType(type, out));
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java b/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java
index 1fcb9e0..b64646d 100644
--- a/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.shaking.ScopedDexMethodSet.AddMethodIfMoreVisibleResult;
+import com.android.tools.r8.utils.IterableUtils;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -42,7 +43,8 @@
     DexClass holder = appView.definitionFor(type);
     scope = scope.newNestedScope();
     if (holder != null && holder.isProgramClass()) {
-      DexEncodedMethod[] newVirtualMethods = processMethods(holder.virtualMethods());
+      DexEncodedMethod[] newVirtualMethods =
+          processMethods(IterableUtils.ensureUnmodifiableList(holder.virtualMethods()));
       if (newVirtualMethods != null) {
         holder.setVirtualMethods(newVirtualMethods);
       }
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index 4bb1b2b..584b84b 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -251,8 +251,9 @@
     }
     assert definition.isInterface();
     boolean liveGetter =
-        definition.virtualMethods().stream()
-            .anyMatch(method -> method.method.name == original.name);
+        definition
+            .getMethodCollection()
+            .hasVirtualMethods(method -> method.method.name == original.name);
     return liveGetter ? original : null;
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
index dc69de3..a96194f 100644
--- a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
@@ -31,6 +31,7 @@
 import com.google.common.collect.Streams;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -267,8 +268,7 @@
       // TODO(b/141452765): Allow class merging between classes in features.
       return MergeGroup.DONT_MERGE;
     }
-    if (clazz.staticFields().size() + clazz.directMethods().size() + clazz.virtualMethods().size()
-        == 0) {
+    if (clazz.staticFields().size() + clazz.getMethodCollection().size() == 0) {
       return MergeGroup.DONT_MERGE;
     }
     if (clazz.instanceFields().size() > 0) {
@@ -278,10 +278,10 @@
         .anyMatch(field -> appView.appInfo().isPinned(field.field))) {
       return MergeGroup.DONT_MERGE;
     }
-    if (clazz.directMethods().stream().anyMatch(DexEncodedMethod::isInitializer)) {
+    if (clazz.getMethodCollection().hasDirectMethods(DexEncodedMethod::isInitializer)) {
       return MergeGroup.DONT_MERGE;
     }
-    if (!clazz.virtualMethods().stream().allMatch(DexEncodedMethod::isPrivateMethod)) {
+    if (clazz.getMethodCollection().hasVirtualMethods(method -> !method.isPrivateMethod())) {
       return MergeGroup.DONT_MERGE;
     }
     if (clazz.isInANest()) {
@@ -427,12 +427,12 @@
       return false;
     }
     // Check that all of the members are private or public.
-    if (!clazz.directMethods().stream()
-        .allMatch(method -> method.accessFlags.isPrivate() || method.accessFlags.isPublic())) {
+    if (clazz
+        .getMethodCollection()
+        .hasDirectMethods(method -> !method.isPrivate() && !method.isPublic())) {
       return false;
     }
-    if (!clazz.staticFields().stream()
-        .allMatch(field -> field.accessFlags.isPrivate() || field.accessFlags.isPublic())) {
+    if (!clazz.staticFields().stream().allMatch(field -> field.isPrivate() || field.isPublic())) {
       return false;
     }
 
@@ -440,7 +440,7 @@
     // virtual methods are private. Therefore, we don't need to consider check if there are any
     // package-private or protected instance fields or virtual methods here.
     assert clazz.instanceFields().size() == 0;
-    assert clazz.virtualMethods().stream().allMatch(method -> method.accessFlags.isPrivate());
+    assert !clazz.getMethodCollection().hasVirtualMethods(method -> !method.isPrivate());
 
     // Check that no methods access package-private or protected members.
     IllegalAccessDetector registry = new IllegalAccessDetector(appView, clazz);
@@ -489,20 +489,20 @@
   }
 
   private List<DexEncodedMethod> mergeMethods(
-      List<DexEncodedMethod> sourceMethods,
-      List<DexEncodedMethod> targetMethods,
+      Iterable<DexEncodedMethod> sourceMethods,
+      Iterable<DexEncodedMethod> targetMethods,
       DexProgramClass targetClass) {
     // Move source methods to result one by one, renaming them if needed.
     MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
-    Set<Wrapper<DexMethod>> existingMethods =
-        targetMethods.stream()
-            .map(targetMethod -> equivalence.wrap(targetMethod.method))
-            .collect(Collectors.toSet());
+    Set<Wrapper<DexMethod>> existingMethods = new HashSet<>();
+    for (DexEncodedMethod targetMethod : targetMethods) {
+      existingMethods.add(equivalence.wrap(targetMethod.method));
+    }
 
     Predicate<DexMethod> availableMethodSignatures =
         method -> !existingMethods.contains(equivalence.wrap(method));
 
-    List<DexEncodedMethod> newMethods = new ArrayList<>(sourceMethods.size());
+    List<DexEncodedMethod> newMethods = new ArrayList<>();
     for (DexEncodedMethod sourceMethod : sourceMethods) {
       DexEncodedMethod sourceMethodAfterMove =
           renameMethodIfNeeded(sourceMethod, targetClass, availableMethodSignatures);
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 6f2010c..6560ebc 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
@@ -254,6 +255,10 @@
     return -1;
   }
 
+  private DexEncodedMethod[] reachableMethods(Iterable<DexEncodedMethod> methods, DexClass clazz) {
+    return reachableMethods(IterableUtils.ensureUnmodifiableList(methods), clazz);
+  }
+
   private DexEncodedMethod[] reachableMethods(List<DexEncodedMethod> methods, DexClass clazz) {
     AppInfoWithLiveness appInfo = appView.appInfo();
     InternalOptions options = appView.options();
diff --git a/src/main/java/com/android/tools/r8/utils/IterableUtils.java b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
index ea35dfb..af3208f 100644
--- a/src/main/java/com/android/tools/r8/utils/IterableUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
@@ -4,10 +4,23 @@
 
 package com.android.tools.r8.utils;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.function.Predicate;
 
 public class IterableUtils {
 
+  public static <T> List<T> ensureUnmodifiableList(Iterable<T> iterable) {
+    List<T> list;
+    if (iterable instanceof List<?>) {
+      list = (List<T>) iterable;
+    } else {
+      list = toNewArrayList(iterable);
+    }
+    return Collections.unmodifiableList(list);
+  }
+
   public static <T> int firstIndexMatching(Iterable<T> iterable, Predicate<T> tester) {
     int i = 0;
     for (T element : iterable) {
@@ -19,7 +32,21 @@
     return -1;
   }
 
-  public static <T> Iterable<T> filter(Iterable<T> methods, Predicate<T> predicate) {
-    return () -> IteratorUtils.filter(methods.iterator(), predicate);
+  public static <T> Iterable<T> filter(Iterable<T> iterable, Predicate<T> predicate) {
+    return () -> IteratorUtils.filter(iterable.iterator(), predicate);
+  }
+
+  public static <T> int size(Iterable<T> iterable) {
+    int result = 0;
+    for (T element : iterable) {
+      result++;
+    }
+    return result;
+  }
+
+  public static <T> List<T> toNewArrayList(Iterable<T> iterable) {
+    List<T> result = new ArrayList<>();
+    iterable.forEach(result::add);
+    return result;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index d62402b..a785262b 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -540,7 +540,7 @@
   private static IdentityHashMap<DexString, List<DexEncodedMethod>> groupMethodsByRenamedName(
       GraphLense graphLens, NamingLens namingLens, DexProgramClass clazz) {
     IdentityHashMap<DexString, List<DexEncodedMethod>> methodsByRenamedName =
-        new IdentityHashMap<>(clazz.directMethods().size() + clazz.virtualMethods().size());
+        new IdentityHashMap<>(clazz.getMethodCollection().size());
     for (DexEncodedMethod encodedMethod : clazz.methods()) {
       // Add method only if renamed, moved, or contains positions.
       DexMethod method = encodedMethod.method;
diff --git a/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java b/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
index 3681a77..dd5b43a 100644
--- a/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
+++ b/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
@@ -90,8 +90,9 @@
   }
 
   private static boolean assertEqualClasses(DexProgramClass a, DexProgramClass b) {
-    assert a.virtualMethods().size() == b.virtualMethods().size();
-    assert a.directMethods().size() == b.directMethods().size();
+    assert a.getMethodCollection().numberOfDirectMethods()
+        == b.getMethodCollection().numberOfDirectMethods();
+    assert a.getMethodCollection().size() == b.getMethodCollection().size();
     return true;
   }
 }
diff --git a/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java b/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
index fd9259f..2daa0a9 100644
--- a/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
+++ b/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
@@ -48,7 +48,7 @@
             AppView.createForR8(new AppInfoWithClassHierarchy(application), options), null);
     converter.optimize();
     DexProgramClass clazz = application.classes().iterator().next();
-    assertEquals(4, clazz.directMethods().size());
+    assertEquals(4, clazz.getMethodCollection().numberOfDirectMethods());
     for (DexEncodedMethod method : clazz.directMethods()) {
       if (!method.method.name.toString().equals("main")) {
         assertEquals(2, method.getCode().asDexCode().instructions.length);
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/NonSuperclassBridgeHoistingTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/NonSuperclassBridgeHoistingTest.java
index 9c4a0d6..87310d2 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/NonSuperclassBridgeHoistingTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/NonSuperclassBridgeHoistingTest.java
@@ -5,7 +5,7 @@
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
@@ -55,7 +55,7 @@
   private void inspect(CodeInspector inspector) {
     ClassSubject aClassSubject = inspector.clazz(A.class);
     assertThat(aClassSubject, isPresent());
-    assertEquals(0, aClassSubject.getDexProgramClass().virtualMethods().size());
+    assertFalse(aClassSubject.getDexProgramClass().getMethodCollection().hasVirtualMethods());
 
     ClassSubject b1ClassSubject = inspector.clazz(B.class);
     assertThat(b1ClassSubject, isPresent());
diff --git a/src/test/java/com/android/tools/r8/cf/IdenticalCatchHandlerTest.java b/src/test/java/com/android/tools/r8/cf/IdenticalCatchHandlerTest.java
index e0818bb..8d25f23 100644
--- a/src/test/java/com/android/tools/r8/cf/IdenticalCatchHandlerTest.java
+++ b/src/test/java/com/android/tools/r8/cf/IdenticalCatchHandlerTest.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.cf;
 
+import static com.google.common.base.Predicates.alwaysTrue;
 import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.ClassFileConsumer;
@@ -72,7 +73,7 @@
   private int countCatchHandlers(AndroidApp inputApp) throws Exception {
     CodeInspector inspector = new CodeInspector(inputApp);
     DexProgramClass dexClass = inspector.clazz(TestClass.class).getDexProgramClass();
-    Code code = dexClass.virtualMethods().get(0).getCode();
+    Code code = dexClass.lookupVirtualMethod(alwaysTrue()).getCode();
     if (code.isCfCode()) {
       CfCode cfCode = code.asCfCode();
       Set<CfLabel> targets = Sets.newIdentityHashSet();
diff --git a/src/test/java/com/android/tools/r8/cf/NonidenticalCatchHandlerTest.java b/src/test/java/com/android/tools/r8/cf/NonidenticalCatchHandlerTest.java
index fcb172b..a2e6005 100644
--- a/src/test/java/com/android/tools/r8/cf/NonidenticalCatchHandlerTest.java
+++ b/src/test/java/com/android/tools/r8/cf/NonidenticalCatchHandlerTest.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.cf;
 
+import static com.google.common.base.Predicates.alwaysTrue;
 import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.ClassFileConsumer;
@@ -71,7 +72,7 @@
   private int countCatchHandlers(AndroidApp inputApp) throws Exception {
     CodeInspector inspector = new CodeInspector(inputApp);
     DexProgramClass dexClass = inspector.clazz(TestClass.class).getDexProgramClass();
-    Code code = dexClass.virtualMethods().get(0).getCode();
+    Code code = dexClass.lookupVirtualMethod(alwaysTrue()).getCode();
     if (code.isCfCode()) {
       CfCode cfCode = code.asCfCode();
       Set<CfLabel> targets = Sets.newIdentityHashSet();
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
index 83dc73e..14f3ded 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
@@ -103,9 +104,10 @@
     assertEquals(
         "Missing duplicated forEach",
         2,
-        myCollection.getDexProgramClass().virtualMethods().stream()
-            .filter(m -> m.method.name.toString().equals("forEach"))
-            .count());
+        IterableUtils.size(
+            myCollection
+                .getDexProgramClass()
+                .virtualMethods(m -> m.method.name.toString().equals("forEach"))));
   }
 
   private void assertWrapperMethodsPresent(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingTest.java
index 0441cbf..2cfe861 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingTest.java
@@ -88,7 +88,12 @@
     assertTrue(inspector.clazz(EnumInstanceField.class).uniqueFieldWithName("a").isPresent());
 
     assertEquals(
-        5, inspector.clazz(EnumStaticMethod.class).getDexProgramClass().directMethods().size());
+        5,
+        inspector
+            .clazz(EnumStaticMethod.class)
+            .getDexProgramClass()
+            .getMethodCollection()
+            .numberOfDirectMethods());
     assertEquals(1, inspector.clazz(EnumVirtualMethod.class).virtualMethods().size());
   }
 
diff --git a/src/test/java/com/android/tools/r8/invalid/DuplicateDefinitionsTest.java b/src/test/java/com/android/tools/r8/invalid/DuplicateDefinitionsTest.java
index 3c3aee6..90771b4 100644
--- a/src/test/java/com/android/tools/r8/invalid/DuplicateDefinitionsTest.java
+++ b/src/test/java/com/android/tools/r8/invalid/DuplicateDefinitionsTest.java
@@ -59,11 +59,11 @@
     assertThat(clazz, isPresent());
 
     // There are two direct methods, but only because one is <init>.
-    assertEquals(2, clazz.getDexProgramClass().directMethods().size());
+    assertEquals(2, clazz.getDexProgramClass().getMethodCollection().numberOfDirectMethods());
     assertThat(clazz.method("void", "<init>", ImmutableList.of()), isPresent());
 
     // There is only one virtual method.
-    assertEquals(1, clazz.getDexProgramClass().virtualMethods().size());
+    assertEquals(1, clazz.getDexProgramClass().getMethodCollection().numberOfVirtualMethods());
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
index 6aac431..2498467 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.kotlin.lambda;
 
+import static com.google.common.base.Predicates.alwaysTrue;
 import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertTrue;
 import static org.junit.Assert.assertFalse;
@@ -213,7 +214,9 @@
           if (check.match(clazz)) {
             // Validate static initializer.
             if (check instanceof Group) {
-              assertEquals(clazz.directMethods().size(), ((Group) check).singletons == 0 ? 1 : 2);
+              assertEquals(
+                  clazz.getMethodCollection().numberOfDirectMethods(),
+                  ((Group) check).singletons == 0 ? 1 : 2);
             }
 
             list.remove(clazz);
@@ -258,8 +261,8 @@
     } else {
       assertTrue(isJStyleLambdaOrGroup(clazz));
       // Taking the number of any virtual method parameters seems to be good enough.
-      assertTrue(clazz.virtualMethods().size() > 0);
-      return clazz.virtualMethods().get(0).method.proto.parameters.size();
+      assertTrue(clazz.getMethodCollection().hasVirtualMethods());
+      return clazz.lookupVirtualMethod(alwaysTrue()).method.proto.parameters.size();
     }
     fail("Failed to get arity for " + clazz.type.descriptor.toString());
     throw new AssertionError();
diff --git a/src/test/java/com/android/tools/r8/shaking/AsterisksTest.java b/src/test/java/com/android/tools/r8/shaking/AsterisksTest.java
index 7261198..b7017e7 100644
--- a/src/test/java/com/android/tools/r8/shaking/AsterisksTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/AsterisksTest.java
@@ -100,7 +100,7 @@
     assertThat(classSubject, isPresent());
     assertThat(classSubject, not(isRenamed()));
     DexClass clazz = classSubject.getDexProgramClass();
-    assertEquals(3, clazz.virtualMethods().size());
+    assertEquals(3, clazz.getMethodCollection().numberOfVirtualMethods());
     for (DexEncodedMethod encodedMethod : clazz.virtualMethods()) {
       assertTrue(encodedMethod.method.name.toString().startsWith("foo"));
       MethodSubject methodSubject =
@@ -141,7 +141,7 @@
     assertThat(classSubject, isPresent());
     assertThat(classSubject, not(isRenamed()));
     DexClass clazz = classSubject.getDexProgramClass();
-    assertEquals(3, clazz.virtualMethods().size());
+    assertEquals(3, clazz.getMethodCollection().numberOfVirtualMethods());
     for (DexEncodedMethod encodedMethod : clazz.virtualMethods()) {
       assertTrue(encodedMethod.method.name.toString().startsWith("foo"));
       MethodSubject methodSubject =
@@ -163,7 +163,7 @@
     assertThat(classSubject, isPresent());
     assertThat(classSubject, not(isRenamed()));
     DexClass clazz = classSubject.getDexProgramClass();
-    assertEquals(3, clazz.virtualMethods().size());
+    assertEquals(3, clazz.getMethodCollection().numberOfVirtualMethods());
     for (DexEncodedMethod encodedMethod : clazz.virtualMethods()) {
       assertTrue(encodedMethod.method.name.toString().startsWith("foo"));
       MethodSubject methodSubject =
diff --git a/src/test/java/com/android/tools/r8/shaking/LibraryMethodOverrideMarkingTest.java b/src/test/java/com/android/tools/r8/shaking/LibraryMethodOverrideMarkingTest.java
index 4afc009..df8fdc4 100644
--- a/src/test/java/com/android/tools/r8/shaking/LibraryMethodOverrideMarkingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/LibraryMethodOverrideMarkingTest.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.shaking;
 
+import static com.google.common.base.Predicates.alwaysTrue;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
@@ -56,8 +57,8 @@
   private void verifySingleVirtualMethodMarkedAsOverridingLibraryMethod(
       AppInfoWithLiveness appInfo, DexType type) {
     DexProgramClass clazz = appInfo.definitionFor(type).asProgramClass();
-    assertEquals(1, clazz.virtualMethods().size());
-    DexEncodedMethod method = clazz.virtualMethods().get(0);
+    assertEquals(1, clazz.getMethodCollection().numberOfVirtualMethods());
+    DexEncodedMethod method = clazz.lookupVirtualMethod(alwaysTrue());
     assertTrue(method.isLibraryMethodOverride().isTrue());
   }
 
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
index 09c1356..578376e 100644
--- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -865,13 +865,12 @@
     CodeInspector inspector = new CodeInspector(processedApplication);
     ClassSubject clazz = inspector.clazz(OutlineOptions.CLASS_NAME);
     assertTrue(clazz.isPresent());
-    assertEquals(3, clazz.getDexProgramClass().directMethods().size());
+    assertEquals(3, clazz.getDexProgramClass().getMethodCollection().numberOfDirectMethods());
     // Collect the return types of the putlines for the body of method1 and method2.
     List<DexType> r = new ArrayList<>();
-    for (int i = 0; i < clazz.getDexProgramClass().directMethods().size(); i++) {
-      if (clazz.getDexProgramClass().directMethods().get(i).getCode().asDexCode().instructions[0]
-          instanceof InvokeVirtual) {
-        r.add(clazz.getDexProgramClass().directMethods().get(i).method.proto.returnType);
+    for (DexEncodedMethod directMethod : clazz.getDexProgramClass().directMethods()) {
+      if (directMethod.getCode().asDexCode().instructions[0] instanceof InvokeVirtual) {
+        r.add(directMethod.method.proto.returnType);
       }
     }
     assert r.size() == 2;
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 1f25605..39f7c44 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -193,7 +193,7 @@
   }
 
   static <S, T extends Subject> void forAll(
-      List<? extends S> items,
+      Iterable<? extends S> items,
       BiFunction<S, FoundClassSubject, ? extends T> constructor,
       FoundClassSubject clazz,
       Consumer<T> consumer) {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index df3f287..207932c 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -101,7 +101,7 @@
         : new FoundMethodSubject(codeInspector, encoded, this);
   }
 
-  private DexEncodedMethod findMethod(List<DexEncodedMethod> methods, DexMethod dexMethod) {
+  private DexEncodedMethod findMethod(Iterable<DexEncodedMethod> methods, DexMethod dexMethod) {
     for (DexEncodedMethod method : methods) {
       if (method.method.equals(dexMethod)) {
         return method;