配列を分割する(Generics版) ※追記あり
配列を指定サイズで分割したい!
なんてシチュエーションは少ないかもしれませんが、そんな数少ないシチュエーションに先日遭遇しました。
自分が必要としたのは String[] の分割処理だったのですが、せっかくですので、汎用版を作ってみました。
package util; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.List; public class Util { /** * 配列を指定サイズで分割します。分割された配列は List (ArrayList) に格納して戻します。 * <p> * 例) * <pre> * String[] array = new Foo[] { "1", "2", "3", "4", "5", "6", "7" }; * List<String[]> result = Util.splitArray(array, 3, String.class); * * ==> [ {"1", "2", "3"}, {"4", "5", "6"}, ("7"} ] * </pre> * * @param <T> 分割元配列の型 * @param array 分割元となる配列 * @param size 分割サイズ * @param arrayClass 分割後配列の型 * @return 分割された配列のリスト */ public static <T> List<T[]> splitArray(T[] array, int size, Class<?> arrayClass) { List<T[]> list = new ArrayList<T[]>(); for (int i = 0; i < array.length; i += size) { int subArrayLength = (array.length - i > size) ? size : (array.length - i); T[] subArray = newArrayInstance(arrayClass, subArrayLength); System.arraycopy(array, i, subArray, 0, subArrayLength); list.add(subArray); } return list; } @SuppressWarnings("unchecked") private static <T> T[] newArrayInstance(Class<?> arrayClass, int subArrayLength) { return (T[]) Array.newInstance(arrayClass, subArrayLength); } }
T型の配列は new できないので、配列の型情報を渡して Array.newInstance で強引に配列を作っています。
ここを上手く解決できる方法ってあるのかなぁ。
(2009/09/14 1:00 追記) Class#getComponentType() を使用する方法で解決できるようです。詳細は http://blog.smartnetwork.co.jp/staff/node/35 を参照してください。GJ!
おまけ:テストクラス
package util; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import java.util.ArrayList; import java.util.List; import org.junit.Test; public class UtilTest { @Test public void splitArrayのテスト_配列が分割されるパターン() { List<String> list = new ArrayList<String>(); for (int i = 0 ; i < 8; i++) { list.add(String.valueOf(i)); } String[] array = list.toArray(new String[] {}); List<String[]> actualList = Util.splitArray(array, 3, String.class); assertThat(actualList.size(), is(3)); assertThat(actualList.get(0).length, is(3)); assertThat(actualList.get(0), is(new String[] {"0","1","2"})); assertThat(actualList.get(1).length, is(3)); assertThat(actualList.get(1), is(new String[] {"3","4","5"})); assertThat(actualList.get(2).length, is(2)); assertThat(actualList.get(2), is(new String[] {"6","7"})); } @Test public void splitArrayのテスト_配列が分割されないパターン() { List<String> list = new ArrayList<String>(); for (int i = 0 ; i < 5; i++) { list.add(String.valueOf(i)); } String[] array = list.toArray(new String[] {}); List<String[]> actualList = Util.splitArray(array, 5, String.class); assertThat(actualList.size(), is(1)); assertThat(actualList.get(0), is(new String[] {"0","1","2","3","4"})); } @Test public void splitArrayのテスト_配列が巨大な場合のパフォーマンス確認() { List<String> list = new ArrayList<String>(); for (int i = 0 ; i < 1000000; i++) { list.add(String.valueOf(i)); } String[] array = list.toArray(new String[] {}); final int splitSize = 10000; long start = System.nanoTime(); List<String[]> actualList = Util.splitArray(array, splitSize, String.class); long execTime = System.nanoTime() - start; System.out.println(execTime / 1000 / 1000 + " msec."); assertThat(actualList.size(), is(100)); for (int i = 0; i < actualList.size(); i++) { assertThat(actualList.get(i).length, is(splitSize)); } } }