diff --git a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/CollectionUtil.java b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/CollectionUtil.java new file mode 100644 index 0000000..44c75c0 --- /dev/null +++ b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/CollectionUtil.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2024-2025 OnixByte. + * + * 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 com.onixbyte.devkit.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.function.Supplier; + +/** + * A utility class providing static methods for manipulating collections. + * + * @author zihluwang + */ +public final class CollectionUtil { + + private static final Logger log = LoggerFactory.getLogger(CollectionUtil.class); + + /** + * Private constructor to prevent instantiation of this utility class. + */ + private CollectionUtil() { + } + + /** + * Splits a collection into a list of sub-collections, each with a maximum size specified by + * the caller. + *

+ * This method takes an original collection and divides it into smaller sub-collections, + * ensuring that each sub-collection contains no more than the specified maximum size. If the + * original collection's size is less than or equal to the maximum size, it is returned as a + * single sub-collection. The sub-collections are created using the provided collection factory. + * + * @param the type of elements in the collection + * @param the type of the collection, which must extend {@link Collection} + * @param originalCollection the collection to be split into sub-collections + * @param maxSize the maximum number of elements allowed in each sub-collection + * @param collectionFactory a supplier that creates new instances of the sub-collection type + * @return a list of sub-collections, each containing up to {@code maxSize} elements + * @throws IllegalArgumentException if {@code originalCollection} is {@code null}, + * {@code maxSize} is less than zero, or + * {@code collectionFactory} is {@code null} + */ + public static > List chunk(C originalCollection, + int maxSize, + Supplier collectionFactory) { + // check inputs + if (Objects.isNull(originalCollection)) { + throw new IllegalArgumentException("Collection must not be null."); + } + + if (maxSize < 0) { + throw new IllegalArgumentException("maxSize must greater than 0."); + } + + if (Objects.isNull(collectionFactory)) { + throw new IllegalArgumentException("Factory method cannot be null."); + } + + var result = new ArrayList(); + var size = originalCollection.size(); + + // if original collection is empty or the size less than maxSize, return it as a single + // sub collection + if (size <= maxSize) { + var singleCollection = collectionFactory.get(); + singleCollection.addAll(originalCollection); + result.add(singleCollection); + return result; + } + + // use iterator to split the given collection + var iter = originalCollection.iterator(); + var count = 0; + var currentSubCollection = collectionFactory.get(); + + while (iter.hasNext()) { + var element = iter.next(); + currentSubCollection.add(element); + count++; + + // add sub collection to result when current sub collection reached maxSize or + // collection traverse is completed + if (count % maxSize == 0 || !iter.hasNext()) { + result.add(currentSubCollection); + currentSubCollection = collectionFactory.get(); + } + } + + return result; + } + +} diff --git a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/ListUtil.java b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/ListUtil.java index 00000bc..f1ed1ba 100644 --- a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/ListUtil.java +++ b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/ListUtil.java @@ -19,25 +19,77 @@ package com.onixbyte.devkit.utils; import java.util.ArrayList; import java.util.List; +import java.util.Objects; +import java.util.function.Supplier; /** - * A utility class for splitting a List into multiple sub lists, where each sublist has a maximum - * number of elements specified by the user. + * A utility class providing static methods for manipulating lists. * * @author siujamo + * @author zihluwang */ public final class ListUtil { /** * Private constructor to prevent instantiation of this utility class. - *

- * This class provides static methods for list manipulation and is not intended to be - * instantiated. The private constructor ensures that no instances can be created, enforcing - * the utility nature of the class. */ private ListUtil() { } + /** + * Splits a given List into a List of sub lists, where each sublist contains at most + * {@code maxSize} elements. The original list is not modified, and new sub lists are created + * to hold the partitioned data. + *

+ * If the original list's size is less than or equal to {@code maxSize}, a single sublist + * containing all elements is returned. If the list is empty, an empty list of sub lists + * is returned. + * + * @param the type of elements in the list + * @param originalList the list to be split, must not be null + * @param maxSize the maximum number of elements in each sublist, must be positive + * @param listFactory list factory + * @return a List of sub lists, where each sublist has at most {@code maxSize} elements + * @throws IllegalArgumentException if {@code originalList} is null or {@code maxSize} is less + * than or equal to 0 + */ + public static List> chunk(List originalList, int maxSize, Supplier> listFactory) { + // check input + if (Objects.isNull(originalList)) { + throw new IllegalArgumentException("List cannot be null"); + } + + if (maxSize <= 0) { + throw new IllegalArgumentException("Max size should be greater than 0"); + } + + if (Objects.isNull(listFactory)) { + throw new IllegalArgumentException("List factory cannot be null"); + } + + var result = new ArrayList>(); + var size = originalList.size(); + + // if the original list is empty or smaller than maxSize, return it as a single sublist + if (size <= maxSize) { + var singleSubList = listFactory.get(); + singleSubList.addAll(originalList); + result.add(singleSubList); + return result; + } + + // split the list + for (var i = 0; i < size; i += maxSize) { + var end = Math.min(i + maxSize, size); // ensure not to exceed list length + var subList = originalList.subList(i, end); + var subListWrapper = listFactory.get(); + subListWrapper.addAll(subList); + result.add(subListWrapper); // create a new list to avoid reference issues + } + + return result; + } + /** * Splits a given List into a List of sub lists, where each sublist contains at most * {@code maxSize} elements. The original list is not modified, and new sub lists are created @@ -53,30 +105,10 @@ public final class ListUtil { * @return a List of sub lists, where each sublist has at most {@code maxSize} elements * @throws IllegalArgumentException if {@code originalList} is null or {@code maxSize} is less * than or equal to 0 + * @see #chunk(List, int, Supplier) */ - public static List> splitList(List originalList, int maxSize) { - // check input - if (originalList == null || maxSize <= 0) { - throw new IllegalArgumentException("List cannot be null and maxSize must be positive"); - } - - var result = new ArrayList>(); - var size = originalList.size(); - - // if the original list is empty or smaller than maxSize, return it as a single sublist - if (size <= maxSize) { - result.add(new ArrayList<>(originalList)); - return result; - } - - // split the list - for (var i = 0; i < size; i += maxSize) { - var end = Math.min(i + maxSize, size); // ensure not to exceed list length - List subList = originalList.subList(i, end); - result.add(new ArrayList<>(subList)); // create a new list to avoid reference issues - } - - return result; + public static List> chunk(List originalList, int maxSize) { + return chunk(originalList, maxSize, ArrayList::new); } }