Java如何实现类似Numpy的array_split的列表均匀分块策略?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1943个文字,预计阅读时间需要8分钟。
在中,请确保以下要点:
>>> import numpy >>> x = [7, 3, 9, 10, 5, 6, 8, 13] # 长度为8的列表 >>> numpy.array_split(x, 3) # 分割成3个子数组 [array([7, 3, 9]), array([10, 5, 6]), array([ 8, 13])]
在这个例子中,一个包含8个元素的列表被分成了3个子列表,其长度分别为3、3、2。这种“按数量分块”的需求在Java中进行数据并行处理或分页时非常常见。
2. Java 实现策略:结合 Guava Lists.partition
Java标准库中没有直接对应 numpy.array_split 的函数。然而,通过巧妙地结合 Guava 库的 Lists.partition 方法和数学计算,我们可以实现相同的逻辑。
Lists.partition(list, size) 方法的作用是将一个列表分割成若干个子列表,每个子列表的最大长度为 size。最后一个子列表的长度可能会小于 size。 为了模拟 array_split 的“分割成 n 块”行为,我们需要首先计算出每个子列表的 最大容量 size,使得原始列表能够被分割成恰好 n 块。
2.1 计算子列表的最大容量
假设原始列表的长度为 totalSize,我们希望将其分割成 numberOfChunks 个子列表。 每个子列表的最大容量 sublistMaxSize 可以通过以下公式计算:
sublistMaxSize = Math.ceil(totalSize / (double) numberOfChunks)
这里使用 (double) 进行浮点除法,并使用 Math.ceil 向上取整。这样做的目的是确保:
立即学习“Java免费学习笔记(深入)”;
- 如果 totalSize 能被 numberOfChunks 整除,sublistMaxSize 就是精确的平均值。
- 如果不能整除,sublistMaxSize 会向上取整,这意味着每个子列表(除了最后一个可能较小)都将拥有这个最大容量,从而保证最终的子列表数量不会超过 numberOfChunks,并且会是期望的 numberOfChunks。
例如,对于长度为8的列表和3个分块: sublistMaxSize = Math.ceil(8 / 3.0) = Math.ceil(2.66...) = 3 这意味着每个子列表的最大容量为3。
2.2 使用 Lists.partition 进行分割
计算出 sublistMaxSize 后,我们就可以直接使用 Lists.partition 方法:
List<List<T>> partitionedLists = Lists.partition(originalList, sublistMaxSize);
这将返回一个包含所有子列表的列表。
3. 示例代码
以下是一个完整的Java示例,演示如何使用Guava库实现Numpy array_split 的功能:
首先,确保你的项目中已添加Guava依赖。如果你使用Maven,可以在 pom.xml 中添加:
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>32.1.3-jre</version> <!-- 请使用最新稳定版本 --> </dependency>
然后,是实现代码:
import com.google.common.collect.Lists; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class ListSplitterExample { /** * 将一个列表分割成指定数量的子列表,模拟Numpy的array_split行为。 * * @param originalList 原始列表 * @param numberOfChunks 期望的子列表数量 * @param <T> 列表中元素的类型 * @return 包含所有子列表的列表 * @throws IllegalArgumentException 如果 numberOfChunks 小于等于 0 */ public static <T> List<List<T>> splitListIntoChunks(List<T> originalList, int numberOfChunks) { if (numberOfChunks <= 0) { throw new IllegalArgumentException("子列表数量必须大于0。"); } if (originalList.isEmpty()) { return new ArrayList<>(); // 空列表直接返回空结果 } // 计算原始列表的总长度 int totalSize = originalList.size(); // 计算每个子列表的最大容量 // 使用Math.ceil确保向上取整,以保证最终的子列表数量不会超过numberOfChunks int sublistMaxSize = (int) Math.ceil(totalSize / (double) numberOfChunks); // 使用Guava的Lists.partition方法进行分割 // Lists.partition会创建固定大小的子列表,最后一个可能较小 // 由于我们精确计算了sublistMaxSize,最终的子列表数量将是numberOfChunks return Lists.partition(originalList, sublistMaxSize); } public static void main(String[] args) { // 示例数据 List<Integer> data = Arrays.asList(7, 3, 9, 10, 5, 6, 8, 13); int chunks = 3; // 期望分割成3个子列表 System.out.println("原始列表: " + data); System.out.println("期望分块数量: " + chunks); // 调用分块方法 List<List<Integer>> partitionedData = splitListIntoChunks(data, chunks); System.out.println("分割后的子列表:"); for (int i = 0; i < partitionedData.size(); i++) { System.out.println(" 子列表 " + (i + 1) + ": " + partitionedData.get(i)); } // 另一个示例:分割成5个子列表 List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry", "fig", "grape"); int wordChunks = 5; System.out.println("\n--- 另一个示例 ---"); System.out.println("原始列表: " + words); System.out.println("期望分块数量: " + wordChunks); List<List<String>> partitionedWords = splitListIntoChunks(words, wordChunks); System.out.println("分割后的子列表:"); for (int i = 0; i < partitionedWords.size(); i++) { System.out.println(" 子列表 " + (i + 1) + ": " + partitionedWords.get(i)); } // 边缘情况:分块数量大于列表长度 List<Integer> smallList = Arrays.asList(1, 2, 3); int smallListChunks = 5; System.out.println("\n--- 边缘情况:分块数量大于列表长度 ---"); System.out.println("原始列表: " + smallList); System.out.println("期望分块数量: " + smallListChunks); List<List<Integer>> partitionedSmallList = splitListIntoChunks(smallList, smallListChunks); System.out.println("分割后的子列表:"); for (int i = 0; i < partitionedSmallList.size(); i++) { System.out.println(" 子列表 " + (i + 1) + ": " + partitionedSmallList.get(i)); } } }
输出示例:
原始列表: [7, 3, 9, 10, 5, 6, 8, 13] 期望分块数量: 3 分割后的子列表: 子列表 1: [7, 3, 9] 子列表 2: [10, 5, 6] 子列表 3: [8, 13] --- 另一个示例 --- 原始列表: [apple, banana, cherry, date, elderberry, fig, grape] 期望分块数量: 5 分割后的子列表: 子列表 1: [apple, banana] 子列表 2: [cherry, date] 子列表 3: [elderberry] 子列表 4: [fig] 子列表 5: [grape] --- 边缘情况:分块数量大于列表长度 --- 原始列表: [1, 2, 3] 期望分块数量: 5 分割后的子列表: 子列表 1: [1] 子列表 2: [2] 子列表 3: [3]
可以看到,当期望分块数量为3时,8个元素的列表被分成了 [3, 3, 2] 三个子列表,与Numpy的行为一致。当期望分块数量为5时,7个元素的列表被分成了 [2, 2, 1, 1, 1] 五个子列表,也符合预期。
4. 注意事项与总结
- Guava依赖:此方法依赖于Google Guava库。如果项目中不允许引入第三方库,则需要手动实现分块逻辑,这会稍微复杂一些,需要循环和 subList() 方法来完成。
- 性能:Lists.partition 返回的是原列表的视图(view),而不是创建新的子列表拷贝。这意味着它具有较高的性能,并且内存开销较小。对返回的子列表的修改会影响原始列表。如果需要独立的子列表,应该进行深拷贝。
- 空列表和分块数量:示例代码已处理了空列表和 numberOfChunks <= 0 的情况。当 numberOfChunks 大于原始列表长度时,每个元素将单独成为一个子列表,其余的子列表将是空列表(如果 sublistMaxSize 计算结果为1,且列表元素数量少于 numberOfChunks,则 Lists.partition 会返回与原始列表长度相同的子列表数量,每个子列表包含一个元素,这与Numpy array_split 在这种情况下返回的行为一致)。
- 通用性:splitListIntoChunks 方法是泛型的,可以处理任何类型的列表。
通过这种结合数学计算和Guava Lists.partition 的策略,我们可以在Java中优雅且高效地实现类似于Numpy array_split 的列表分块功能,这对于处理大规模数据、实现并行计算任务或构建分页逻辑都非常有用。
本文共计1943个文字,预计阅读时间需要8分钟。
在中,请确保以下要点:
>>> import numpy >>> x = [7, 3, 9, 10, 5, 6, 8, 13] # 长度为8的列表 >>> numpy.array_split(x, 3) # 分割成3个子数组 [array([7, 3, 9]), array([10, 5, 6]), array([ 8, 13])]
在这个例子中,一个包含8个元素的列表被分成了3个子列表,其长度分别为3、3、2。这种“按数量分块”的需求在Java中进行数据并行处理或分页时非常常见。
2. Java 实现策略:结合 Guava Lists.partition
Java标准库中没有直接对应 numpy.array_split 的函数。然而,通过巧妙地结合 Guava 库的 Lists.partition 方法和数学计算,我们可以实现相同的逻辑。
Lists.partition(list, size) 方法的作用是将一个列表分割成若干个子列表,每个子列表的最大长度为 size。最后一个子列表的长度可能会小于 size。 为了模拟 array_split 的“分割成 n 块”行为,我们需要首先计算出每个子列表的 最大容量 size,使得原始列表能够被分割成恰好 n 块。
2.1 计算子列表的最大容量
假设原始列表的长度为 totalSize,我们希望将其分割成 numberOfChunks 个子列表。 每个子列表的最大容量 sublistMaxSize 可以通过以下公式计算:
sublistMaxSize = Math.ceil(totalSize / (double) numberOfChunks)
这里使用 (double) 进行浮点除法,并使用 Math.ceil 向上取整。这样做的目的是确保:
立即学习“Java免费学习笔记(深入)”;
- 如果 totalSize 能被 numberOfChunks 整除,sublistMaxSize 就是精确的平均值。
- 如果不能整除,sublistMaxSize 会向上取整,这意味着每个子列表(除了最后一个可能较小)都将拥有这个最大容量,从而保证最终的子列表数量不会超过 numberOfChunks,并且会是期望的 numberOfChunks。
例如,对于长度为8的列表和3个分块: sublistMaxSize = Math.ceil(8 / 3.0) = Math.ceil(2.66...) = 3 这意味着每个子列表的最大容量为3。
2.2 使用 Lists.partition 进行分割
计算出 sublistMaxSize 后,我们就可以直接使用 Lists.partition 方法:
List<List<T>> partitionedLists = Lists.partition(originalList, sublistMaxSize);
这将返回一个包含所有子列表的列表。
3. 示例代码
以下是一个完整的Java示例,演示如何使用Guava库实现Numpy array_split 的功能:
首先,确保你的项目中已添加Guava依赖。如果你使用Maven,可以在 pom.xml 中添加:
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>32.1.3-jre</version> <!-- 请使用最新稳定版本 --> </dependency>
然后,是实现代码:
import com.google.common.collect.Lists; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class ListSplitterExample { /** * 将一个列表分割成指定数量的子列表,模拟Numpy的array_split行为。 * * @param originalList 原始列表 * @param numberOfChunks 期望的子列表数量 * @param <T> 列表中元素的类型 * @return 包含所有子列表的列表 * @throws IllegalArgumentException 如果 numberOfChunks 小于等于 0 */ public static <T> List<List<T>> splitListIntoChunks(List<T> originalList, int numberOfChunks) { if (numberOfChunks <= 0) { throw new IllegalArgumentException("子列表数量必须大于0。"); } if (originalList.isEmpty()) { return new ArrayList<>(); // 空列表直接返回空结果 } // 计算原始列表的总长度 int totalSize = originalList.size(); // 计算每个子列表的最大容量 // 使用Math.ceil确保向上取整,以保证最终的子列表数量不会超过numberOfChunks int sublistMaxSize = (int) Math.ceil(totalSize / (double) numberOfChunks); // 使用Guava的Lists.partition方法进行分割 // Lists.partition会创建固定大小的子列表,最后一个可能较小 // 由于我们精确计算了sublistMaxSize,最终的子列表数量将是numberOfChunks return Lists.partition(originalList, sublistMaxSize); } public static void main(String[] args) { // 示例数据 List<Integer> data = Arrays.asList(7, 3, 9, 10, 5, 6, 8, 13); int chunks = 3; // 期望分割成3个子列表 System.out.println("原始列表: " + data); System.out.println("期望分块数量: " + chunks); // 调用分块方法 List<List<Integer>> partitionedData = splitListIntoChunks(data, chunks); System.out.println("分割后的子列表:"); for (int i = 0; i < partitionedData.size(); i++) { System.out.println(" 子列表 " + (i + 1) + ": " + partitionedData.get(i)); } // 另一个示例:分割成5个子列表 List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry", "fig", "grape"); int wordChunks = 5; System.out.println("\n--- 另一个示例 ---"); System.out.println("原始列表: " + words); System.out.println("期望分块数量: " + wordChunks); List<List<String>> partitionedWords = splitListIntoChunks(words, wordChunks); System.out.println("分割后的子列表:"); for (int i = 0; i < partitionedWords.size(); i++) { System.out.println(" 子列表 " + (i + 1) + ": " + partitionedWords.get(i)); } // 边缘情况:分块数量大于列表长度 List<Integer> smallList = Arrays.asList(1, 2, 3); int smallListChunks = 5; System.out.println("\n--- 边缘情况:分块数量大于列表长度 ---"); System.out.println("原始列表: " + smallList); System.out.println("期望分块数量: " + smallListChunks); List<List<Integer>> partitionedSmallList = splitListIntoChunks(smallList, smallListChunks); System.out.println("分割后的子列表:"); for (int i = 0; i < partitionedSmallList.size(); i++) { System.out.println(" 子列表 " + (i + 1) + ": " + partitionedSmallList.get(i)); } } }
输出示例:
原始列表: [7, 3, 9, 10, 5, 6, 8, 13] 期望分块数量: 3 分割后的子列表: 子列表 1: [7, 3, 9] 子列表 2: [10, 5, 6] 子列表 3: [8, 13] --- 另一个示例 --- 原始列表: [apple, banana, cherry, date, elderberry, fig, grape] 期望分块数量: 5 分割后的子列表: 子列表 1: [apple, banana] 子列表 2: [cherry, date] 子列表 3: [elderberry] 子列表 4: [fig] 子列表 5: [grape] --- 边缘情况:分块数量大于列表长度 --- 原始列表: [1, 2, 3] 期望分块数量: 5 分割后的子列表: 子列表 1: [1] 子列表 2: [2] 子列表 3: [3]
可以看到,当期望分块数量为3时,8个元素的列表被分成了 [3, 3, 2] 三个子列表,与Numpy的行为一致。当期望分块数量为5时,7个元素的列表被分成了 [2, 2, 1, 1, 1] 五个子列表,也符合预期。
4. 注意事项与总结
- Guava依赖:此方法依赖于Google Guava库。如果项目中不允许引入第三方库,则需要手动实现分块逻辑,这会稍微复杂一些,需要循环和 subList() 方法来完成。
- 性能:Lists.partition 返回的是原列表的视图(view),而不是创建新的子列表拷贝。这意味着它具有较高的性能,并且内存开销较小。对返回的子列表的修改会影响原始列表。如果需要独立的子列表,应该进行深拷贝。
- 空列表和分块数量:示例代码已处理了空列表和 numberOfChunks <= 0 的情况。当 numberOfChunks 大于原始列表长度时,每个元素将单独成为一个子列表,其余的子列表将是空列表(如果 sublistMaxSize 计算结果为1,且列表元素数量少于 numberOfChunks,则 Lists.partition 会返回与原始列表长度相同的子列表数量,每个子列表包含一个元素,这与Numpy array_split 在这种情况下返回的行为一致)。
- 通用性:splitListIntoChunks 方法是泛型的,可以处理任何类型的列表。
通过这种结合数学计算和Guava Lists.partition 的策略,我们可以在Java中优雅且高效地实现类似于Numpy array_split 的列表分块功能,这对于处理大规模数据、实现并行计算任务或构建分页逻辑都非常有用。

