Rewrite and prune misc. information on AppInfoWithLiveness
Change-Id: I0d14841db5cd3f37d5f27386036aa75c430d3acd
diff --git a/src/main/java/com/android/tools/r8/graph/AbstractAccessContexts.java b/src/main/java/com/android/tools/r8/graph/AbstractAccessContexts.java
index a30eb47..b8e779d 100644
--- a/src/main/java/com/android/tools/r8/graph/AbstractAccessContexts.java
+++ b/src/main/java/com/android/tools/r8/graph/AbstractAccessContexts.java
@@ -86,6 +86,8 @@
abstract AbstractAccessContexts rewrittenWithLens(
DexDefinitionSupplier definitions, GraphLens lens);
+ abstract AbstractAccessContexts withoutPrunedItems(PrunedItems prunedItems);
+
public static EmptyAccessContexts empty() {
return EmptyAccessContexts.getInstance();
}
@@ -155,6 +157,11 @@
public AbstractAccessContexts join(AbstractAccessContexts contexts) {
return contexts;
}
+
+ @Override
+ AbstractAccessContexts withoutPrunedItems(PrunedItems prunedItems) {
+ return this;
+ }
}
public static class ConcreteAccessContexts extends AbstractAccessContexts {
@@ -378,6 +385,14 @@
contexts.asConcrete().accessesWithContexts.forEach(addAllMethods);
return new ConcreteAccessContexts(newAccessesWithContexts);
}
+
+ @Override
+ AbstractAccessContexts withoutPrunedItems(PrunedItems prunedItems) {
+ for (ProgramMethodSet methodSet : accessesWithContexts.values()) {
+ methodSet.removeIf(method -> prunedItems.isRemoved(method.getReference()));
+ }
+ return this;
+ }
}
public static class UnknownAccessContexts extends AbstractAccessContexts {
@@ -440,5 +455,10 @@
public AbstractAccessContexts join(AbstractAccessContexts contexts) {
return this;
}
+
+ @Override
+ AbstractAccessContexts withoutPrunedItems(PrunedItems prunedItems) {
+ return this;
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 9ceb56c..c09b0fd 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -880,7 +880,6 @@
testing().verticallyMergedClassesConsumer.accept(dexItemFactory(), verticallyMergedClasses);
} else {
assert this.verticallyMergedClasses != null;
- assert verticallyMergedClasses.isEmpty();
}
}
@@ -1097,7 +1096,10 @@
public void run(Timing timing) {
if (appView.hasLiveness()) {
result =
- appView.appInfoWithLiveness().rewrittenWithLens(application, lens, timing);
+ appView
+ .appInfoWithLiveness()
+ .rewrittenWithLens(
+ application, lens, appliedLensInModifiedLens, timing);
} else {
assert appView.hasClassHierarchy();
AppView<AppInfoWithClassHierarchy> appViewWithClassHierarchy =
@@ -1255,6 +1257,23 @@
public boolean shouldRun() {
return !appView.getStartupProfile().isEmpty();
}
+ },
+ new ThreadTask() {
+ @Override
+ public void run(Timing timing) {
+ ImmutableSet.Builder<DexMethod> cfByteCodePassThroughBuilder =
+ ImmutableSet.builder();
+ for (DexMethod method : appView.cfByteCodePassThrough) {
+ cfByteCodePassThroughBuilder.add(
+ lens.getRenamedMethodSignature(method, appliedLensInModifiedLens));
+ }
+ appView.cfByteCodePassThrough = cfByteCodePassThroughBuilder.build();
+ }
+
+ @Override
+ public boolean shouldRun() {
+ return !appView.cfByteCodePassThrough.isEmpty();
+ }
});
});
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
index 6b2bad4..29cd5a4 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
@@ -9,7 +9,9 @@
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.Timing;
import java.util.IdentityHashMap;
+import java.util.Iterator;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -102,4 +104,17 @@
assert infos.values().size() == SetUtils.newIdentityHashSet(infos.values()).size();
return true;
}
+
+ public FieldAccessInfoCollectionImpl withoutPrunedItems(PrunedItems prunedItems) {
+ Iterator<Entry<DexField, FieldAccessInfoImpl>> iterator = infos.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Entry<DexField, FieldAccessInfoImpl> entry = iterator.next();
+ if (prunedItems.isRemoved(entry.getKey())) {
+ iterator.remove();
+ } else {
+ entry.setValue(entry.getValue().withoutPrunedItems(prunedItems));
+ }
+ }
+ return this;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
index 0dae257..39aa866 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
@@ -406,4 +406,10 @@
merged.writesWithContexts = writesWithContexts.join(impl.writesWithContexts);
return merged;
}
+
+ public FieldAccessInfoImpl withoutPrunedItems(PrunedItems prunedItems) {
+ readsWithContexts = readsWithContexts.withoutPrunedItems(prunedItems);
+ writesWithContexts = writesWithContexts.withoutPrunedItems(prunedItems);
+ return this;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java b/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java
index 3b8681b..b541bfc 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java
@@ -17,7 +17,9 @@
import com.android.tools.r8.utils.collections.ThrowingMap;
import com.google.common.collect.Sets;
import java.util.IdentityHashMap;
+import java.util.Iterator;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
@@ -178,18 +180,18 @@
}
}
- public MethodAccessInfoCollection withoutPrunedItems(PrunedItems prunedItems) {
+ public MethodAccessInfoCollection withoutPrunedContexts(PrunedItems prunedItems) {
if (!fullyDestroyed) {
- pruneItems(prunedItems, directInvokes);
- pruneItems(prunedItems, interfaceInvokes);
- pruneItems(prunedItems, staticInvokes);
- pruneItems(prunedItems, superInvokes);
- pruneItems(prunedItems, virtualInvokes);
+ pruneContexts(prunedItems, directInvokes);
+ pruneContexts(prunedItems, interfaceInvokes);
+ pruneContexts(prunedItems, staticInvokes);
+ pruneContexts(prunedItems, superInvokes);
+ pruneContexts(prunedItems, virtualInvokes);
}
return this;
}
- private static void pruneItems(
+ private static void pruneContexts(
PrunedItems prunedItems, Map<DexMethod, ProgramMethodSet> invokes) {
if (isThrowingMap(invokes)) {
return;
@@ -212,6 +214,45 @@
});
}
+ public MethodAccessInfoCollection withoutPrunedItems(PrunedItems prunedItems) {
+ if (!fullyDestroyed) {
+ pruneItems(prunedItems, directInvokes);
+ pruneItems(prunedItems, interfaceInvokes);
+ pruneItems(prunedItems, staticInvokes);
+ pruneItems(prunedItems, superInvokes);
+ pruneItems(prunedItems, virtualInvokes);
+ }
+ return this;
+ }
+
+ private static void pruneItems(
+ PrunedItems prunedItems, Map<DexMethod, ProgramMethodSet> invokes) {
+ if (isThrowingMap(invokes)) {
+ return;
+ }
+ Iterator<Entry<DexMethod, ProgramMethodSet>> iterator = invokes.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Entry<DexMethod, ProgramMethodSet> entry = iterator.next();
+ if (prunedItems.isRemoved(entry.getKey())) {
+ iterator.remove();
+ } else {
+ ProgramMethodSet contexts = entry.getValue();
+ contexts.removeIf(
+ context -> {
+ if (prunedItems.isRemoved(context.getReference())) {
+ return true;
+ }
+ assert prunedItems.getPrunedApp().definitionFor(context.getReference()) != null
+ : "Expected method to be present: " + context.getReference().toSourceString();
+ return false;
+ });
+ if (contexts.isEmpty()) {
+ iterator.remove();
+ }
+ }
+ }
+ }
+
public boolean verify(AppView<AppInfoWithLiveness> appView) {
assert verifyNoNonResolving(appView);
return true;
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java
index 70d1666..b55811b 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java
@@ -43,5 +43,5 @@
AppInfo appInfo);
ObjectAllocationInfoCollection rewrittenWithLens(
- DexDefinitionSupplier definitions, GraphLens lens, Timing timing);
+ DexDefinitionSupplier definitions, GraphLens lens, GraphLens appliedLens, Timing timing);
}
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
index 6050c9d..2ffe096 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
@@ -8,6 +8,7 @@
import static com.android.tools.r8.utils.MapUtils.ignoreKey;
import com.android.tools.r8.graph.lens.GraphLens;
+import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.shaking.GraphReporter;
import com.android.tools.r8.shaking.InstantiationReason;
@@ -144,14 +145,17 @@
@Override
public ObjectAllocationInfoCollectionImpl rewrittenWithLens(
- DexDefinitionSupplier definitions, GraphLens lens, Timing timing) {
+ DexDefinitionSupplier definitions, GraphLens lens, GraphLens appliedLens, Timing timing) {
return timing.time(
- "Rewrite ObjectAllocationInfoCollectionImpl", () -> rewrittenWithLens(definitions, lens));
+ "Rewrite ObjectAllocationInfoCollectionImpl",
+ () -> rewrittenWithLens(definitions, lens, appliedLens));
}
private ObjectAllocationInfoCollectionImpl rewrittenWithLens(
- DexDefinitionSupplier definitions, GraphLens lens) {
- return builder(true, null).rewrittenWithLens(this, definitions, lens).build(definitions);
+ DexDefinitionSupplier definitions, GraphLens lens, GraphLens appliedLens) {
+ return builder(true, null)
+ .rewrittenWithLens(this, definitions, lens, appliedLens)
+ .build(definitions);
}
public ObjectAllocationInfoCollectionImpl withoutPrunedItems(PrunedItems prunedItems) {
@@ -474,11 +478,12 @@
Builder rewrittenWithLens(
ObjectAllocationInfoCollectionImpl objectAllocationInfos,
DexDefinitionSupplier definitions,
- GraphLens lens) {
+ GraphLens lens,
+ GraphLens appliedLens) {
instantiatedHierarchy = null;
objectAllocationInfos.classesWithoutAllocationSiteTracking.forEach(
clazz -> {
- DexType type = lens.lookupType(clazz.type);
+ DexType type = lens.lookupType(clazz.type, appliedLens);
if (type.isPrimitiveType()) {
return;
}
@@ -488,7 +493,7 @@
});
objectAllocationInfos.classesWithAllocationSiteTracking.forEach(
(clazz, allocationSitesForClass) -> {
- DexType type = lens.lookupType(clazz.type);
+ DexType type = lens.lookupType(clazz.type, appliedLens);
if (type.isPrimitiveType()) {
return;
}
@@ -507,7 +512,7 @@
});
for (DexProgramClass abstractType :
objectAllocationInfos.interfacesWithUnknownSubtypeHierarchy) {
- DexType type = lens.lookupType(abstractType.type);
+ DexType type = lens.lookupType(abstractType.type, appliedLens);
if (type.isPrimitiveType()) {
assert false;
continue;
@@ -517,15 +522,19 @@
assert !interfacesWithUnknownSubtypeHierarchy.contains(rewrittenClass);
interfacesWithUnknownSubtypeHierarchy.add(rewrittenClass);
}
+ LensCodeRewriterUtils rewriter = new LensCodeRewriterUtils(definitions, lens, appliedLens);
objectAllocationInfos.instantiatedLambdas.forEach(
(iface, lambdas) -> {
- DexType type = lens.lookupType(iface);
+ DexType type = lens.lookupType(iface, appliedLens);
if (type.isPrimitiveType()) {
assert false;
return;
}
- // TODO(b/150277553): Rewrite lambda descriptor.
- instantiatedLambdas.computeIfAbsent(type, ignoreKey(ArrayList::new)).addAll(lambdas);
+ List<LambdaDescriptor> newLambdas =
+ instantiatedLambdas.computeIfAbsent(type, ignoreKey(ArrayList::new));
+ for (LambdaDescriptor lambda : lambdas) {
+ newLambdas.add(lambda.rewrittenWithLens(lens, appliedLens, rewriter));
+ }
});
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
index 013bfde..0e31e93 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
@@ -118,15 +118,19 @@
.getName();
}
- @SuppressWarnings("ReferenceEquality")
public DexMethodHandle rewriteDexMethodHandle(
DexMethodHandle methodHandle, MethodHandleUse use, ProgramMethod context) {
+ return rewriteDexMethodHandle(methodHandle, use, context.getReference());
+ }
+
+ @SuppressWarnings("ReferenceEquality")
+ public DexMethodHandle rewriteDexMethodHandle(
+ DexMethodHandle methodHandle, MethodHandleUse use, DexMethod context) {
if (methodHandle.isMethodHandle()) {
DexMethod invokedMethod = methodHandle.asMethod();
MethodHandleType oldType = methodHandle.type;
MethodLookupResult lensLookup =
- graphLens.lookupMethod(
- invokedMethod, context.getReference(), oldType.toInvokeType(), codeLens);
+ graphLens.lookupMethod(invokedMethod, context, oldType.toInvokeType(), codeLens);
DexMethod rewrittenTarget = lensLookup.getReference();
DexMethod actualTarget;
MethodHandleType newType;
@@ -161,7 +165,7 @@
}
}
if (newType != oldType || actualTarget != invokedMethod || rewrittenTarget != actualTarget) {
- DexClass holder = definitions.definitionFor(actualTarget.holder, context);
+ DexClass holder = definitions.definitionFor(actualTarget.holder);
boolean isInterface = holder != null ? holder.isInterface() : methodHandle.isInterface;
return definitions
.dexItemFactory()
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
index 1c9d26d..e3cbc07 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
@@ -20,6 +20,10 @@
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry.MethodHandleUse;
+import com.android.tools.r8.graph.lens.GraphLens;
+import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import com.android.tools.r8.utils.SetUtils;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.List;
@@ -42,8 +46,8 @@
final DexProto enforcedProto;
public final DexMethodHandle implHandle;
- public final List<DexType> interfaces = new ArrayList<>();
- public final Set<DexProto> bridges = Sets.newIdentityHashSet();
+ public final List<DexType> interfaces;
+ public final Set<DexProto> bridges;
public final DexTypeList captures;
// Used for accessibility analysis and few assertions only.
@@ -51,13 +55,7 @@
private final DexType targetHolder;
private LambdaDescriptor() {
- uniqueId = null;
- enforcedProto = null;
- implHandle = null;
- captures = null;
- targetAccessFlags = null;
- targetHolder = null;
- mainMethod = null;
+ this(null, null, null, null, null, null, null, null, null);
}
public DexProto getErasedProto() {
@@ -96,7 +94,8 @@
this.enforcedProto = enforcedProto;
this.implHandle = implHandle;
this.captures = captures;
-
+ this.bridges = Sets.newIdentityHashSet();
+ this.interfaces = new ArrayList<>();
this.interfaces.add(mainInterface);
DexClassAndMethod targetMethod =
context == null ? null : lookupTargetMethod(appView, appInfo, context);
@@ -109,6 +108,27 @@
}
}
+ private LambdaDescriptor(
+ String uniqueId,
+ DexMethod mainMethod,
+ DexProto enforcedProto,
+ DexMethodHandle implHandle,
+ List<DexType> interfaces,
+ Set<DexProto> bridges,
+ DexTypeList captures,
+ MethodAccessFlags targetAccessFlags,
+ DexType targetHolder) {
+ this.uniqueId = uniqueId;
+ this.mainMethod = mainMethod;
+ this.enforcedProto = enforcedProto;
+ this.implHandle = implHandle;
+ this.interfaces = interfaces;
+ this.bridges = bridges;
+ this.captures = captures;
+ this.targetAccessFlags = targetAccessFlags;
+ this.targetHolder = targetHolder;
+ }
+
final DexType getImplReceiverType() {
// The receiver of instance impl-method is captured as the first captured
// value or should be the first argument of the enforced method signature.
@@ -498,4 +518,32 @@
return false;
}
+
+ public LambdaDescriptor rewrittenWithLens(
+ GraphLens lens, GraphLens appliedLens, LensCodeRewriterUtils rewriter) {
+ String newUniqueId = uniqueId;
+ DexMethod newMainMethod = lens.getRenamedMethodSignature(mainMethod, appliedLens);
+ DexProto newEnforcedProto = rewriter.rewriteProto(enforcedProto);
+ DexMethodHandle newImplHandle =
+ rewriter.rewriteDexMethodHandle(
+ implHandle, MethodHandleUse.ARGUMENT_TO_LAMBDA_METAFACTORY, mainMethod);
+ List<DexType> newInterfaces =
+ new ArrayList<>(
+ SetUtils.mapLinkedHashSet(interfaces, itf -> lens.lookupType(itf, appliedLens)));
+ Set<DexProto> newBridges = SetUtils.mapIdentityHashSet(bridges, rewriter::rewriteProto);
+ DexTypeList newCaptures = captures.map(capture -> lens.lookupType(capture, appliedLens));
+ MethodAccessFlags newTargetAccessFlags = targetAccessFlags;
+ DexType newTargetHolder =
+ targetHolder != null ? lens.lookupType(targetHolder, appliedLens) : null;
+ return new LambdaDescriptor(
+ newUniqueId,
+ newMainMethod,
+ newEnforcedProto,
+ newImplHandle,
+ newInterfaces,
+ newBridges,
+ newCaptures,
+ newTargetAccessFlags,
+ newTargetHolder);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 93ac4f4..e634f97 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.Definition;
+import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndField;
@@ -316,8 +317,8 @@
pruneMethods(previous.bootstrapMethods, prunedItems, tasks),
pruneMethods(previous.virtualMethodsTargetedByInvokeDirect, prunedItems, tasks),
pruneMethods(previous.liveMethods, prunedItems, tasks),
- previous.fieldAccessInfoCollection,
- previous.methodAccessInfoCollection.withoutPrunedItems(prunedItems),
+ previous.fieldAccessInfoCollection.withoutPrunedItems(prunedItems),
+ previous.methodAccessInfoCollection.withoutPrunedContexts(prunedItems),
previous.objectAllocationInfoCollection.withoutPrunedItems(prunedItems),
pruneCallSites(previous.callSites, prunedItems),
extendPinnedItems(previous, prunedItems.getAdditionalPinnedItems()),
@@ -1090,12 +1091,19 @@
return appInfoWithLiveness;
}
+ public AppInfoWithLiveness rebuildWithLiveness(DexApplication application) {
+ return rebuildWithLiveness(getSyntheticItems().commit(application));
+ }
+
public AppInfoWithLiveness rebuildWithLiveness(CommittedItems committedItems) {
return new AppInfoWithLiveness(this, committedItems);
}
public AppInfoWithLiveness rewrittenWithLens(
- DirectMappedDexApplication application, NonIdentityGraphLens lens, Timing timing) {
+ DirectMappedDexApplication application,
+ NonIdentityGraphLens lens,
+ GraphLens appliedLens,
+ Timing timing) {
assert checkIfObsolete();
// Switchmap classes should never be affected by renaming.
@@ -1126,7 +1134,8 @@
lens.rewriteReferences(liveMethods),
fieldAccessInfoCollection.rewrittenWithLens(definitionSupplier, lens, timing),
methodAccessInfoCollection.rewrittenWithLens(definitionSupplier, lens, timing),
- objectAllocationInfoCollection.rewrittenWithLens(definitionSupplier, lens, timing),
+ objectAllocationInfoCollection.rewrittenWithLens(
+ definitionSupplier, lens, appliedLens, timing),
lens.rewriteCallSites(callSites, definitionSupplier, timing),
keepInfo.rewrite(definitionSupplier, lens, application.options, timing),
// Take any rule in case of collisions.
diff --git a/src/main/java/com/android/tools/r8/utils/SetUtils.java b/src/main/java/com/android/tools/r8/utils/SetUtils.java
index be05c5d..a7c3366 100644
--- a/src/main/java/com/android/tools/r8/utils/SetUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/SetUtils.java
@@ -6,9 +6,11 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
+import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
@@ -115,7 +117,7 @@
return builder.build();
}
- public static <T, S> Set<T> mapIdentityHashSet(Set<S> set, Function<S, T> fn) {
+ public static <T, S> Set<T> mapIdentityHashSet(Collection<S> set, Function<S, T> fn) {
Set<T> out = newIdentityHashSet(set.size());
for (S element : set) {
out.add(fn.apply(element));
@@ -123,6 +125,14 @@
return out;
}
+ public static <T, S> Set<T> mapLinkedHashSet(Collection<S> set, Function<S, T> fn) {
+ Set<T> out = new LinkedHashSet<>(set.size());
+ for (S element : set) {
+ out.add(fn.apply(element));
+ }
+ return out;
+ }
+
public static <T> T removeFirst(Set<T> set) {
T element = set.iterator().next();
set.remove(element);