From 5e9e779ed26126d2d5bcfb6b99dafd1d12d59304 Mon Sep 17 00:00:00 2001 From: Viraj Jasani Date: Thu, 3 Jun 2021 15:26:00 +0530 Subject: [PATCH] HADOOP-17152. Provide Hadoop's own Lists utility to reduce dependency on Guava (#3061) Change-Id: I52e55b9d9826ad661e9ad7dc15f007aa168f0fe1 Reviewed-by: Wei-Chiu Chuang Signed-off-by: Takanobu Asanuma --- .../java/org/apache/hadoop/util/Lists.java | 235 ++++++++++++++++++ .../org/apache/hadoop/util/TestLists.java | 102 ++++++++ 2 files changed, 337 insertions(+) create mode 100644 hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Lists.java create mode 100644 hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestLists.java diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Lists.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Lists.java new file mode 100644 index 00000000000..b6d74ee6792 --- /dev/null +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Lists.java @@ -0,0 +1,235 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.hadoop.util; + +import org.apache.hadoop.classification.InterfaceAudience; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +/** + * Static utility methods pertaining to {@link List} instances. + * This class is Hadoop's internal use alternative to Guava's Lists + * utility class. + * Javadocs for majority of APIs in this class are taken from Guava's Lists + * class from Guava release version 27.0-jre. + */ +@InterfaceAudience.Private +public final class Lists { + + private Lists() { + // empty + } + + /** + * Creates a mutable, empty {@code ArrayList} instance. + */ + public static ArrayList newArrayList() { + return new ArrayList<>(); + } + + /** + * Creates a mutable {@code ArrayList} instance containing the given + * elements. + * + *

Note that even when you do need the ability to add or remove, + * this method provides only a tiny bit of syntactic sugar for + * {@code newArrayList(} + * {@link Arrays#asList asList} + * {@code (...))}, or for creating an empty list then calling + * {@link Collections#addAll}. + */ + @SafeVarargs + public static ArrayList newArrayList(E... elements) { + if (elements == null) { + throw new NullPointerException(); + } + // Avoid integer overflow when a large array is passed in + int capacity = computeArrayListCapacity(elements.length); + ArrayList list = new ArrayList<>(capacity); + Collections.addAll(list, elements); + return list; + } + + /** + * Creates a mutable {@code ArrayList} instance containing the + * given elements; a very thin shortcut for creating an empty list then + * calling Iterables#addAll. + */ + public static ArrayList newArrayList(Iterable elements) { + if (elements == null) { + throw new NullPointerException(); + } + return (elements instanceof Collection) + ? new ArrayList<>(cast(elements)) + : newArrayList(elements.iterator()); + } + + /** + * Creates a mutable {@code ArrayList} instance containing the + * given elements; a very thin shortcut for creating an empty list + * and then calling Iterators#addAll. + */ + public static ArrayList newArrayList(Iterator elements) { + ArrayList list = newArrayList(); + addAll(list, elements); + return list; + } + + /** + * Creates an {@code ArrayList} instance backed by an array with the + * specified initial size; + * simply delegates to {@link ArrayList#ArrayList(int)}. + * + * @param initialArraySize the exact size of the initial backing array for + * the returned array list + * ({@code ArrayList} documentation calls this value the "capacity"). + * @return a new, empty {@code ArrayList} which is guaranteed not to + * resize itself unless its size reaches {@code initialArraySize + 1}. + * @throws IllegalArgumentException if {@code initialArraySize} is negative. + */ + public static ArrayList newArrayListWithCapacity( + int initialArraySize) { + checkNonnegative(initialArraySize, "initialArraySize"); + return new ArrayList<>(initialArraySize); + } + + /** + * Creates an {@code ArrayList} instance to hold {@code estimatedSize} + * elements, plus an unspecified amount of padding; + * you almost certainly mean to call {@link + * #newArrayListWithCapacity} (see that method for further advice on usage). + * + * @param estimatedSize an estimate of the eventual {@link List#size()} + * of the new list. + * @return a new, empty {@code ArrayList}, sized appropriately to hold the + * estimated number of elements. + * @throws IllegalArgumentException if {@code estimatedSize} is negative. + */ + public static ArrayList newArrayListWithExpectedSize( + int estimatedSize) { + return new ArrayList<>(computeArrayListCapacity(estimatedSize)); + } + + /** + * Creates a mutable, empty {@code LinkedList} instance. + * + *

Performance note: {@link ArrayList} and + * {@link java.util.ArrayDeque} consistently + * outperform {@code LinkedList} except in certain rare and specific + * situations. Unless you have + * spent a lot of time benchmarking your specific needs, use one of those + * instead. + */ + public static LinkedList newLinkedList() { + return new LinkedList<>(); + } + + /** + * Creates a mutable {@code LinkedList} instance containing the given + * elements; a very thin shortcut for creating an empty list then calling + * Iterables#addAll. + * + *

Performance note: {@link ArrayList} and + * {@link java.util.ArrayDeque} consistently + * outperform {@code LinkedList} except in certain rare and specific + * situations. Unless you have spent a lot of time benchmarking your + * specific needs, use one of those instead. + */ + public static LinkedList newLinkedList( + Iterable elements) { + LinkedList list = newLinkedList(); + addAll(list, elements); + return list; + } + + private static int computeArrayListCapacity(int arraySize) { + checkNonnegative(arraySize, "arraySize"); + return saturatedCast(5L + arraySize + (arraySize / 10)); + } + + private static int checkNonnegative(int value, String name) { + if (value < 0) { + throw new IllegalArgumentException(name + " cannot be negative but was: " + + value); + } + return value; + } + + /** + * Returns the {@code int} nearest in value to {@code value}. + * + * @param value any {@code long} value. + * @return the same value cast to {@code int} if it is in the range of the + * {@code int} type, {@link Integer#MAX_VALUE} if it is too large, + * or {@link Integer#MIN_VALUE} if it is too small. + */ + private static int saturatedCast(long value) { + if (value > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + if (value < Integer.MIN_VALUE) { + return Integer.MIN_VALUE; + } + return (int) value; + } + + private static boolean addAll(Collection addTo, + Iterator iterator) { + if (addTo == null) { + throw new NullPointerException(); + } + if (iterator == null) { + throw new NullPointerException(); + } + boolean wasModified = false; + while (iterator.hasNext()) { + wasModified |= addTo.add(iterator.next()); + } + return wasModified; + } + + private static Collection cast(Iterable iterable) { + return (Collection) iterable; + } + + /** + * Adds all elements in {@code iterable} to {@code collection}. + * + * @return {@code true} if {@code collection} was modified as a result of + * this operation. + */ + private static boolean addAll(Collection addTo, + Iterable elementsToAdd) { + if (elementsToAdd instanceof Collection) { + Collection c = cast(elementsToAdd); + return addTo.addAll(c); + } + if (elementsToAdd == null) { + throw new NullPointerException(); + } + return addAll(addTo, elementsToAdd.iterator()); + } + +} diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestLists.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestLists.java new file mode 100644 index 00000000000..537e3781edc --- /dev/null +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestLists.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.hadoop.util; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Simple tests for utility class Lists. + */ +public class TestLists { + + @Test + public void testAddToEmptyArrayList() { + List list = Lists.newArrayList(); + list.add("record1"); + Assert.assertEquals(1, list.size()); + Assert.assertEquals("record1", list.get(0)); + } + + @Test + public void testAddToEmptyLinkedList() { + List list = Lists.newLinkedList(); + list.add("record1"); + Assert.assertEquals(1, list.size()); + Assert.assertEquals("record1", list.get(0)); + } + + @Test + public void testVarArgArrayLists() { + List list = Lists.newArrayList("record1", "record2", "record3"); + list.add("record4"); + Assert.assertEquals(4, list.size()); + Assert.assertEquals("record1", list.get(0)); + Assert.assertEquals("record2", list.get(1)); + Assert.assertEquals("record3", list.get(2)); + Assert.assertEquals("record4", list.get(3)); + } + + @Test + public void testItrArrayLists() { + Set set = new HashSet<>(); + set.add("record1"); + set.add("record2"); + set.add("record3"); + List list = Lists.newArrayList(set); + list.add("record4"); + Assert.assertEquals(4, list.size()); + } + + @Test + public void testItrLinkedLists() { + Set set = new HashSet<>(); + set.add("record1"); + set.add("record2"); + set.add("record3"); + List list = Lists.newLinkedList(set); + list.add("record4"); + Assert.assertEquals(4, list.size()); + } + + @Test + public void testArrayListWithSize() { + List list = Lists.newArrayListWithCapacity(3); + list.add("record1"); + list.add("record2"); + list.add("record3"); + Assert.assertEquals(3, list.size()); + Assert.assertEquals("record1", list.get(0)); + Assert.assertEquals("record2", list.get(1)); + Assert.assertEquals("record3", list.get(2)); + list = Lists.newArrayListWithCapacity(3); + list.add("record1"); + list.add("record2"); + list.add("record3"); + Assert.assertEquals(3, list.size()); + Assert.assertEquals("record1", list.get(0)); + Assert.assertEquals("record2", list.get(1)); + Assert.assertEquals("record3", list.get(2)); + } + +}