在Java中对一个List进行分割

评论 0 浏览 0 2013-12-20

1.概述

在这篇文章中,我们将说明如何将一个List分割成若干个给定大小的子List

对于一个相对简单的操作,在标准的Java集合API中竟然没有支持。幸运的是,GuavaApache Commons Collections都以一种类似的方式实现了这个操作。

这篇文章是the “Java –Back to Basic”系列的一部分

进一步的阅读。

在Java中把列表转换为字符串

Java中的置换集合

Java中的Spliterator简介

2.使用Guava对列表进行分区

Guava通过Lists.partition操作将List划分为指定大小的子列表。

@Test
public void givenList_whenParitioningIntoNSublists_thenCorrect() {
    List<Integer> intList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);
    List<List<Integer>> subSets = Lists.partition(intList, 3);

    List<Integer> lastPartition = subSets.get(2);
    List<Integer> expectedLastPartition = Lists.<Integer> newArrayList(7, 8);
    assertThat(subSets.size(), equalTo(3));
    assertThat(lastPartition, equalTo(expectedLastPartition));
}

3.使用Guava来划分一个集合

划分一个集合也可以用Guava来实现。

@Test
public void givenCollection_whenParitioningIntoNSublists_thenCorrect() {
    Collection<Integer> intCollection = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);

    Iterable<List<Integer>> subSets = Iterables.partition(intCollection, 3);

    List<Integer> firstPartition = subSets.iterator().next();
    List<Integer> expectedLastPartition = Lists.<Integer> newArrayList(1, 2, 3);
    assertThat(firstPartition, equalTo(expectedLastPartition));
}

请记住,分区是原始集合的子列表视图,这意味着原始集合的变化将反映在分区中。

@Test
public void givenListPartitioned_whenOriginalListIsModified_thenPartitionsChangeAsWell() {
    // Given
    List<Integer> intList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);
    List<List<Integer>> subSets = Lists.partition(intList, 3);

    // When
    intList.add(9);

    // Then
    List<Integer> lastPartition = subSets.get(2);
    List<Integer> expectedLastPartition = Lists.<Integer> newArrayList(7, 8, 9);
    assertThat(lastPartition, equalTo(expectedLastPartition));
}

4.使用Apache Commons集合来划分列表

最新发布的Apache Commons Collections也最近增加了对List的分区的支持。

@Test
public void givenList_whenParitioningIntoNSublists_thenCorrect() {
    List<Integer> intList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);
    List<List<Integer>> subSets = ListUtils.partition(intList, 3);

    List<Integer> lastPartition = subSets.get(2);
    List<Integer> expectedLastPartition = Lists.<Integer> newArrayList(7, 8);
    assertThat(subSets.size(), equalTo(3));
    assertThat(lastPartition, equalTo(expectedLastPartition));
}

Commons Collections没有一个类似于Guava Iterables.partition的对应选项来分割一个原始的Collection

最后,同样的注意事项也适用于此:所产生的分区是原始List的视图。

5.使用Java8对列表进行分区

现在让我们来看看如何使用Java8来划分我们的List。

5.1.收集器 partitioningBy

我们可以使用Collectors.partitioningBy()将列表分割成2个子列表。

@Test
public void givenList_whenParitioningIntoSublistsUsingPartitionBy_thenCorrect() {
    List<Integer> intList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);

    Map<Boolean, List<Integer>> groups = 
      intList.stream().collect(Collectors.partitioningBy(s -> s > 6));
    List<List<Integer>> subSets = new ArrayList<List<Integer>>(groups.values());

    List<Integer> lastPartition = subSets.get(1);
    List<Integer> expectedLastPartition = Lists.<Integer> newArrayList(7, 8);
    assertThat(subSets.size(), equalTo(2));
    assertThat(lastPartition, equalTo(expectedLastPartition));
}

注意:产生的分区不是主列表的一个视图,所以主列表发生的任何变化都不会影响分区。

5.2. Collectors groupingBy

我们还可以使用 Collectors.groupingBy()来将我们的列表分成多个分区。

@Test
public final void givenList_whenParitioningIntoNSublistsUsingGroupingBy_thenCorrect() {
    List<Integer> intList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);

    Map<Integer, List<Integer>> groups = 
      intList.stream().collect(Collectors.groupingBy(s -> (s - 1) / 3));
    List<List<Integer>> subSets = new ArrayList<List<Integer>>(groups.values());

    List<Integer> lastPartition = subSets.get(2);
    List<Integer> expectedLastPartition = Lists.<Integer> newArrayList(7, 8);
    assertThat(subSets.size(), equalTo(3));
    assertThat(lastPartition, equalTo(expectedLastPartition));
}

注意:就像使用Collectors.partitioningBy()一样,产生的分区不会受到主列表中的变化的影响。

5.3.按分隔符拆分列表

我们也可以用Java8来分割我们的List,用分离器来分割。

@Test
public void givenList_whenSplittingBySeparator_thenCorrect() {
    List<Integer> intList = Lists.newArrayList(1, 2, 3, 0, 4, 5, 6, 0, 7, 8);

    int[] indexes = 
      Stream.of(IntStream.of(-1), IntStream.range(0, intList.size())
      .filter(i -> intList.get(i) == 0), IntStream.of(intList.size()))
      .flatMapToInt(s -> s).toArray();
    List<List<Integer>> subSets = 
      IntStream.range(0, indexes.length - 1)
               .mapToObj(i -> intList.subList(indexes[i] + 1, indexes[i + 1]))
               .collect(Collectors.toList());

    List<Integer> lastPartition = subSets.get(2);
    List<Integer> expectedLastPartition = Lists.<Integer> newArrayList(7, 8);
    assertThat(subSets.size(), equalTo(3));
    assertThat(lastPartition, equalTo(expectedLastPartition));
}

注意:我们用“0”作为分隔符。我们首先获得List中所有“0”元素的索引,然后根据这些索引分割List

6.结语

此处介绍的解决方案使用了额外的库,即 Guava 和 Apache Commons Collections。这两者都非常轻量级并且总体上非常有用,因此将其中之一放在类路径中是非常有意义的。但是,如果这不是一个选项,此处显示的是仅 Java 解决方案

所有这些例子和代码片断的实现可以在GitHub上找到。这是一个基于Maven的项目,所以应该很容易导入和运行。

最后更新2022-12-15
0 个评论
标签