S-JIS[2003-07-06/2021-09-19] 変更履歴

Java配列

Javaの配列について。

他言語の配列表記方法

配列の宣言

配列の宣言の仕方は2通りある。

	int a[];
	int[] a;	//Javaでは基本的にこちらが使われる

ローカル変数以外の場合、宣言だけだと、配列変数にはnullがデフォルトとして入る。(上記の例だと、a=null)
ちなみに、「int a[10]」といった書き方は、コンパイルエラーになってしまう。

一行で複数の配列変数を宣言するには、以下のように書く。[2008-04-22]
(しかし現実的には、一行には1つの変数だけを宣言するのが普通)

	int a[], b[];	//C言語風
	int[] a, b;	//Javaはこちらを使う

使うには、newで実体を用意する必要がある。配列の個数は、ここで初めて決まる。

	a = new int[10];

この場合、a[0]〜a[9]が使用可能。範囲外の添え字を使おうとすると実行時に例外(ArrayIndexOutOfBoundsException)が発生する。
配列の個数a.lengthで取得することが出来る。

	System.out.println("配列の個数" + a.length);

配列宣言時の初期値の設定として、次のような書き方が出来る。

	String[] ss = new String[]{ "abc", "def" };
	String[] ss = { "abc", "def" };

(C言語と同じく、)配列の初期値の並びの最後にカンマが入っていても大丈夫(ただ単に無視される)。[2008-07-03]

	String[] ss = { "abc", "def", };	//これは、要素数が2個の配列

初期値を設定しなかった場合は、各要素には型のデフォルト値が入る。

	int[] a = new int[10];		//a[n]の内容は、0
	String[] s = new String[10];	//s[n]の内容は、null

多次元配列の書き方 [2008-05-11]
リストから配列への変換 [2008-08-11]
配列をリフレクションで扱う方法 [2007-02-13]


配列に(宣言時でなく、後から)初期値を与える場合は以下のようになる。[2008-05-11]

	String[] ss;
○	ss = new String[]{ "abc", "def" };
×	ss =             { "abc", "def" };	←この書き方は出来ない
○	int[] a = { 1, 2, 3};
○	a = new int[]{ 4, 5, 6};
×	a = { 7, 8, 9 };

配列の使用

配列は以下の様に(普通の変数と同様に)扱う。[2008-08-09]

	int[] array = new int[10];

	arr[0] = 1;
	System.out.println(array[0]);
	int sum = 0;
	for (int i = 0; i < array.length; i++) {
		sum += array[i];
	}
	int sum = 0;
	for (int n : array) {	//JDK1.5以降
		sum += n;
	}

配列の値埋め

配列を特定の値で埋めるには、java.util.Arraysというユーティリティークラスを利用する。[2006-11-18]

	Arrays.fill(a, 値);
	Arrays.fill(a, pos, pos+len, 値);

配列の比較

2つの配列の内容が一致しているかどうかを判定するメソッドもある。[2007-01-11]

	if (Arrays.equals(a1 ,a2)) {
		//一致
	}
	if (Arrays.deepEquals(a1, a2)) { //JDK1.5以降 [2007-12-07]
		//ネスト(入れ子)になった配列としても完全一致
	}
比較メソッド[2017-09-25]
メソッド ver 備考
boolean equals(型[] a, 型[] b) 1.2 等しいときtrue
boolean equals(
  型[] a, int aFromIndex, int aToIndex,
  型[] b, int bFromIndex, int bToIndex
)
9
boolean equals(T[] a, T[] b, Comparator<T> cmp) 9
boolean equals(
  T[] a, int aFromIndex, int aToIndex,
  T[] b, int bFromIndex, int bToIndex,
  Comparator<T> cmp
)
9
boolean deepEquals(Object[] a, Object[] b) 1.5 等しいときtrue
int compare(型[] a, 型[] b) 9 等しいとき0
a<bのとき負数
a>bのとき正数
int compare(
  型[] a, int aFromIndex, int aToIndex,
  型[] b, int bFromIndex, int bToIndex
)
9
int compareUnsigned(型[] a, 型[] b) 9
int mismatch(型[] a, 型[] b) 9 等しいとき-1
不一致のときは、最初に不一致になった箇所のindex
int mismatch(
  型[] a, int aFromIndex, int aToIndex,
  型[] b, int bFromIndex, int bToIndex
)
9
int mismatch(T[] a, T[] b, Comparator<T> cmp) 9
int mismatch(
  T[] a, int aFromIndex, int aToIndex,
  T[] b, int bFromIndex, int bToIndex,
  Comparator<T> cmp
)
9

配列のequals()hashCode()は、特に実装されていない。[2010-03-01]
つまりObjectクラスの実装のままなので、インスタンスの一致かどうかは判定できても内容の一致は判定できない。

	byte[] b1 = new byte[10];
	byte[] b2 = new byte[10];
	System.out.println(b1.equals(b2));		→false
	System.out.println(Arrays.equals(b1, b2));	→true

配列のコピー

配列をコピーするメソッドは、なぜかSystemに用意されている。[2006-11-18]
これはマシン依存の実装がされており、ループを使って自分でコピーするより高速(だといいなぁと思っていつもこれを使っている(爆))。

	System.arraycopy(src, src_pos, dst, dst_pos, len);

JDK1.6からは、コピーメソッド(というか、内容をコピーした新しい配列を作るメソッド)がArraysに用意された。[2007-12-07]
(内部では結局System#arraycopy()を呼んでるみたい)

	Type[] src = new Type[] { 〜 };
	Type[] dst = Arrays.copyOf(src, src.length);
	Type[] ds2 = Arrays.copyOfRange(src, from, to);

配列とリストとを変換する方法 [2007-12-07]


内部でSystem.arraycopy()を呼んでいるからArraysを使わず直接System.arraycopy()を呼んだ方が速そうな気がする。[2013-01-26]

だが、Arrays.copyOf()を使うと(配列のコピーであることが明らかなので(?))最適化が働くんだそうだ。


配列のダンプ

配列オブジェクトをSystem.out.println()で表示すると、ただ単にクラス名とハッシュコードが表示される。[2007-12-07]
多くのプログラマーにとって、これは望んだ動作ではないだろう。

JDK1.5で、配列(の各要素)を(列挙した)文字列に変換するメソッドが用意された。
int[]等のプリミティブ型の配列も、それ用のオーバーロードが用意されているので 利用可)

	String[] arr = { "abc", "def" };
	System.out.println(Arrays.toString(arr));

実行結果:

[abc, def]


入れ子になった配列(多次元配列)の内容を文字列に変換には、以下のメソッドを使う。(ネストした配列用なので、Objectの派生クラスの配列のみ使用可能。つまり単純な(一次元の)プリミティブ型の配列は対象外)

	String[][] arrs = { { "abc", "def" }, { "foo", "zzz" } };
	System.out.println(Arrays.deepToString(arrs));

	int[][] arri = { { 1, 2, 3 }, { 4, 5, 6 } };
	System.out.println(Arrays.deepToString(arri));

実行結果:

[[abc, def], [foo, zzz]]
[[1, 2, 3], [4, 5, 6]]

バイト配列(byte[ ])

バイト列(byte[])を十六進数でダンプするHexDumpEncoderというクラスもある。[2007-01-11]
このクラスはsun.miscというパッケージに属しており、Javaの標準のランタイムであるrt.jarの中に入っているのですぐ使えるが、ドキュメント化(Javadocが公開)されていない。 (=非公開なクラス・メソッド。将来勝手に変わっても文句言えない)

import sun.misc.HexDumpEncoder;

	HexDumpEncoder hexdumpencoder = new HexDumpEncoder();
	System.out.println(hexdumpencoder.encodeBuffer(byteArray));

Java17で追加されたHexFormatというクラスで、バイト配列を文字列に変換することが出来る。[2021-09-19]

import java.util.HexFormat;

	HexFormat hex = HexFormat.ofDelimiter();
	System.out.println(hex.formatHex(byteArray));

C言語では配列はポインターなので、配列の一部を関数に渡そうと思えばポインターにオフセットを加算すればよく、大した区別は無い。

	//C言語:呼ばれる関数の例
	copy(char *src, char *dst, int len)

	//呼ぶ例
	copy(src,   dst, len  );
	copy(src+1, dst, len-2);

Javaではそう簡単にいかないが、呼ばれるメソッドが引数にInputStream/OutputStreamを使う(呼ぶ側はByteArrayInputStream/ ByteArrayOutputStreamを使う)ようにすると 似た状態が出来る。

	//Java:呼ばれるメソッドの例
	copy(InputStream is, OutputStream os, int len)

	//呼ぶ例
	ByteArrayOutputStream os = new ByteArrayOutputStream();
	copy(new ByteArrayInputStream(src), os, len);
	copy(new ByteArrayInputStream(src, 1, len-2), os, len-2);
	byte[] dst = os.toByteArray();

もちろん実行速度はバイト配列を直接渡すより遅くなると思うが、もともとC言語に比べれば実行効率は悪いんだし…、Javaを使うからにはコーディングが便利になるようにすべきだろう。
例えば 上記のcopyメソッドなら、バイト配列だけでなくファイルの入出力も同じメソッドがそのまま使える。ポリモルフィズムというやつ。オブジェクト指向万歳!(笑)

	//ファイル入出力の例
	copy(new FileInputStream(src_name), new FileOutputStream(dst_name), len);

配列もオブジェクト

配列も参照型となる。[2008-09-13]

配列の直接の親クラスはObject
また、SerializableCloneableインターフェースを実装している。

したがってclone()を実行することが出来る。

	int[]  a = new int[10];
	Object b = a.clone();	//JDK1.4
	int[]  c = a.clone();	//JDK1.5以降のclone()の拡張に対応している

ただ、clone()メソッドはリフレクション(getMethod()系)では取得できないようだ。

そういう意味では、length(フィールド)もリフレクション(getField()系)では取得できない(Array.getLength()を使う)。

Java言語仕様第3版10.7章『配列のメンバ』


共変

配列は共変(covariant)な変換が出来るらしい。[2008-05-21]
つまり、要素のクラスに継承関係があるとき、その配列は親クラスの配列に代入できる。

	Integer[] arri = new Integer[10];
	Number[]  arrn = arri;	//代入可能

でもこれはタイプセーフ(型安全)でなくなってしまうので、危険行為(違う型を入れていることがコンパイル時に分からない為)。
Javaの失敗と言われているらしい。

	Integer[] arri = new Integer[10];
	Number[]  arrn = arri;
	arrn[0] = new Long(0);	//コンパイルエラーにならず、実行時にjava.lang.ArrayStoreExceptionが発生する。

共変戻り値も使えなくはない


ビット配列・boolean配列

booleanの配列もboolean[]で扱える。[2010-02-13]
しかしbooleanは真か偽かの二値しか持たないのに、boolean[](やbooleanのリスト:List<Boolean>)はメモリーの使用効率が悪い。
そういう場合にjava.util.BitSetが使える。
(名前は「Set」だが、Setの具象クラスではない。使い道からするとListにも似ているが、Listの具象クラスでもない)

	BitSet bs = new BitSet(32); //初期ビット数
	bs.set(0, true);
	boolean r = bs.get(1);

指定したインデックスが負だと例外が発生するが、正の数の場合は(intの範囲内で)いくつでも許容される。
インデックス毎にbooleanを保持しているという考え方の他に、nビット目のビットを立てる・テストするという考え方も出来る。

BitSetでは、内部ではlong配列の各ビットでbooleanを表現している。
特にtrueをセットする(1を立てる)場合は、内部配列の最大サイズを超えていたら自動的に拡張される。
(falseをセットする(あるいはクリアする、0にする)場合は、デフォルト値でもあるので、配列の拡張は伴わない)
BitSet#size()で返ってくるのは、実際に内部で確保されている配列のサイズ(配列の要素数でなく、ビット数)。


Java目次へ戻る / 新機能へ戻る / 技術メモへ戻る
メールの送信先:ひしだま