diff --git a/src/main/java/org/apache/commons/lang3/ArrayUtils.java b/src/main/java/org/apache/commons/lang3/ArrayUtils.java index aae2ce34fe4..119bffa3caa 100644 --- a/src/main/java/org/apache/commons/lang3/ArrayUtils.java +++ b/src/main/java/org/apache/commons/lang3/ArrayUtils.java @@ -21,11 +21,14 @@ import java.lang.reflect.Method; import java.lang.reflect.Type; import java.security.SecureRandom; +import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.Comparator; import java.util.Date; import java.util.HashMap; +import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Random; @@ -8862,6 +8865,63 @@ public static T[] toArray(@SuppressWarnings("unchecked") final T... items) { return items; } + /** + * Converts an {@link Iterator} into an array. + *

+ * Returns {@code null} if the input iterator is {@code null}. + * If the iterator has no elements, an empty {@code Object[]} is returned. + *

+ *

+ * Note: The returned array has runtime type {@code Object[]}, and requires that all elements + * in the iterator are of the same type. If a type-safe array is needed, use + * {@link #iteratorToArray(Iterator, Class)} instead. + *

+ * + * @param iterator the iterator to convert, may be {@code null} + * @param the element type + * @return the array containing elements from the iterator, + * or {@code null} if the input is {@code null} + * @since 3.18 + */ + @SuppressWarnings("unchecked") + public static T[] iteratorToArray(Iterator iterator) { + if (iterator == null) { + return null; + } + + if (!iterator.hasNext()) { + return (T[]) ArrayUtils.EMPTY_OBJECT_ARRAY; + } + + return (T[]) Streams.of(iterator).toArray(); + } + + /** + * Converts an {@link Iterator} into a typed array. + *

+ * Returns {@code null} if the input iterator or class is {@code null}. + * If the iterator has no elements, an empty array of the specified type is returned. + *

+ * + * @param iterator the iterator to convert, may be {@code null} + * @param clazz the class of the array component type, must not be {@code null} + * @param the element type + * @return the array containing elements from the iterator, or {@code null} if the input is {@code null} + * @since 3.18 + */ + @SuppressWarnings("unchecked") + public static T[] iteratorToArray(Iterator iterator, Class clazz) { + if (iterator == null || clazz == null) { + return null; + } + + final List list = new ArrayList<>(); + iterator.forEachRemaining(list::add); + + final T[] array = (T[]) Array.newInstance(clazz, list.size()); + return list.toArray(array); + } + /** * Converts the given array into a {@link java.util.Map}. Each element of the array * must be either a {@link java.util.Map.Entry} or an Array, containing at least two diff --git a/src/test/java/org/apache/commons/lang3/ArrayUtilsTest.java b/src/test/java/org/apache/commons/lang3/ArrayUtilsTest.java index 4163dfbffb4..f85bc50c918 100644 --- a/src/test/java/org/apache/commons/lang3/ArrayUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/ArrayUtilsTest.java @@ -40,6 +40,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.Date; +import java.util.Iterator; import java.util.Map; import java.util.Random; import java.util.function.Function; @@ -6452,6 +6453,55 @@ void testTextIndexesOfInt() { assertEquals(emptySet, ArrayUtils.indexesOf(array, 99)); } + @Test + void testIteratorToArray() { + final Iterator iterator = Arrays.asList("one", "two", "three").iterator(); + final Object[] normalResult = ArrayUtils.iteratorToArray(iterator); + + assertArrayEquals(new Object[]{"one", "two", "three"}, normalResult); + + final Iterator emptyIterator = Collections.emptyIterator(); + final Object[] emptyArray = ArrayUtils.iteratorToArray(emptyIterator); + + assertEquals(0, emptyArray.length); + assertNull(ArrayUtils.iteratorToArray(null)); + + final Iterator singletonIterator = Collections.singletonList(42).iterator(); + final Object[] result = ArrayUtils.iteratorToArray(singletonIterator); + + assertEquals(1, result.length); + assertEquals(42, result[0]); + + final Iterator mixed = Arrays.asList(1, "two", 3.0).iterator(); + final Object[] mixedResult = ArrayUtils.iteratorToArray(mixed); + + assertArrayEquals(new Object[]{1, "two", 3.0}, mixedResult); + } + + @Test + void testIteratorToArrayWithClazz() { + final Iterator it1 = Arrays.asList("a", "b", "c").iterator(); + final String[] result1 = ArrayUtils.iteratorToArray(it1, String.class); + assertArrayEquals(new String[] {"a", "b", "c"}, result1); + + final Iterator it2 = Collections.emptyList().iterator(); + final Integer[] result2 = ArrayUtils.iteratorToArray(it2, Integer.class); + assertNotNull(result2); + assertEquals(0, result2.length); + + final String[] result3 = ArrayUtils.iteratorToArray(null, String.class); + assertNull(result3); + + final Iterator it4 = Arrays.asList(1.1, 2.2).iterator(); + final Double[] result4 = ArrayUtils.iteratorToArray(it4, null); + assertNull(result4); + + final Iterator it5 = Arrays.asList("x", null, "z").iterator(); + final String[] result5 = ArrayUtils.iteratorToArray(it5, String.class); + assertArrayEquals(new String[] {"x", null, "z"}, result5); + } + + @Test void testToMap() { Map map = ArrayUtils.toMap(new String[][] { { "foo", "bar" }, { "hello", "world" } });