diff --git a/apct-tests/perftests/core/src/android/util/ArrayMapPerfTest.java b/apct-tests/perftests/core/src/android/util/ArrayMapPerfTest.java new file mode 100644 index 0000000000000..b93a6ea18d8db --- /dev/null +++ b/apct-tests/perftests/core/src/android/util/ArrayMapPerfTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * 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 android.util; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; + +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.function.BiConsumer; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class ArrayMapPerfTest { + private static final int NUM_ITERATIONS = 100; + private static final int SET_SIZE_SMALL = 10; + private static final int SET_SIZE_LARGE = 50; + + @Rule + public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Test + public void testForEach_Small() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + BiConsumer consumer = (s, i) -> { + }; + while (state.keepRunning()) { + for (int i = 0; i < NUM_ITERATIONS; ++i) { + ArrayMap map = new ArrayMap<>(); + for (int j = 0; j < SET_SIZE_SMALL; j++) { + map.put(Integer.toString(j), j); + } + map.forEach(consumer); + } + } + } + + @Test + public void testForEach_Large() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + BiConsumer consumer = (s, i) -> { + }; + while (state.keepRunning()) { + for (int i = 0; i < NUM_ITERATIONS; ++i) { + ArrayMap map = new ArrayMap<>(); + for (int j = 0; j < SET_SIZE_LARGE; j++) { + map.put(Integer.toString(j), j); + } + map.forEach(consumer); + } + } + } +} diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java index 4edff27d0ced4..f50b51bbdb356 100644 --- a/core/java/android/util/ArrayMap.java +++ b/core/java/android/util/ArrayMap.java @@ -27,6 +27,7 @@ import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Map; import java.util.Set; +import java.util.function.BiConsumer; /** * ArrayMap is a generic key->value mapping data structure that is @@ -979,6 +980,28 @@ public final class ArrayMap implements Map { return MapCollections.containsAllHelper(this, collection); } + /** + * Performs the given action for all elements in the stored order. This implementation overrides + * the default implementation to avoid iterating using the {@link #entrySet()} and iterates in + * the key-value order consistent with {@link #keyAt(int)} and {@link #valueAt(int)}. + * + * @param action The action to be performed for each element + */ + @Override + public void forEach(BiConsumer action) { + if (action == null) { + throw new NullPointerException("action must not be null"); + } + + final int size = mSize; + for (int i = 0; i < size; ++i) { + if (size != mSize) { + throw new ConcurrentModificationException(); + } + action.accept(keyAt(i), valueAt(i)); + } + } + /** * Perform a {@link #put(Object, Object)} of all key/value pairs in map * @param map The map whose contents are to be retrieved.