J List切成多个list

博客主要围绕Java中List的操作展开,包括将List切成多个List进行操作,对List切换junit测试,还涉及对象模块。此外,还提出可对拆分后的List进行分组排序,使用纯Java代码处理。

将list切成多个list在进行操作

list切换junit测试

package com.tan.tools.test;

import org.junit.Before;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @ClassName ListDateSpit
 * @Description TODO
 * @Author Tan.li
 * @Date 2020-12-02
 * @Since 1.0
 */

public class ListDateSpit {

    private List<String> listData = new ArrayList<>();


    @Before
    public void testInitData() {
        for (int i = 0; i < 1000; i++) {
            listData.add(String.format("数据ID-%s", i + 1));
        }
    }


    @Test
    public void testForeach() {
        for (String str : listData) {
            System.out.println("输出数据:" + str);
        }
    }

    @Test
    public void testSpitList() {

        List<List<String>> lisize = spitList(listData, 201);

        for (List<String> listList : lisize) {

            System.out.println(String.format("listlist:%s", listList.size()));
            for(String stt:listList){
                System.out.println(String.format("拆解后数据:%s", stt));
            }
            System.err.println("==============================================");

        }
    }

    /**
     * @Description TODO  拆分list 与分页逻辑相符
     * @Author Tan.li
     * @Date 2020-12-02
     */
    public static List<List<String>> spitList(List<String> list, int pageSize) {

        List<List<String>> lists = new ArrayList<>();

        int countSize = list.size();

        int subSize = (countSize % pageSize > 0) ? (countSize / pageSize) + 1 : (countSize / pageSize);

        int currentSize = 0;
        for (int i = 0; i < subSize; i++) {
            System.out.println(String.format("subSize%s,i:%s", subSize, i));
            if (subSize == (i + 1)) {
                System.out.println(String.format("currentSize:%s,countSize:%s", currentSize, countSize));
                List<String> newList = list.subList(currentSize, countSize);
                lists.add(newList);
            } else {
                System.out.println(String.format("currentSize:%s,countSize:%s", currentSize, pageSize));
                List<String> newList = list.subList(currentSize, currentSize + pageSize);
                lists.add(newList);
            }
            currentSize += pageSize;
        }
        return lists;
    }

    @Test
    public void testDistinctList(){
        System.out.println(String.format("增加重复数据前listData size:%s",listData.size()));
        listData.addAll(createData());
        System.out.println(String.format("增加重复数据之后listData size:%s",listData.size()));

        List<String> testData=distinctList(listData);

        for (String tts:testData) {
            System.out.println(String.format("去重复后Data:"+tts));
        }
    }

    /**
     * @Description TODO 新建数据
     * @Author Tan.li
     * @Date 2020-12-02
     */
    public List<String> createData(){
        List<String> newList=new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            newList.add(String.format("数据ID-%s", i + 1));
        }
        return newList;
    }

    /**
     * @Description TODO 去重
     * @Author Tan.li
     * @Date 2020-12-02
     */
    public static List<String> distinctList(List<String> list){
        return list.stream().distinct().collect(Collectors.toList());
    }


    @Test
    public void testItemVOdistinct(){
        List<ItemVO> list=createItemData();

        for (ItemVO it:list){
            System.out.println(String.format("编号:%s,货品名称:%s",it.itemCode,it.itemName));
        }
    }


    public static List<ItemVO> createItemData(){
        List<ItemVO> itemList=new ArrayList<>();
        for(int i=0;i<1000;i++){
            itemList.add(new ItemVO(i,String.format("货品名称%s编号",i)));
        }
        return itemList;
    }

}

对象模块

package com.tan.tools.test;

/**
 * @ClassName ItemVO
 * @Description TODO
 * @Author Tan.li
 * @Date 2020-12-02
 * @Since 1.0
 */
public class ItemVO {

    Integer itemCode;

    String itemName;

    public ItemVO(Integer itemCode, String itemName) {
        this.itemCode = itemCode;
        this.itemName = itemName;
    }

    public Integer getItemCode() {
        return itemCode;
    }

    public void setItemCode(Integer itemCode) {
        this.itemCode = itemCode;
    }

    public String getItemName() {
        return itemName;
    }

    public void setItemName(String itemName) {
        this.itemName = itemName;
    }
}

思考:既然已经拆分,也可以将list进行分组排序,纯J代码进行处理

<think> 我们被要求将Java List集合切割成n个部分。注意,这里不是按固定大小切割,而是切割成n个子列表。 一种常见的方法是计算每个子列表的大致大小,然后根据这个大小进行切割。由于列表大小可能不能被n整除,我们需要将余数分配到前面的一些子列表中。 思路: 1. 计算每个子列表的基本大小:size = list.size() / n 2. 计算余数:remainder = list.size() % n 3. 使用循环,从0到n-1切割: - 对于前remainder个子列表,每个大小为size + 1 - 对于后面的子列表,每个大小为size 但是注意,还有一种情况:如果列表的大小小于n,那么每个子列表只有一个元素,直到列表元素用完,后面的子列表为空(或者我们可以选择不创建空列表,但通常我们会创建n个列表,其中一些可能为空)。根据需求,我们可以选择创建空列表或者调整n的值,但这里我们按照n来创建。 另一种方法是使用Guava库的Lists.partition,但是它只能按固定大小切割,不能直接切割成n个部分。因此,我们需要自己实现。 我们也可以使用Apache Commons Collections的ListUtils.partition,但同样也是按固定大小分割。所以,我们最好自己写。 下面是一个实现示例: 步骤: 1. 初始化一个列表(结果列表)用于存放切割后的子列表。 2. 计算每个区块的基本大小和余数。 3. 遍历0到n-1,计算每个子列表的起始和结束索引。 起始索引:i * size + Math.min(i, remainder) 结束索引:起始索引 + size + (i < remainder ? 1 : 0) 4. 使用subList方法切割原列表,注意subList是左闭右开区间。 注意:原列表应该是非空的,并且n应该大于0。如果n大于列表大小,那么会有一些空列表(当列表大小小于n时,前面的子列表各一个元素,后面的为空)。 我们写一个通用的方法: 示例代码: ```java import java.util.ArrayList; import java.util.List; public class ListPartition { public static <T> List<List<T>> partition(final List<T> list, final int n) { if (n <= 0) { throw new IllegalArgumentException("n must be greater than 0"); } int size = list.size(); int baseSize = size / n; int remainder = size % n; List<List<T>> partitions = new ArrayList<>(n); int start = 0; for (int i = 0; i < n; i++) { // 计算当前子列表的长度:前面的余数个列表长度比后面的大1 int length = baseSize + (i < remainder ? 1 : 0); if (start >= size) { // 如果起始位置已经超过或等于列表大小,添加空列表 partitions.add(new ArrayList<>()); } else { // 计算结束位置,注意不能超过列表大小 int end = Math.min(start + length, size); partitions.add(list.subList(start, end)); start = end; } } return partitions; } public static void main(String[] args) { List<Integer> list = new ArrayList<>(); for (int i = 1; i <= 10; i++) { list.add(i); } int n = 3; List<List<Integer>> partitions = partition(list, n); for (List<Integer> partition : partitions) { System.out.println(partition); } // 预期输出: // [1, 2, 3, 4] // 10个元素分成3份,第一份4个,第二份3个,第三份3个?但实际上我们的算法:前余数(10%3=1)个列表多一个,所以只有第一个列表多一个:4,3,3 // 但是按照我们的划分:第一个子列表:0到4(0,1,2,3)-> [1,2,3,4];第二个子列表:4到7(4,5,6)-> [5,6,7];第三个子列表:7到10(7,8,9)-> [8,9,10]?不对,注意索引从0开始,结束索引是开区间。 // 我们重新计算: // 第一个列表:开始0,长度=10/3=3,余数1,所以第一个列表长度=3+1=4 -> [0,4) -> [1,2,3,4] // 第二个列表:开始=4,长度=3+(1<余数1? 第二个i=1,此时i<remainder? 余数1,i=1不小于余数1(因为余数1,i从0开始,所以i=0时满足,i=1时不满足),所以长度=3 -> [4,7) -> [5,6,7] // 第三个列表:开始=7,长度=3 -> [7,10) -> [8,9,10] 但是这里第三个列表应该是3个元素:5,6,7应该是第二个列表,第三个列表应该是从7到10?不对,索引7是第8个元素。 // 实际上,我们的列表:索引0:1, 1:2, 2:3, 3:4, 4:5, 5:6, 6:7, 7:8, 8:9, 9:10 // 第一个子列表:0到4(不包括4)?不对,subList(0,4)是[0,3]四个元素(1,2,3,4)。然后下一个从4开始,长度3:4到7(不包括7)-> [4,5,6] -> 5,6,7。然后下一个从7开始,长度3:7到10(不包括10,但10是size,所以就是到末尾)-> [7,8,9] -> 8,9,10。这样最后一个列表只有3个元素,而第一个列表4个,第二个3个,第三个3个。总数正确。 // 但是上面的算法中,我们使用了结束索引为start+length,然后作为subList的结束索引(这个结束索引是exclusive的)。 // 所以代码中的end = start + length,然后subList(start, end)就是正确的。 // 输出: // [1,2,3,4] // [5,6,7] // [8,9,10] } } ``` 但是,上面的代码中,我们使用了`list.subList`,它返回的是原列表的视图,这意味着对子列表的修改会影响原列表。如果希望每个子列表都是独立的(不影响原列表),则需要创建新的列表: 在切割后,我们可以创建一个新的ArrayList: ```java partitions.add(new ArrayList<>(list.subList(start, end))); ``` 那么,完整的方法可以调整为(如果需要独立): 根据需求,如果不需要独立,使用原视图即可。这里我们提供两种选择?但题目没有明确,通常切割成n个部分,可能是为了并行处理,所以通常需要独立。但题目没有明确,我们就按原视图返回。注意:原视图要求原列表不能改变(结构性修改),否则子列表会失效。 因此,如果后续原列表可能会被修改,或者子列表需要独立操作不影响原列表,则创建新列表。否则,使用视图更节省内存。 我们可以提供一个参数来控制是否创建新列表,但题目没有要求,我们就按视图返回。如果需要创建新列表,可以在方法中添加一个参数,或者修改方法内部实现。这里我们按照视图返回,因为题目没有特殊要求。 但是,如果我们使用视图,那么调用者需要注意:原列表的结构性修改(如添加、删除)会导致子列表的并发修改异常。所以,如果后续不会修改原列表,则可以使用视图。 另一种方式:我们可以默认创建新的列表,这样避免与原列表的关联。这样更安全。 修改方法:创建新列表。 因此,将切割部分改为: ```java partitions.add(new ArrayList<>(list.subList(start, end))); ``` 这样每个子列表都是独立的ArrayList。 测试:将10个元素的列表分成3份,得到4,3,3。 另外,考虑边界情况:n=0,我们已经在开始检查。如果n大于列表大小,比如列表10个元素,分成15份,那么前10份各一个元素,后面5份为空列表。 但是,如果用户希望后面没有空列表,我们可以调整n的值,但题目要求切割成n个,所以我们要返回n个列表,即使后面的是空的。 因此,我们按照上述实现。 完整代码(创建新列表): ```java import java.util.ArrayList; import java.util.Collections; import java.util.List; public class ListUtils { /** * 将List分割成n个子列表 * @param list 要分割的列表 * @param n 分割成n个子列表 * @return 包含n个子列表的列表 */ public static <T> List<List<T>> partition(final List<T> list, final int n) { if (n <= 0) { throw new IllegalArgumentException("n must be greater than 0"); } if (list == null || list.isEmpty()) { // 如果列表为空,返回n个空列表 return Collections.nCopies(n, new ArrayList<T>()); } int size = list.size(); int baseSize = size / n; int remainder = size % n; List<List<T>> partitions = new ArrayList<>(n); int start = 0; for (int i = 0; i < n; i++) { int length = baseSize + (i < remainder ? 1 : 0); if (length <= 0) { partitions.add(new ArrayList<>()); } else { int end = start + length; if (end > size) { end = size; } partitions.add(new ArrayList<>(list.subList(start, end))); start = end; } } return partitions; } } ``` 测试: 情况1:10个元素,n=3 -> 4,3,3 情况2:5个元素,n=3 -> 2,2,1 情况3:3个元素,n=5 -> 1,1,1,0,0 -> 但实际上我们的循环中,对于长度<=0的情况,我们添加空列表。在循环中,前3个子列表长度=1(因为baseSize=0,remainder=3,所以前3个列表长度=0+1=1),然后后面2个列表长度=0,所以添加空列表。 注意:列表为空时,我们返回n个空列表。 这个实现可以满足要求。 另外,我们也可以使用流的方式,但这里使用循环更清晰。 因此,我们提供上述方法。 注意:如果原列表很大,创建新的ArrayList会占用更多内存,但通常切割是为了处理每个子部分,所以创建新列表可以避免并发修改问题。 如果内存敏感,且原列表不会被修改,我们可以返回视图,但这样需要文档说明。 根据题目,没有特别要求,我们按照安全的方式(创建新列表)实现。 最后,我们可以在方法中添加一些注释,说明每个子列表都是原列表的拷贝(独立)。 相关问题: 1. 如何在Java中按固定大小分割List? 2. 使用Guava分割List的方法是什么? 3. 分割后的子列表是否会影响原列表? 4. 如果不想创建新的List,如何实现视图分割? </think> 在Java中将`List`集合切割成`n`个子列表,可以通过以下步骤实现: ### 核心实现方法 ```java import java.util.ArrayList; import java.util.List; public class ListPartition { /** * 将List分割成n个近似等长的子列表 * @param list 原始列表 * @param n 分割数量 * @return 包含n个子列表的集合 */ public static <T> List<List<T>> partition(List<T> list, int n) { if (n <= 0) throw new IllegalArgumentException("n必须大于0"); int size = list.size(); int baseSize = size / n; // 每个子列表的基础大小 int remainder = size % n; // 余数,用于调整前几个子列表的大小 List<List<T>> partitions = new ArrayList<>(n); int start = 0; for (int i = 0; i < n; i++) { // 计算当前子列表的长度(余数不为0时前几个子列表额外+1) int length = baseSize + (i < remainder ? 1 : 0); if (start >= size) { partitions.add(new ArrayList<>()); // 处理空列表 } else { int end = Math.min(start + length, size); partitions.add(new ArrayList<>(list.subList(start, end))); start = end; } } return partitions; } // 示例用法 public static void main(String[] args) { List<Integer> list = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9); List<List<Integer>> result = partition(list, 4); // 输出: [[1,2,3], [4,5], [6,7], [8,9]] System.out.println(result); } } ``` ### 关键说明 1. **算法逻辑**: - 计算基础大小:$ \text{baseSize} = \lfloor \frac{\text{list.size()}}{n} \rfloor $ - 计算余数:$ \text{remainder} = \text{list.size()} \mod n $ - 前`remainder`个子列表大小为$\text{baseSize} + 1$,其余为$\text{baseSize}$ 2. **边界处理**: - 当`n > list.size()`时,多出的子列表返回空集合 - 当`n <= 0`时抛出异常 - 使用`list.subList()`切割后立即创建新`ArrayList`,避免原始列表修改导致的`ConcurrentModificationException` 3. **时间复杂度**:$O(n)$,仅需遍历一次列表 ### 替代方案 1. **Guava库实现**(需添加依赖): ```java import com.google.common.collect.Lists; List<List<Integer>> partitions = Lists.partition(list, size); ``` 注:Guava只能按**固定大小**分割,需提前计算`size = ceil(list.size()/n)`[^1] 2. **Stream API实现**(Java 8+): ```java int size = (int) Math.ceil((double) list.size() / n); Collection<List<T>> partitions = IntStream.range(0, n) .mapToObj(i -> list.subList(i * size, Math.min((i + 1) * size, list.size()))) .collect(Collectors.toList()); ``` ### 注意事项 - 对链表`LinkedList`使用`subList()`效率较低($O(n)$时间复杂度),建议转为`ArrayList` - 切割后的子列表**独立于原列表**(深拷贝),修改互不影响 - 当元素总数不能被`n`整除时,子列表长度最大相差1(如9个元素分4份:$3+2+2+2$)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值