Java17(LTS)でリストを指定された件数ごとに分割する

2025年4月6日(日)

環境

全体図

/
    +-home
    |     +-mizuki
    |           +-workspace
    |                 +-java17 (ローカルリポジトリのルート)
    |                       +-.git
    |                       |
    |                       +-java17-split-list (プロジェクト)
    |                             +-.mvn
    |                             |      |-jvm.config
    |                             |      |-maven.config
    |                             |
    |                             +-src
    |                             |     +-main
    |                             |     |     +-java
    |                             |     |           +-amnesia
    |                             |     |                 +-app
    |                             |     |                 |     |-App.java (メインクラス)
    |                             |     |                 |
    |                             |     |                 +-util
    |                             |     |                       |-Util.java
    |                             |     |
    |                             |     +-test
    |                             |           +-java
    |                             |                 +-amnesia
    |                             |                       +-app
    |                             |                       |     |-AppTest.java
    |                             |                       |
    |                             |                       +-util
    |                             |                             |-UtilTest.java
    |                             |
    |                             +-target
    |                             |     |-java17-split-list-1.0-SNAPSHOT.jar
    |                             |
    |                             |-pom.xml
    |                             |-.gitignore
    |
    +-opt
          +-apache-maven-3.9.9
          |     +-bin
          |           |-mvn
          |
          +-java
                +-jdk-17.0.14+7
                |     +-bin
                |           |-java
                |
                +-jdk-21.0.6+7
                      +-bin
                            |-java
          

GitHubのリポジトリ

https://github.com/yvafdevnsk/java17

1. Mavenプロジェクトを作成する

環境変数JAVA_HOMEにJava17を指定する。

$ export JAVA_HOME=/opt/java/jdk-17.0.14+7
        

Mavenプロジェクトを作成する。プロジェクト名はartifactIdで指定する。

$ cd /home/mizuki/workspace/java17
$ /opt/apache-maven-3.9.9/bin/mvn archetype:generate -DgroupId=amnesia.app -DartifactId=java17-split-list -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.5 -DinteractiveMode=false
        

実行結果

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------< org.apache.maven:standalone-pom >-------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] >>> archetype:3.3.1:generate (default-cli) > generate-sources @ standalone-pom >>>
[INFO]
[INFO] <<< archetype:3.3.1:generate (default-cli) < generate-sources @ standalone-pom <<<
[INFO]
[INFO]
[INFO] --- archetype:3.3.1:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Batch mode
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: maven-archetype-quickstart:1.5
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: amnesia.app
[INFO] Parameter: artifactId, Value: java17-split-list
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: amnesia.app
[INFO] Parameter: packageInPathFormat, Value: amnesia/app
[INFO] Parameter: junitVersion, Value: 5.11.0
[INFO] Parameter: package, Value: amnesia.app
[INFO] Parameter: groupId, Value: amnesia.app
[INFO] Parameter: artifactId, Value: java17-split-list
[INFO] Parameter: javaCompilerVersion, Value: 17
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[WARNING] Don't override file /home/mizuki/workspace/java17/java17-split-list/src/main/java/amnesia/app
[WARNING] Don't override file /home/mizuki/workspace/java17/java17-split-list/src/test/java/amnesia/app
[WARNING] CP Don't override file /home/mizuki/workspace/java17/java17-split-list/.mvn
[INFO] Project created from Archetype in dir: /home/mizuki/workspace/java17/java17-split-list
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  0.837 s
[INFO] Finished at: 2025-04-04T22:39:37+09:00
[INFO] ------------------------------------------------------------------------
        

参考情報

2. Visual Studio CodeでMavenプロジェクトを開く

WSL2上のUbuntu側でMavenプロジェクトのディレクトリからVisual Studio Codeを起動する。

$ cd /home/mizuki/workspace/java17/java17-split-list
$ code .
        

MavenプロジェクトをビルドしてJARファイルを作成する。

$ cd /home/mizuki/workspace/java17/java17-split-list
$ /opt/apache-maven-3.9.9/bin/mvn clean package
        

実行結果。

[INFO] Building jar: /home/mizuki/workspace/java17/java17-split-list/target/java17-split-list-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  5.087 s
[INFO] Finished at: 2025-05-08T21:56:37+09:00
[INFO] ------------------------------------------------------------------------
        

作成したJARファイル内のメインクラスを実行する。

$ cd /home/mizuki/workspace/java17/java17-split-list/target
$ $JAVA_HOME/bin/java -cp java17-split-list-1.0-SNAPSHOT.jar amnesia.app.App
        

実行結果。

Hello World!
        

3. リストを指定された件数ごとに分割する機能を実装する

amnesia.utilパッケージを追加する。

Visual Studio Code >> EXPLORER
    src/main/java/amnesia
        右クリック
            New Java Package...
          

amnesia.util.Utilクラスを追加する。

Visual Studio Code >> EXPLORER
    src/main/java/amnesia/util
        右クリック
            New Java File...
                Class...
          

amnesia.util.UtilクラスにsplitListメソッドを追加する。

package amnesia.util;

import java.util.ArrayList;
import java.util.List;

public class Util {
    /**
      * リストを指定された件数ごとに分割する。
      * 
      * srcListが10,500件のリストの場合、
      * countに2,000を指定すると2,000件のリストが5個、500件のリストが1個の合計6個のリストに分割される。
      * 
      * srcList[10500]
      * splitedList(0)=subList[2000]
      * splitedList(1)=subList[2000]
      * splitedList(2)=subList[2000]
      * splitedList(3)=subList[2000]
      * splitedList(4)=subList[2000]
      * splitedList(5)=subList[500]
      * 
      * @param srcList 分割するリスト
      * @param count 何件ずつ分割するか
      * @return 指定された件数ごとに分割されたリスト
      */
    public static <T> List<List<T>> splitList(List<T> srcList, int count) {
        if ((srcList == null) || srcList.isEmpty() || (count <= 0)) {
            return new ArrayList<>();
        }
        List<List<T>> splitedList = new ArrayList<>();

        int itemTotalIndex = 0;
        List<T> subList = new ArrayList<>();

        while (itemTotalIndex < srcList.size()) {
            subList.add(srcList.get(itemTotalIndex++));

            // 分割単位を満たしたsubListをここで回収する。
            if (count <= subList.size()) {
                splitedList.add(subList);
                subList = new ArrayList<>();
            }
        }

        // 分割単位を満たさない端数のsubListをここで回収する。
        if (!subList.isEmpty()) {
            splitedList.add(subList);
        }

        return splitedList;
    }
}
        

4. リストを指定された件数ごとに分割する機能のテストを実装する

テストにamnesia.utilパッケージを追加する。

Visual Studio Code >> EXPLORER
    src/test/java/amnesia
        右クリック
            New Java Package...
          

テストにamnesia.util.UtilTestクラスを追加する。

Visual Studio Code >> EXPLORER
    src/test/java/amnesia/util
        右クリック
            New Java File...
                Class...
          

amnesia.util.UtilTestクラスにテストケースを追加する。

package amnesia.util;

import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.ArrayList;
import java.util.List;

import org.junit.jupiter.api.Test;

public class UtilTest {
    /**
      * srcList=null
      */
    @Test
    void test1() {
        List<List<String>> resultList = Util.splitList(null, 1);
        assertTrue(resultList.isEmpty());
    }

    /**
      * srcList=empty
      */
    @Test
    void test2() {
        List<List<String>> resultList = Util.splitList(new ArrayList<String>(), 1);
        assertTrue(resultList.isEmpty());
    }

    /**
      * count<=0
      */
    @Test
    void test3() {
        List<String> srcList = new ArrayList<>();
        srcList.add("1");

        List<List<String>> resultList = Util.splitList(srcList, 0);
        assertTrue(resultList.isEmpty());
        resultList = Util.splitList(srcList, -1);
        assertTrue(resultList.isEmpty());
    }

    /**
      * srcList.size()=count
      */
    @Test
    void test4() {
        for (int count = 1; count <= 10; count++) {
            List<Integer> srcList = new ArrayList<>();
            for (int k = 0; k < count; k++) {
                srcList.add(k);
            }

            List<List<Integer>> resultList = Util.splitList(srcList, count);
            assertTrue(resultList.size() == 1);
        }
    }

    /**
      * 0 < count <= srcList.size
      * srcList.size < count
      */
    @Test
    void test5() {
        List<Integer> srcList = new ArrayList<>();
        for (int k = 0; k < 100; k++) {
            srcList.add(k);
        }

        for (int count = 1; count <= 200; count++) {
            List<List<Integer>> resultList = Util.splitList(srcList, count);
            System.out.println("srcList.size[" + srcList.size() + "]count[" + count + "]resultList.size[" + resultList.size() + "]");
            
            int subListCount = srcList.size() / count;
            int mod = srcList.size() % count;
            if (mod == 0) {
                assertTrue(resultList.size() == subListCount);
            }
            else {
                assertTrue(resultList.size() == (subListCount + 1));
            }
        }
    }
}
        

5. メインクラスにリストを指定された件数ごとに分割する機能の呼び出しを実装する

src/main/java/amnesia/app/App.java

package amnesia.app;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import amnesia.util.Util;

/**
  * Hello world!
  */
public class App {
    public static void main(String[] args) {
        System.out.println("Hello World!");

        List<String> srcList = new ArrayList<>(Arrays.asList(new String[10500]));
        int count = 2000;
        List<List<String>> splitList = Util.splitList(srcList, count);
        for (int i = 0; i < splitList.size(); i++) {
            System.out.println(String.format("分割リスト[%d]=[%d]件", i, splitList.get(i).size()));
        }
    }
}
        

6. メインクラスを実行して結果を確認する

MavenプロジェクトをビルドしてJARファイルを作成する。テストケースも実行される。

$ cd /home/mizuki/workspace/java17/java17-split-list
$ /opt/apache-maven-3.9.9/bin/mvn clean package
        

実行結果。

[INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.018 s -- in amnesia.util.UtilTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 6, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] --- jar:3.4.2:jar (default-jar) @ java17-split-list ---
[INFO] Building jar: /home/mizuki/workspace/java17/java17-split-list/target/java17-split-list-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.581 s
[INFO] Finished at: 2025-05-24T21:37:36+09:00
[INFO] ------------------------------------------------------------------------
        

作成したJARファイル内のメインクラスを実行する。

$ cd /home/mizuki/workspace/java17/java17-split-list/target
$ $JAVA_HOME/bin/java -cp java17-split-list-1.0-SNAPSHOT.jar amnesia.app.App
        

実行結果。

Hello World!
分割リスト[0]=[2000]件
分割リスト[1]=[2000]件
分割リスト[2]=[2000]件
分割リスト[3]=[2000]件
分割リスト[4]=[2000]件
分割リスト[5]=[500]件
        

7. プロジェクトをGitHubにアップロードする

始めにGitHub側のリモートリポジトリの構造を確認する。

github.com
    +-user_name
          +-java17 (リポジトリのルート)
                +-Java17Log4j (プロジェクト)
                +-java17-stringbuilder-substring (プロジェクト)
                +-java17-write-text-file (プロジェクト)
          

次に同じ構造になるようにローカル側のリポジトリを作成する。リポジトリのルートにあたる場所で作業する。

/home/mizuki/workspace/java17 (リポジトリのルート)
    +-java17-split-list (プロジェクト)
          
$ cd /home/mizuki/workspace/java17
$ git init
        
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint:
hint:   git config --global init.defaultBranch <name>
hint:
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint:
hint:   git branch -m <name>
Initialized empty Git repository in /home/mizuki/workspace/java17/.git/
        

GitHub側に合わせてブランチ名を"master"から"main"に変更する。

$ cd /home/mizuki/workspace/java17
$ git branch -m main
        

GitHubのリポジトリにアップロードしないファイルを指定する設定ファイルを追加する。プロジェクトのディレクトリの直下に.gitignoreファイルを追加する。

/home/mizuki/workspace/java17/java17-split-list/.gitignore
          
# Mavenの出力ディレクトリを除外する。
/target/
          

gitの設定にユーザー名とメールアドレスを設定する。"--global"オプションはUbuntuのユーザー単位の設定になる。

$ cd /home/mizuki/workspace/java17
$ git config --global user.name "Fukumura Mizuki"
$ git config --global user.email mizuki@home
        

gitの設定を確認する。

$ cd /home/mizuki/workspace/java17
$ git config --list
        
user.name=Fukumura Mizuki
user.email=mizuki@home
        

プロジェクトのファイルをgitで追跡する。Mavenの出力ディレクトリ(target)は.gitignoreファイルの設定により除外されている。

$ cd /home/mizuki/workspace/java17
$ git add java17-split-list
        

プロジェクトのファイルの状態を確認する。

$ cd /home/mizuki/workspace/java17
$ git status
        
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   java17-split-list/.gitignore
        new file:   java17-split-list/.mvn/jvm.config
        new file:   java17-split-list/.mvn/maven.config
        new file:   java17-split-list/pom.xml
        new file:   java17-split-list/src/main/java/amnesia/app/App.java
        new file:   java17-split-list/src/main/java/amnesia/util/Util.java
        new file:   java17-split-list/src/test/java/amnesia/app/AppTest.java
        new file:   java17-split-list/src/test/java/amnesia/util/UtilTest.java
        

プロジェクトのファイルをコミットする。

$ cd /home/mizuki/workspace/java17
$ git commit
        

GNU nanoが起動する。1行目にコミットメッセージ『initial commit』を入力する。"CTRL+O"で保存を指示して"Enter"でデフォルトのファイルにコミットメッセージを保存する。"CTRL+X"でGNU nanoを終了するとファイルがコミットされる。

[main (root-commit) 3bafc65] initial commit
 8 files changed, 275 insertions(+)
 create mode 100644 java17-split-list/.gitignore
 create mode 100644 java17-split-list/.mvn/jvm.config
 create mode 100644 java17-split-list/.mvn/maven.config
 create mode 100644 java17-split-list/pom.xml
 create mode 100644 java17-split-list/src/main/java/amnesia/app/App.java
 create mode 100644 java17-split-list/src/main/java/amnesia/util/Util.java
 create mode 100644 java17-split-list/src/test/java/amnesia/app/AppTest.java
 create mode 100644 java17-split-list/src/test/java/amnesia/util/UtilTest.java
        

プロジェクトのファイルの状態を確認する。

$ cd /home/mizuki/workspace/java17
$ git status
        
On branch main
nothing to commit, working tree clean
        

ローカル側のリポジトリにGitHub側のリモートリポジトリの参照を"github"という名前を付けて追加する。

$ git remote add github https://github.com/yvafdevnsk/java17
$ git remote -v
        
github  https://github.com/yvafdevnsk/java17 (fetch)
github  https://github.com/yvafdevnsk/java17 (push)
        

GitHub側でpersonal access tokenを発行する。リポジトリに対する権限は"Contents"の"Read-and-write"にする。"Administrator"ではコミットできない。

Profile
    settings
        Developer settings
            Personal access tokens
                Fine-grained tokens
          
New fine-grained personal access token
    Toke name
        java17-contents-20250525
    Expiration
        30 days

Repository access
    Only select repositories
        yvafdevnsk/java17

Permissions
    Repository permissions
        Contents
            Access: Read-and-write
          

ローカル側のリポジトリの履歴をGitHub側のリモートリポジトリに反映する。

$ cd /home/mizuki/workspace/java17
$ git push github main
Username for 'https://github.com': <user_name>
Password for 'https://yvafdevnsk@github.com': <personal_access_token>
        
To https://github.com/yvafdevnsk/java17
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'https://github.com/yvafdevnsk/java17'
hint: Updates were rejected because the remote contains work that you do not
hint: have locally. This is usually caused by another repository pushing to
hint: the same ref. If you want to integrate the remote changes, use
hint: 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
        

ローカル側のリポジトリにない履歴がGitHub側のリモートリポジトリにあるので、先にリモートリポジトリの履歴をローカルに持ってくる。

$ cd /home/mizuki/workspace/java17
$ git pull github main
        
remote: Enumerating objects: 78, done.
remote: Counting objects: 100% (78/78), done.
remote: Compressing objects: 100% (67/67), done.
remote: Total 78 (delta 16), reused 40 (delta 1), pack-reused 0 (from 0)
Unpacking objects: 100% (78/78), 23.47 KiB | 1.30 MiB/s, done.
From https://github.com/yvafdevnsk/java17
 * branch            main       -> FETCH_HEAD
 * [new branch]      main       -> github/main
hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint:
hint:   git config pull.rebase false  # merge
hint:   git config pull.rebase true   # rebase
hint:   git config pull.ff only       # fast-forward only
hint:
hint: You can replace "git config" with "git config --global" to set a default
hint: preference for all repositories. You can also pass --rebase, --no-rebase,
hint: or --ff-only on the command line to override the configured default per
hint: invocation.
fatal: Need to specify how to reconcile divergent branches.
        

"git pull"コマンドに対して"merge"するか"rebase"するかを指定する必要がある。"rebase"を指定する。

$ cd /home/mizuki/workspace/java17
$ git pull --rebase github main
        
From https://github.com/yvafdevnsk/java17
 * branch            main       -> FETCH_HEAD
Successfully rebased and updated refs/heads/main.
        

もう一度、ローカル側のリポジトリの履歴をGitHub側のリモートリポジトリに反映する。

$ cd /home/mizuki/workspace/java17
$ git push github main
Username for 'https://github.com': <user_name>
Password for 'https://yvafdevnsk@github.com': <personal_access_token>
        
Enumerating objects: 23, done.
Counting objects: 100% (23/23), done.
Delta compression using up to 20 threads
Compressing objects: 100% (13/13), done.
Writing objects: 100% (22/22), 3.70 KiB | 3.70 MiB/s, done.
Total 22 (delta 1), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To https://github.com/yvafdevnsk/java17
   c40ee92..8b254eb  main -> main
        

参考情報。