landrunner’s blog

しばらく開発から離れてた人間が、技術的キャッチアップを図るための勉強ブログ

Javaのプリミティブ型配列操作(結合・部分配列・サイズ変更)

プリミティブ型配列

Javaではプリミティブ型(intとかbyteとか)はオブジェクトではないためジェネリクスに適用できないので、Collection等で使えない。 なのでプリミティブ型の値の集合は配列として扱うことになりやすいけど、プリイティブ型の配列はかなり扱いづらい。 オートボクシングが使えないとか、可変長配列(Listとか)にできないなどデメリットがいっぱいだ。

int a = 0;
Integer wa = a; //OK
int[] b = {0};
Integer[] wb = b; //コンパイルエラー オートボクシングはきかない
List<Integer> lb = Arrays.asList(b); //コンパイルエラー int[]のListが生成されそうになる

今回はそれでも配列を使わないといけない時のために結合等の操作方法をまとめておく。 ちなみにここで述べる方法はオブジェクト型の配列にも使用できるものが多いけど、よほど問題がない限り一度Listにしてから操作したほうがいいと思う。 なお、ここのコードはJava7以上の環境を想定している。

配列の結合

標準機能の利用

System.arraycopyを使う。 引数が5つもありどれに何を入れればいいかわかりにくい。バグの温床になりそう。 一応使い方を説明すると コピー元配列、コピー元配列の開始位置、コピー先配列、コピー先配列開始位置、コピーする要素数の順で入力する。

byte[] a = {0,1,2}; 
byte[] b = {3,4,5};
byte[] c = new byte[a.length + b.length];
System.arraycopy(a,0,c,0,a.length); //a→cにコピー
System.arraycopy(b,0,c,a.length, b.length); //b→cにコピー
System.out.println(Arrays.toString(c)); //[0,1,2,3,4,5]が表示される

apache commons langの利用

Apache Commons Langに含まれるArrayUtils.addAllを使えば簡単に書ける。 ちなみにこのメソッドの中では、上のSystem.arraycopyを呼び出している。

byte[] a = {0,1,2}; 
byte[] b = {3,4,5};
//aの後ろにbのすべての要素を追加した配列を返す
byte[] c = ArrayUtils.addAll(a,b); 
System.out.println(Arrays.toString(c)); //[0,1,2,3,4,5]が表示される

guavaの利用

google guavaのBytes.concatでもArrayUtilsと同じようなことができる。 これも中では同じようにSystem.arraycopyを呼んでいる。 こちらのメリットは複数の配列を同時に結合できること。 まとめて複数個の配列を結合するのであれば、一度に領域を確保できる分ArrayUtils.addAllより速い。

byte[] a = {0,1,2}; 
byte[] b = {3,4,5};
byte[] c = {6};
//guavaのBytes.concatでa,b,cの配列をまとめて結合
byte[] d = Bytes.concat(a,b,c); 
System.out.println(Arrays.toString(d)); //[0,1,2,3,4,5,6]が表示される

guavaでbyte以外のプリミティブ型の結合を行う場合は、対応するクラスのconcatを使う。 対応は byte→Bytes int→Ints double→Doubles みたいな感じで先頭を大文字にしてお尻にsをつけたやつ。

部分配列の取得

Arrays.copyOfRangeを使用する。 こいつの仕様が少し厄介で、copyOfRange(byte[] original, int from, int to)という形で範囲を指定するのだけど、toのindexの要素はコピーされず、そのひとつ前の要素までがコピーされる。

byte[] a = {0,1,2,3,4,5};
byte[] b = Arrays.copyOfRange(a,2,4); //a[2]:2, a[3]:3,a[4]:4 
System.out.println(Arrays.toString(b)); //[2,3]が表示される。

最初の要素からのコピーであれば下で書いているcopyOfも使用できる。

配列のサイズ変更

といっても新しい配列を作って元の配列の内容をコピーするだけなんだけど。 Arrays.copyOfを使用すれば、長くも短くもできる。

byte[] a = {0,1,2};
byte[] b = Arrays.copyOf(a, 4); //要素4の配列を生成
byte[] c = Arrays.copyOf(a, 2); //要素2の配列を生成
System.out.println("Length:" + a.length + " Elem:" + Arrays.toString(a));
//Length:3 Elem:[0,1,2]
System.out.println("Length:" + b.length + " Elem:" + Arrays.toString(b));
//Length:4 Elem:[0,1,2,0]
System.out.println("Length:" + c.length + " Elem:" + Arrays.toString(c));
//Length:2 Elem:[0,1]

おわりに

配列で困ったらとりあずArrays, Commons Lang, Guavaあたりを見てみよう。 今回書いたこと以外にもソートだったり値のサーチだったりそれなりのことはできる。