Javaでリストをパーティション分割する
1. 概要
このチュートリアルでは、特定のサイズのhow to split a List into several sublistsを説明します。
比較的単純な操作の場合、驚くべきことに、標準のJavaコレクションAPIにはサポートがありません。 幸い、GuavaとApache Commons Collectionsの両方が同様の方法で操作を実装しています。
この記事は、例としてここのthe “Java – Back to Basic” seriesの一部です。
参考文献:
2. Guavaを使用してリストを分割する
Guavaは、the Lists.partition operationを介して、リストを指定されたサイズのサブリストに分割することを容易にします。
@Test
public void givenList_whenParitioningIntoNSublists_thenCorrect() {
List intList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);
List> subSets = Lists.partition(intList, 3);
List lastPartition = subSets.get(2);
List expectedLastPartition = Lists. newArrayList(7, 8);
assertThat(subSets.size(), equalTo(3));
assertThat(lastPartition, equalTo(expectedLastPartition));
}
3. Guavaを使用してコレクションを分割します
Partitioning a CollectionはGuavaでも可能です:
@Test
public void givenCollection_whenParitioningIntoNSublists_thenCorrect() {
Collection intCollection = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);
Iterable> subSets = Iterables.partition(intCollection, 3);
List firstPartition = subSets.iterator().next();
List expectedLastPartition = Lists. newArrayList(1, 2, 3);
assertThat(firstPartition, equalTo(expectedLastPartition));
}
パーティションはsublist views of the original collectionであることに注意してください。これは、元のコレクションの変更がパーティションに反映されることを意味します。
@Test
public void givenListPartitioned_whenOriginalListIsModified_thenPartitionsChangeAsWell() {
// Given
List intList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);
List> subSets = Lists.partition(intList, 3);
// When
intList.add(9);
// Then
List lastPartition = subSets.get(2);
List expectedLastPartition = Lists. newArrayList(7, 8, 9);
assertThat(lastPartition, equalTo(expectedLastPartition));
}
4. ApacheCommonsCollectionsを使用してリストを分割する
Apache Commons Collectionsの最新リリースでは、リストのパーティション化もrecently addedでサポートされています。
@Test
public void givenList_whenParitioningIntoNSublists_thenCorrect() {
List intList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);
List> subSets = ListUtils.partition(intList, 3);
List lastPartition = subSets.get(2);
List expectedLastPartition = Lists. newArrayList(7, 8);
assertThat(subSets.size(), equalTo(3));
assertThat(lastPartition, equalTo(expectedLastPartition));
}
CommonsCollectionsのGuavaIterables.partitionに似たno corresponding option to partition a raw Collectionがあります。
最後に、ここでも同じ警告が適用されます。結果のパーティションは元のリストのビューです。
5. Java8を使用してリストを分割する
それでは、Java8を使用してリストを分割する方法を見てみましょう。
5.1. コレクターpartitioningBy
次のように、Collectors.partitioningBy()を使用してリストを2つのサブリストに分割できます。
@Test
public void givenList_whenParitioningIntoSublistsUsingPartitionBy_thenCorrect() {
List intList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);
Map> groups =
intList.stream().collect(Collectors.partitioningBy(s -> s > 6));
List> subSets = new ArrayList>(groups.values());
List lastPartition = subSets.get(1);
List expectedLastPartition = Lists. newArrayList(7, 8);
assertThat(subSets.size(), equalTo(2));
assertThat(lastPartition, equalTo(expectedLastPartition));
}
注:結果のパーティションはメインリストのビューではないため、メインリストに変更が加えられてもパーティションには影響しません。
5.2. コレクターgroupingBy
Collectors.groupingBy()を使用して、リストを複数のパーティションに分割することもできます。
@Test
public final void givenList_whenParitioningIntoNSublistsUsingGroupingBy_thenCorrect() {
List intList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);
Map> groups =
intList.stream().collect(Collectors.groupingBy(s -> (s - 1) / 3));
List> subSets = new ArrayList>(groups.values());
List lastPartition = subSets.get(2);
List expectedLastPartition = Lists. newArrayList(7, 8);
assertThat(subSets.size(), equalTo(3));
assertThat(lastPartition, equalTo(expectedLastPartition));
}
注:Collectors.partitioningBy()と同様に、結果のパーティションはメインリストの変更による影響を受けません。
5.3. 区切り文字でリストを分割する
Java8を使用して、リストを区切り記号で分割することもできます。
@Test
public void givenList_whenSplittingBySeparator_thenCorrect() {
List 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> subSets =
IntStream.range(0, indexes.length - 1)
.mapToObj(i -> intList.subList(indexes[i] + 1, indexes[i + 1]))
.collect(Collectors.toList());
List lastPartition = subSets.get(2);
List expectedLastPartition = Lists. newArrayList(7, 8);
assertThat(subSets.size(), equalTo(3));
assertThat(lastPartition, equalTo(expectedLastPartition));
}
注:区切り文字として「0」を使用しました。最初にリスト内のすべての「0」要素のインデックスを取得し、次にこれらのインデックスのListを分割しました。
6. 結論
ここで紹介するソリューションは、追加のライブラリ– GuavaまたはApache Commons Collectionsライブラリを使用します。 これらは両方とも非常に軽量で全体的に非常に便利なので、クラスパスにそれらの1つを置くことは完全に理にかなっています。ただし、それがオプションでない場合– Javaのみのソリューションis shown here。
これらすべての例とコードスニペットcan be found over on GitHubの実装–これはMavenベースのプロジェクトであるため、そのままインポートして実行するのは簡単です。