【问题标题】:Efficient way to divide a list into lists of n size将列表划分为 n 大小列表的有效方法
【发布时间】:2011-08-15 01:16:55
【问题描述】:

我有一个ArrayList,我想将它分成n 大小的更小的List 对象,并对每个对象执行操作。 我目前执行此操作的方法是使用 Java 中的 ArrayList 对象实现的。任何伪代码都可以。

    for (int i = 1; i <= Math.floor((A.size() / n)); i++) {
            ArrayList temp = subArray(A, ((i * n) - n),
                    (i * n) - 1);
            // do stuff with temp
        }

    private ArrayList<Comparable> subArray(ArrayList A, int start,
                int end) {
            ArrayList toReturn = new ArrayList();
            for (int i = start; i <= end; i++) {
                toReturn.add(A.get(i));
            }
            return toReturn;
        }

其中A 是列表,n 是所需列表的大小

我认为这种方式在处理多达 100 万个相当大的列表时会花费太多时间,因此我正在尝试找出更有效的方法。

【问题讨论】:

标签: java arraylist partitioning


【解决方案1】:

由于您想优化性能,您应该使用并行流而不是 for 循环。这样您就可以使用多个线程。

Lists.partition(A, n).parallelStream().forEach({
    //do stuff with temp
});

如果符合您的目的,您还可以使用其他方式来处理流,例如收集或映射。

【讨论】:

  • 请注意,Lists.partition() 似乎是 Google Guava 特定的实现。
【解决方案2】:

// 测试数据

List<Integer> list = Arrays.asList(0, 1, 2,     3, 4, 5,    6, 7, 8,    9);
int n = 3;

// java 8 流和 list.subList 的一行(语句)

List<List<Integer>> partitions = IntStream.range(0, list.size())
    .filter(i -> i % n == 0)
    .mapToObj(i -> list.subList(i, Math.min(i + n, list.size() )))
    .collect(Collectors.toList());

【讨论】:

    【解决方案3】:

    好吧,在我看到 ColinD 的回答 (+1) 之前,我自己写了一个,使用 Guava 绝对是要走的路。一个人呆着太有趣了,所以下面给你一个列表的副本而不是视图,所以 GUava 肯定比这更有效。我发布这个是因为写起来很有趣,而不是暗示它同样有效:

    Hamcrest 测试(无论如何之一):

    assertThat(chunk(asList("a", "b", "c", "d", "e"), 2), 
               equalTo(asList(asList("a", "b"), asList("c", "d"), asList("e"))));
    

    代码:

    public static <T> Iterable<Iterable<T>> chunk(Iterable<T> in, int size) {
        List<Iterable<T>> lists = new ArrayList();
        Iterator<T> i = in.iterator();
        while (i.hasNext()) {
            List<T> list = new ArrayList();
            for (int j=0; i.hasNext() && j<size; j++) {
                list.add(i.next());
            }
            lists.add(list);
        }
        return lists;
    }
    

    【讨论】:

      【解决方案4】:
      import java.util.ArrayList;
      import java.util.Arrays;
      import java.util.List;
      
      public class SubListTest
      {
          public static void main(String[] args)
          {
              List<String> alphabetNames = new ArrayList<String>();
      
              // populate alphabetNames array with AAA,BBB,CCC,.....
              int a = (int) 'A';
              for (int i = 0; i < 26; i++)
              {
                  char x = (char) (a + i);
                  char[] array = new char[3];
                  Arrays.fill(array, x);
                  alphabetNames.add(new String(array));
              }
      
              int[] maxListSizes = new int[]
              {
                  5, 10, 15, 20, 25, 30
              };
      
              for (int maxListSize : maxListSizes)
              {
                  System.out.println("######################################################");
                  System.out.println("Partitioning original list of size " + alphabetNames.size() + " in to sub lists of max size "
                      + maxListSize);
      
                  ArrayList<List<String>> subListArray = new ArrayList<List<String>>();
                  if (alphabetNames.size() <= maxListSize)
                  {
                      subListArray.add(alphabetNames);
                  }
                  else
                  {
                      // based on subLists of maxListSize X
                      int subListArraySize = (alphabetNames.size() + maxListSize - 1) / maxListSize;
                      for (int i = 0; i < subListArraySize; i++)
                      {
                          subListArray.add(alphabetNames.subList(i * maxListSize,
                              Math.min((i * maxListSize) + maxListSize, alphabetNames.size())));
                      }
                  }
      
                  System.out.println("Resulting number of partitions " + subListArray.size());
      
                  for (List<String> subList : subListArray)
                  {
                      System.out.println(subList);
                  }
              }
          }
      }
      

      输出:

      ######################################################
      Partitioning original list of size 26 in to sub lists of max size 5
      Resulting number of partitions 6
      [AAA, BBB, CCC, DDD, EEE]
      [FFF, GGG, HHH, III, JJJ]
      [KKK, LLL, MMM, NNN, OOO]
      [PPP, QQQ, RRR, SSS, TTT]
      [UUU, VVV, WWW, XXX, YYY]
      [ZZZ]
      ######################################################
      Partitioning original list of size 26 in to sub lists of max size 10
      Resulting number of partitions 3
      [AAA, BBB, CCC, DDD, EEE, FFF, GGG, HHH, III, JJJ]
      [KKK, LLL, MMM, NNN, OOO, PPP, QQQ, RRR, SSS, TTT]
      [UUU, VVV, WWW, XXX, YYY, ZZZ]
      ######################################################
      Partitioning original list of size 26 in to sub lists of max size 15
      Resulting number of partitions 2
      [AAA, BBB, CCC, DDD, EEE, FFF, GGG, HHH, III, JJJ, KKK, LLL, MMM, NNN, OOO]
      [PPP, QQQ, RRR, SSS, TTT, UUU, VVV, WWW, XXX, YYY, ZZZ]
      ######################################################
      Partitioning original list of size 26 in to sub lists of max size 20
      Resulting number of partitions 2
      [AAA, BBB, CCC, DDD, EEE, FFF, GGG, HHH, III, JJJ, KKK, LLL, MMM, NNN, OOO, PPP, QQQ, RRR, SSS, TTT]
      [UUU, VVV, WWW, XXX, YYY, ZZZ]
      ######################################################
      Partitioning original list of size 26 in to sub lists of max size 25
      Resulting number of partitions 2
      [AAA, BBB, CCC, DDD, EEE, FFF, GGG, HHH, III, JJJ, KKK, LLL, MMM, NNN, OOO, PPP, QQQ, RRR, SSS, TTT, UUU, VVV, WWW, XXX, YYY]
      [ZZZ]
      ######################################################
      Partitioning original list of size 26 in to sub lists of max size 30
      Resulting number of partitions 1
      [AAA, BBB, CCC, DDD, EEE, FFF, GGG, HHH, III, JJJ, KKK, LLL, MMM, NNN, OOO, PPP, QQQ, RRR, SSS, TTT, UUU, VVV, WWW, XXX, YYY, ZZZ]
      

      【讨论】:

        【解决方案5】:

        一行使用 Java 8:

        IntStream.range(0, list.size() / batchSize + 1)
                .mapToObj(i -> list.subList(i * batchSize,
                        Math.min(i * batchSize + batchSize, list.size())))
                .filter(s -> !s.isEmpty()).collect(Collectors.toList());
        

        【讨论】:

          【解决方案6】:

          这是一种将 List 划分为子列表数组的方法,可确保除最后一个子列表之外的所有子列表都具有相同数量的元素:

          static <T> List<T>[] split(List<T> source, int numPartitions) {
              if (numPartitions < 2)
                  return new List[]{source};
          
              final int sourceSize = source.size(),
                  partitions = numPartitions > sourceSize ? sourceSize: numPartitions,
                  increments = sourceSize / partitions;
          
              return IntStream.rangeClosed(0, partitions)
                  .mapToObj(i -> source.subList(i*increments, Math.min((i+1)*increments, sourceSize)))
                  .toArray(List[]::new);
          }
          

          如果你想保证numPartitions 数组大小那么你想要:

          static <T> List<T>[] split(List<T> source, int numPartitions) {
              if (numPartitions < 2)
                  return new List[]{source};
          
              final int sourceSize = source.size(),
                  partitions = numPartitions > sourceSize ? sourceSize: numPartitions,
                  increments = sourceSize / partitions;
          
              return IntStream.range(0, partitions)
                  .mapToObj(i -> source.subList(i*increments, i == partitions-1 ? sourceSize : (i+1)*increments))
                  .toArray(List[]::new);
          }
          

          【讨论】:

          • If you want to guarantee numPartitions array size 这是什么意思?
          【解决方案7】:

          如果您不想使用库,这是我的解决方案

          1.分成N等份:

          private <T> List<List<T>> nPartition(List<T> objs, final int N) {
              return new ArrayList<>(IntStream.range(0, objs.size()).boxed().collect(
                      Collectors.groupingBy(e->e%N,Collectors.mapping(e->objs.get(e), Collectors.toList())
                              )).values());
          }
          

          2。以 N 项为一组进行分区:

          private <T> List<List<T>> nPartition(List<T> objs, final int N) {
              return new ArrayList<>(IntStream.range(0, objs.size()).boxed().collect(
                      Collectors.groupingBy(e->e/N,Collectors.mapping(e->objs.get(e), Collectors.toList())
                              )).values());
              }
          

          这里的行动: https://ideone.com/QiQnbE

          【讨论】:

            【解决方案8】:

            您需要做一些利用List.subList(int, int) 视图的事情,而不是复制每个子列表。要真正轻松地做到这一点,请使用GuavaLists.partition(List, int) 方法:

            List<Foo> foos = ...
            for (List<Foo> partition : Lists.partition(foos, n)) {
              // do something with partition
            }
            

            请注意,与许多事情一样,这对于不是RandomAccess(例如LinkedList)的List 不是很有效。

            【讨论】:

            • 如果你有Iterable而不是List,还有Iterables.partition(Iterable, int)
            • 最后一个链接变了,我想是这个google.github.io/guava/releases/19.0/api/docs/com/google/common/…, int)
            • 我有一个关于lists.partition 的快速问题,如果输入列表大小小于子列表的预期大小会发生什么情况。此方法是否处理或客户需要验证?
            • @user506591:更新了 Javadoc 链接。分区中的最后一个列表可以小于分区大小。因此,在整个原始列表较小的情况下,分区将包含一个包含所有元素的列表。
            【解决方案9】:

            例如:

                int partitionSize = 10;
                List<List<String>> partitions = new ArrayList<>();
            
                for (int i=0; i<yourlist.size(); i += partitionSize) {
                    partitions.add(yourlist.subList(i, Math.min(i + partitionSize, yourlist.size())));
                }
            
                for (List<String> list : partitions) {
                    //Do your stuff on each sub list
                }
            

            【讨论】:

            • 绝对是最佳答案
            • 感谢您提供最佳解决方案。
            【解决方案10】:

            我刚刚实现了一个列表分区,因为我不能使用库。

            所以我想在这里分享我的代码:

            import java.util.Iterator;
            import java.util.List;
            import java.util.NoSuchElementException;
            
            public class ListPartitioning<T> implements Iterable<List<T>> {
            
              private final List<T> list;
              private final int partitionSize;
            
              public ListPartitioning(List<T> list, int partitionSize) {
                if (list == null) {
                  throw new IllegalArgumentException("list must not be null");
                }
                if (partitionSize < 1) {
                  throw new IllegalArgumentException("partitionSize must be 1 or greater");
                }
                this.list = list;
                this.partitionSize = partitionSize;
              }
            
              @Override
              public Iterator<List<T>> iterator() {
                return new ListPartitionIterator<T>(list, partitionSize);
              }
            
              private static class ListPartitionIterator<T> implements Iterator<List<T>> {
            
                private int index = 0;
            
                private List<T> listToPartition;
                private int partitionSize;
                private List<T> nextPartition;
            
                public ListPartitionIterator(List<T> listToPartition, int partitionSize) {
                  this.listToPartition = listToPartition;
                  this.partitionSize = partitionSize;
                }
            
                @Override
                public boolean hasNext() {
                  return index < listToPartition.size();
                }
            
                @Override
                public List<T> next() {
                  if (!hasNext()) {
                    throw new NoSuchElementException();
                  }
            
                  int partitionStart = index;
                  int partitionEnd = Math.min(index + partitionSize, listToPartition.size());
            
                  nextPartition = listToPartition.subList(partitionStart, partitionEnd);
                  index = partitionEnd;
                  return nextPartition;
                }
            
                @Override
                public void remove() {
                  if (nextPartition == null) {
                    throw new IllegalStateException("next must be called first");
                  }
            
                  nextPartition.clear();
                  index -= partitionSize;
                  nextPartition = null;
                }
              }
            }
            

            以及基于testng的单元测试。

            import org.testng.Assert;
            import org.testng.annotations.Test;
            
            import java.util.*;
            
            
            public class ListPartitioningTest {
            
              @Test(expectedExceptions = IllegalArgumentException.class)
              public void nullList() {
                ListPartitioning<String> lists = new ListPartitioning<String>(null, 1);
              }
            
              @Test(groups = Group.UNIT_TEST, expectedExceptions = IllegalArgumentException.class)
              public void wrongPartitionSize() {
                ListPartitioning<String> lists = new ListPartitioning<String>(new ArrayList<String>(), 0);
              }
            
            
              @Test()
              public void iteratorTest() {
                List<Integer> integers = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
                ListPartitioning<Integer> listPartitioning = new ListPartitioning<Integer>(integers, 7);
                Iterator<List<Integer>> partitionIterator = listPartitioning.iterator();
                Assert.assertNotNull(partitionIterator);
            
                Assert.assertTrue(partitionIterator.hasNext(), "next partition (first)");
                List<Integer> partition = partitionIterator.next();
                Assert.assertEquals(partition, Arrays.asList(0, 1, 2, 3, 4, 5, 6));
            
                Assert.assertTrue(partitionIterator.hasNext(), "next partition (second)");
                partition = partitionIterator.next();
                Assert.assertEquals(partition, Arrays.asList(7, 8, 9, 10, 11, 12, 13));
            
                Assert.assertTrue(partitionIterator.hasNext(), "next partition (third)");
                partition = partitionIterator.next();
                Assert.assertEquals(partition, Arrays.asList(14, 15));
            
                Assert.assertFalse(partitionIterator.hasNext());
              }
            
              @Test(expectedExceptions = NoSuchElementException.class)
              public void noSuchElementException() {
                List<Integer> integers = Arrays.asList(1);
                ListPartitioning<Integer> listPartitioning = new ListPartitioning<Integer>(integers, 2);
                Iterator<List<Integer>> partitionIterator = listPartitioning.iterator();
                List<Integer> partition = partitionIterator.next();
                partition = partitionIterator.next();
              }
            
              @Test(expectedExceptions = IllegalStateException.class)
              public void removeWithoutNext() {
                List<Integer> integers = new ArrayList<Integer>(Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15));
                ListPartitioning<Integer> listPartitioning = new ListPartitioning<Integer>(integers, 7);
                Iterator<List<Integer>> partitionIterator = listPartitioning.iterator();
                partitionIterator.remove();
              }
            
              @Test()
              public void remove() {
                List<Integer> integers = new ArrayList<Integer>(Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15));
                ListPartitioning<Integer> listPartitioning = new ListPartitioning<Integer>(integers, 7);
                Iterator<List<Integer>> partitionIterator = listPartitioning.iterator();
            
                partitionIterator.next();
                partitionIterator.next();
            
                partitionIterator.remove();
                Assert.assertTrue(partitionIterator.hasNext(), "next partition ");
                List<Integer> partition = partitionIterator.next();
                Assert.assertEquals(partition, Arrays.asList(14, 15));
            
                Assert.assertFalse(partitionIterator.hasNext());
            
                Assert.assertEquals(integers, Arrays.asList(0, 1, 2, 3, 4, 5, 6, 14, 15));
              }
            }
            

            【讨论】:

              【解决方案11】:
              public <E> Iterable<List<E>> partition(List<E> list, final int batchSize)
              {
                  assert(batchSize > 0);
                  assert(list != null);
                  assert(list.size() + batchSize <= Integer.MAX_VALUE); //avoid overflow
              
                  int idx = 0;
              
                  List<List<E>> result = new ArrayList<List<E>>();
              
                  for (idx = 0; idx + batchSize <= list.size(); idx += batchSize) {
                      result.add(list.subList(idx, idx + batchSize));
                  }
                  if (idx < list.size()) {
                      result.add(list.subList(idx, list.size()));
                  }
              
                  return result;
              }
              

              【讨论】:

                【解决方案12】:

                如果您使用列表,我使用“Apache Commons Collections 4”库。它在 ListUtils 类中有一个分区方法:

                ...
                int targetSize = 100;
                List<Integer> largeList = ...
                List<List<Integer>> output = ListUtils.partition(largeList, targetSize);
                

                此方法改编自http://code.google.com/p/guava-libraries/

                【讨论】:

                  【解决方案13】:

                  怎么样

                  Arrays.copyOfRange( original, from, to )
                  

                  ?

                  【讨论】:

                  • to之后的项目呢?
                  【解决方案14】:

                  如果你正在处理数组,你可以使用System.arraycopy()

                   int[] a = {1,2,3,4,5};
                  
                   int[] b = new int[2];
                   int[] c = new int[3];
                  
                   System.arraycopy(a, 0, b, 0, 2); // b will be {1,2}
                   System.arraycopy(a, 2, c, 0, 3); // c will be {3,4,5}
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2012-07-12
                    • 1970-01-01
                    • 2012-07-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多