Java:实现集合分组

1.概述

在本教程中,我将说明如何将List拆分为给定大小的多个子列表

对于相对简单的操作,令人惊讶的是在标准Java集合API中没有支持。幸运的是,GuavaApache Commons Collections都以类似的方式实现了操作

 

2.使用Guava对List进行分区

Guava 便于将列表分成指定大小的子列表-经由Lists.partition操作

@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对Collection进行分区

Guava也可以对Collection进行分区

@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 Collections对List进行分区

最新版本的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));
}

没有相应的选项来划分一个原始收藏 -类似于Guava Iterables.partition在通用集合。

最后,同样的警告也适用于此 - 结果分区是原始List的视图

5.使用Java8对List进行分区

现在,让我们看看如何使用Java8来分区列表。

5.1. Collectors 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));
}

注意:生成的分区不是主List的视图,因此主List发生的任何更改都不会影响分区。

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()一样 - 生成的分区不会受到主List中的更改的影响。

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

六,结论

这里介绍的解决方案使用了额外的库--Guava或Apache Commons Collections库。这两个都非常轻量级,总体上非常有用,所以在类路径中使用其中一个非常有意义; 但是,如果这不是一个选项 - 这里显示一个Java解决方案。

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

 

猜你喜欢

转载自blog.csdn.net/fly910905/article/details/89305512