※当サイトの記事には、広告・プロモーションが含まれます。

Javaのリストで等間隔に要素を追加してみる

nazology.net

⇧ amazing...

Javaのリストで等間隔に要素を追加してみる

標準のAPIで実現できそうなものが見当たらなかったので、実装してみました。

調べた感じ、

stackoverflow.com

⇧ リストの要素を交互にマージするっていう情報はあったんですが、等間隔で要素を追加していくって情報が見当たらなかったので。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class TestAdd {

	public static void main(String[] args) {
		// 変更前のリスト
//     	List<String> ids = Arrays.asList("1", "2", "3");
//		List<String> ids = Arrays.asList("1", "2", "3", "4", "5");
//		List<String> ids = Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8");
		List<String> ids = Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8", "9");
//		List<String> ids = Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22");

		List<String> notNullIds = ids.stream()
				.filter(Objects::nonNull)
				.collect(Collectors.toList());

		// 追加用のリスト
		List<List<String>>nestLists = Arrays.asList(
				Arrays.asList("【01】", "21", "22", "23", "24", "25", "26"),
				Arrays.asList("【02】", "77", "78", "79", "80"),
				Arrays.asList("【03】", "11", "12"),
				Arrays.asList("【04】", "31", "32", "33", "34", "35", "36"),
				Arrays.asList("【05】", "57", "58", "59", "60"),
				Arrays.asList("【06】", "911", "912")
		);

		// 追加開始位置
		int insertPosition = 3;
		// 追加要素数
		int insertElementnum = 3;
		// 追加する間隔
		int distance = 3;
		
		// 固定値を追加
		List<String> afterList = modifyList(insertPosition, insertElementnum, distance, notNullIds, "?");
		System.out.println("■固定値を追加");
		System.out.println("【追加前】:" + notNullIds);
		System.out.println("【追加後】:" + afterList);	
		
		// リストを追加
		List<String> afterList02 = modifyListFromtwoDimentionList(insertPosition, distance, notNullIds, nestLists);
		System.out.println("■リストを追加");
		System.out.println("【追加前】:" + notNullIds);
		System.out.println("【追加後】:" + afterList02);	
	}
	
	/**
	 * リストの任意の箇所に要素を追加する
	 * @param <T> データ型
	 * @param insertPosition 追加開始位置
	 * @param insertElementNum 追加する要素数
	 * @param distance 追加する間隔
	 * @param beforeList 変更前のリスト
	 * @param element 追加する要素
	 * @return 要素追加後のリスト
	 */
	private static <T> List<T> modifyList(int insertPosition, int insertElementNum, int distance, List<T> beforeList,
			T element) {
		// 何回追加したかのカウント数
		int insertCount = 0;
		// 変更前のリストのサイズ
		int beforeListSize = beforeList.size();
		// 追加開始位置が不正の場合
		if (insertPosition > beforeListSize || insertPosition < 0) {
			return beforeList;
		}
		// 追加する間隔が不正の場合
		if (distance < 0) {
			return beforeList;
		}
		// 追加する要素数が不正の場合
		if (insertElementNum <= 0) {
			return beforeList;			
		}
		
		// 変更後のリスト
		List<T> afterList = new ArrayList<>();
		afterList.addAll(beforeList);

		// リストへの追加処理
		for (int index = 0; index < afterList.size(); index++) {	
			// 最後の要素の後に追加できるかどうか
			if (checkAvailableAddLast(index, beforeListSize, distance, afterList.size(), insertPosition)) {
				for (int i = 0; i < insertElementNum; i++) {
					afterList.add(insertPosition, element);
					insertPosition++;
				}
				break;
			}
			
			// 追加できない位置の場合
			if (insertPosition > afterList.size()) {
				break;
			}
			
			// 追加する位置に来たら追加
			if (index == insertPosition) {
				// 追加する間隔が0の場合
				if (distance == 0 && insertCount == 0) {
					for (int i = index; i < beforeListSize; i++) {	
						for (int j = 0; j < insertElementNum; j++) {
							afterList.add(insertPosition, element);
							insertPosition++;
						}
					}
					break;
				}
				for (int i = 0; i < insertElementNum; i++) {
					afterList.add(insertPosition, element);
					insertPosition++;
				}

				insertPosition += distance;
				// 追加したカウント数を加算
				insertCount++;
			}

		}
		return afterList;
	}
	
	/**
	 * リストの任意の個所にリストをマージする
	 * @param <T> データ型
	 * @param insertPosition  追加開始位置
	 * @param distance 追加する間隔
	 * @param beforeList 変更前のリスト
	 * @param sourceList マージ元のリスト
	 * @return 要素追加後のリスト
	 */
	private static <T> List<T> modifyListFromtwoDimentionList(int insertPosition, int distance, List<T> beforeList,
			List<List<T>> sourceList) {
		// 何回追加したかのカウント数
		int insertCount = 0;
		// 変更前のリストのサイズ
		int beforeListSize = beforeList.size();
		// 追加開始位置が不正の場合
		if (insertPosition > beforeListSize || insertPosition < 0) {
			return beforeList;
		}
		// 追加する間隔が不正の場合
		if (distance < 0) {
			return beforeList;
		}
		
		// 変更後のリスト
		List<T> afterList = new ArrayList<>();
		afterList.addAll(beforeList);
		
		int insertElementNum = 0;
		// リストへの追加処理
		for (int index = 0; index < afterList.size(); index++) {	
			// 追加する要素が無くなった場合
			if (insertCount > sourceList.size() -1) {
				break;
			}
			// 最後の要素の後に追加できるかどうか
			if (checkAvailableAddLast(index, beforeListSize, distance, afterList.size(), insertPosition)) {
				insertElementNum = sourceList.get(insertCount).size();
				for (int i = 0; i < insertElementNum; i++) {
					afterList.add(insertPosition, sourceList.get(insertCount).get(i));
					insertPosition++;
				}
				break;
			}
			
			// 追加できない位置の場合
			if (insertPosition > afterList.size()) {
				break;
			}
			
			// 追加する位置に来たら追加
			if (index == insertPosition) {

				insertElementNum = sourceList.get(insertCount).size();
				for (int i = 0; i < insertElementNum; i++) {
					afterList.add(insertPosition, sourceList.get(insertCount).get(i));
					insertPosition++;
				}
				// 次の追加位置を設定
				insertPosition += distance;
				// 追加したカウント数を加算
				insertCount++;
			}
		}
		return afterList;
	}
	
	/**
	 * リストの最後の要素の後に要素が追加できるか判定
	 * @param afterListIndex 要素追加後のリストのインデックス
	 * @param beforeListSize 変更前のリストのサイズ
	 * @param distance 追加する間隔
	 * @param afterListSize 変更後のリストのサイズ
	 * @param insertPosition 要素を追加する位置
	 * @return true:追加可能、false:追加不可
	 */
	private static boolean checkAvailableAddLast(int afterListIndex, int beforeListSize, 
			int distance, int afterListSize, int insertPosition) {

		// 最後の要素の位置
		int lastElementIndex = afterListSize-1;
		// 最後の要素じゃない場合
		if (!(afterListIndex != 0 && afterListIndex == lastElementIndex)) {
		   return false;
		}
		
		// 最後の要素の後に追加できるかどうか
		if (insertPosition <= afterListSize && beforeListSize % distance == 0) {
			return true;
		}
		return false;
	}
}

実行。

■追加開始位置:3、追加する間隔:3

■追加開始位置:2、追加する間隔:3

■追加開始位置:1、追加する間隔:3

■追加開始位置:0、追加する間隔:3

実装してみたものの、まぁ、用途が思いつかんのだけど...

あと、動作確認が網羅できてないので、不具合もありそう...

メソッドの引数も多くなってしまうし、シンプルに実装するのって難しい...

実装した後に気付いたのだけど、Javaの標準APIでリストをサブリストに分割した後にリストを結合し直した方がシンプルっぽい...

fumidzuki.com

⇧ 上記サイト様を参考に、とりあえず、試してみた。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class TestMerge {

	public static void main(String[] args) {
		List<String> ids = Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22");
		List<String> notNullIds = ids.stream()
				.filter(Objects::nonNull)
				.collect(Collectors.toList());
		// 追加用のリスト
		List<List<String>>nestLists = Arrays.asList(
				Arrays.asList("【01_START】", "21", "22", "23", "24", "25", "26", "【01_END】"),
				Arrays.asList("【02_START】", "77", "78", "79", "80", "【02_END】"),
				Arrays.asList("【03_START】", "11", "12", "【03_END】"),
				Arrays.asList("【04_START】", "31", "32", "33", "34", "35", "36", "【04_END】"),
				Arrays.asList("【05_START】", "57", "58", "59", "60", "【05_END】"),
				Arrays.asList("【06_START】", "911", "912", "【06_END】")
		);
		
		List<List<String>> partitionedList = partitionedList(notNullIds, 10);
		List<String> mergedList = new ArrayList<>();
		for (int index = 0; index < partitionedList.size(); index++) {
			if (index < nestLists.size()) {
				mergedList.addAll(partitionedList.get(index));
				mergedList.addAll(nestLists.get(index));
				continue;
			}
			mergedList.addAll(partitionedList.get(index));
		}
		// 結果
		System.out.println("【変更前】:" + notNullIds);	
		System.out.println("【分割後】:" + partitionedList);
		System.out.println("【追加後】:" + mergedList);
	}

	public static <T> List<List<T>> partitionedList(List<T> list, int size) {
		if (Objects.isNull(list) || list.isEmpty() || size <= 0) {
			return Collections.emptyList();
		}

		// リスト数
		List<List<T>> partitionedList = new ArrayList<List<T>>(size);
		// リストのサイズ
		int block = (list.size() + (size - 1)) / size;

		int addTotal = 0;
		for (int index = 0; index < size; index++) {
			int start = index * block;
			int end = Math.min(start + block, list.size());
			if (addTotal >= list.size()) {
				break;
			}
			partitionedList.add(list.subList(start, end));
			addTotal += list.subList(start, end).size();
		}
		return partitionedList;
	}
}

分割するリストのサイズの計算方法が、いまいち理解できん...

pokuwagata.hatenablog.com

ceil が使える言語ならそれでもいいんじゃないか?と思われるが、a, b の値によっては浮動小数点計算の誤差が発生する可能性があるので、この方法が安全らしい。なるほど。

まあ (a+b-1)/b の方がシンプルだし賢いやり方感がある。

(a + b - 1) / b で a / b の切り上げを計算する - yn2011's blog

⇧ なるほど、Javaを使ってる場合は、

docs.oracle.com

素直に、Math.ceilとか使った方が良さそうな気もするのだけど、どうなんだろう?

毎度モヤモヤ感が半端ない...

今回はこのへんで。