Skip to content

Commit

Permalink
Merge pull request #1722 from apache/OAK-11073
Browse files Browse the repository at this point in the history
OAK-11073 : added ensureCapacity method to create hash based collecti…
  • Loading branch information
rishabhdaim authored Sep 17, 2024
2 parents 717769f + f31a7c9 commit 5f8fe02
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@
package org.apache.jackrabbit.oak.commons.collections;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;
Expand All @@ -34,6 +36,10 @@
*/
public class CollectionUtils {

// Maximum capacity for a hash based collection. (used internally by JDK).
// Also, it helps to avoid overflow errors when calculating the capacity
private static final int MAX_CAPACITY = 1 << 30;

private CollectionUtils() {
// no instances for you
}
Expand Down Expand Up @@ -105,15 +111,28 @@ public static <T> Set<T> toSet(@NotNull final Iterator<T> iterator) {
public static <T> Set<T> toSet(@NotNull final T... elements) {
Objects.requireNonNull(elements);
// make sure the set does not need to be resized given the initial content
float loadFactor = (float) 0.75; // HashSet default
int initialCapacity = 1 + (int) (elements.length / loadFactor);
final Set<T> result = new HashSet<>(initialCapacity, loadFactor);
final Set<T> result = new HashSet<>(ensureCapacity(elements.length));
for (T element : elements) {
result.add(element);
}
return result;
}

/**
* Creates a new, empty HashMap with expected capacity.
* <p>
* The returned map uses the default load factor of 0.75, and its capacity is
* large enough to add expected number of elements without resizing.
*
* @param capacity the expected number of elements
* @throws IllegalArgumentException if capacity is negative
*/
@NotNull
public static <K, V> Map<K, V> newHashMap(final int capacity) {
// make sure the set does not need to be resized given the initial content
return new HashMap<>(ensureCapacity(capacity));
}

/**
* Convert an {@code Iterator} to an {@code Iterable}.
* <p>
Expand All @@ -131,12 +150,12 @@ public static <T> Set<T> toSet(@NotNull final T... elements) {
public static <T> Iterable<T> toIterable(@NotNull final Iterator<T> iterator) {
Objects.requireNonNull(iterator);

Iterable<T> delegate = new Iterable<T>() {
return new Iterable<>() {

private boolean consumed = false;

@Override
public Iterator<T> iterator() {
public @NotNull Iterator<T> iterator() {
if (consumed) {
throw new IllegalStateException("Iterator already returned once");
} else {
Expand All @@ -145,8 +164,6 @@ public Iterator<T> iterator() {
}
}
};

return delegate;
}

/**
Expand All @@ -170,7 +187,27 @@ public static <T> Stream<T> toStream(@NotNull Iterable<T> iterable) {
* iterator to convert
* @return the stream (representing the remaining elements in the iterator)
*/
public static <T> Stream<T> toStream(Iterator<T> iterator) {
@NotNull
public static <T> Stream<T> toStream(@NotNull Iterator<T> iterator) {
return StreamSupport.stream(toIterable(iterator).spliterator(), false);
}

/**
* Ensure the capacity of a map or set given the expected number of elements.
*
* @param capacity the expected number of elements
* @return the capacity to use to avoid rehashing & collisions
*/
static int ensureCapacity(final int capacity) {

if (capacity < 0) {
throw new IllegalArgumentException("Capacity must be non-negative");
}

if (capacity > MAX_CAPACITY) {
return MAX_CAPACITY;
}

return 1 + (int) (capacity / 0.75f);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.junit.Assert.fail;

public class CollectionUtilsTest {

final List<String> data = Arrays.asList("one", "two", "three", null);
Expand Down Expand Up @@ -98,7 +100,7 @@ public void iteratorToIIteratable() {
Assert.assertFalse(testit.hasNext());
try {
testit = iterable.iterator();
Assert.fail("should only work once");
fail("should only work once");
} catch (IllegalStateException expected) {
// that's what we want
}
Expand All @@ -112,4 +114,22 @@ public void iteratorToStream() {
List<String> result = stream.collect(Collectors.toList());
Assert.assertEquals(input.toString(), result.toString());
}

@Test
public void ensureCapacity() {
int capacity = CollectionUtils.ensureCapacity(8);
Assert.assertEquals(11, capacity);
}

@Test
public void ensureCapacityWithMaxValue() {
int capacity = CollectionUtils.ensureCapacity(1073741825);
Assert.assertEquals(1073741824, capacity);
}

@Test(expected = IllegalArgumentException.class)
public void ensureCapacityWithNegativeValue() {
int capacity = CollectionUtils.ensureCapacity(-8);
fail("Should throw IllegalArgumentException");
}
}

0 comments on commit 5f8fe02

Please sign in to comment.