Java Language
配列
サーチ…
前書き
配列は、任意の量の値の格納と検索を可能にします。それらは数学のベクトルに類似しています。配列の配列は行列に似ており、多次元配列として機能します。配列には、 int
型やObject
などの参照型などのプリミティブ型のデータを格納できます。
構文
-
ArrayType[] myArray;
//配列を宣言する -
ArrayType myArray[];
//別の有効な構文(あまり一般的ではなく、推奨されません) -
ArrayType[][][] myArray;
//多次元ジグザグ配列を宣言する([]を繰り返す) -
ArrayType myVar = myArray[index];
//インデックスの要素へのアクセス(読み込み) -
myArray[index] = value;
//配列の位置index
に値を代入する -
ArrayType[] myArray = new ArrayType[arrayLength];
//配列の初期化構文 -
int[] ints = {1, 2, 3};
//値が指定された配列初期化構文。長さは、指定された値の数から推測されます。{[value1 [、value2] *]} -
new int[]{4, -5, 6} // Can be used as argument, without a local variable
-
int[] ints = new int[3]; // same as {0, 0, 0}
-
int[][] ints = {{1, 2}, {3}, null};
//多次元配列の初期化。 int []はObjectを拡張します(したがってanyType []も同様です)。nullは有効な値です。
パラメーター
パラメータ | 詳細 |
---|---|
ArrayType | 配列の型。これはプリミティブ( int 、 long 、 byte )またはObjects( String 、 MyObject など)です。 |
索引 | インデックスとは、配列内の特定のオブジェクトの位置を指します。 |
長さ | 作成されるすべての配列には、指定された長さのセットが必要です。これは、空の配列( new int[3] )を作成するとき、または値( {1, 2, 3} )を指定するときに暗黙のうちに行われます。 |
配列の作成と初期化
基本的なケース
int[] numbers1 = new int[3]; // Array for 3 int values, default value is 0
int[] numbers2 = { 1, 2, 3 }; // Array literal of 3 int values
int[] numbers3 = new int[] { 1, 2, 3 }; // Array of 3 int values initialized
int[][] numbers4 = { { 1, 2 }, { 3, 4, 5 } }; // Jagged array literal
int[][] numbers5 = new int[5][]; // Jagged array, one dimension 5 long
int[][] numbers6 = new int[5][4]; // Multidimensional array: 5x4
配列は、任意のプリミティブ型または参照型を使用して作成できます。
float[] boats = new float[5]; // Array of five 32-bit floating point numbers.
double[] header = new double[] { 4.56, 332.267, 7.0, 0.3367, 10.0 };
// Array of five 64-bit floating point numbers.
String[] theory = new String[] { "a", "b", "c" };
// Array of three strings (reference type).
Object[] dArt = new Object[] { new Object(), "We love Stack Overflow.", new Integer(3) };
// Array of three Objects (reference type).
最後の例では、宣言された配列型のサブタイプが配列内で使用できることに注意してください。
ユーザー定義型の配列は、プリミティブ型と同様に構築できます
UserDefinedClass[] udType = new UserDefinedClass[5];
配列、コレクション、およびストリーム
// Parameters require objects, not primitives
// Auto-boxing happening for int 127 here
Integer[] initial = { 127, Integer.valueOf( 42 ) };
List<Integer> toList = Arrays.asList( initial ); // Fixed size!
// Note: Works with all collections
Integer[] fromCollection = toList.toArray( new Integer[toList.size()] );
//Java doesn't allow you to create an array of a parameterized type
List<String>[] list = new ArrayList<String>[2]; // Compilation error!
// Streams - JDK 8+
Stream<Integer> toStream = Arrays.stream( initial );
Integer[] fromStream = toStream.toArray( Integer[]::new );
イントロ
配列は、固定数のプリミティブ値またはオブジェクトインスタンスへの参照を保持するデータ構造です。
配列内の各項目は要素と呼ばれ、各要素はその数値インデックスによってアクセスされます。配列が作成されると、配列の長さが設定されます。
int size = 42;
int[] array = new int[size];
配列のサイズは、初期化時に実行時に固定されます。初期化後は変更できません。実行時にサイズを変更可能にする必要がある場合は、代わりにArrayList
などのCollection
クラスを使用する必要があります。 ArrayList
は配列に要素を格納し、新しい配列を割り当て 、古い配列から要素をコピーすることによってサイズ変更をサポートします。
配列がプリミティブ型の場合、つまり
int[] array1 = { 1,2,3 };
int[] array2 = new int[10];
値は配列自体に格納されます。上記のarray2
ようにイニシャライザがない場合、各要素に割り当てられるデフォルト値は0
(ゼロ)です。
配列型がオブジェクト参照の場合、
SomeClassOrInterface[] array = new SomeClassOrInterface[10];
配列にはSomeClassOrInterface
型のオブジェクトへの参照が含まれます。これらの参照は、のインスタンスを指すことができるSomeClassOrInterface
または(クラスの)任意のサブクラス又は(インターフェイスの)実装クラスSomeClassOrInterface
。配列宣言に初期化子がない場合、 null
のデフォルト値が各要素に割り当てられnull
。
すべての配列であるのでint
-indexed、アレイのサイズは、で指定されなければならないint
。配列のサイズはlong
として指定することはできません。
long size = 23L;
int[] array = new int[size]; // Compile-time error:
// incompatible types: possible lossy conversion from
// long to int
配列は0
から始まるインデックスシステムを使用します 。つまり、インデックス作成は0
から始まり、 length - 1
終了します。
たとえば、次の図はサイズが10
配列を表しています。ここでは、最初の要素がインデックス1
あり、最後の要素がインデックス10
にあるのではなく、最初の要素がインデックス0
あり、最後の要素がインデックス9
にあります(下図参照)。
配列の要素へのアクセスは一定の時間内に行われます。つまり、配列の最初の要素へのアクセスには、2番目の要素、3番目の要素などをアクセスするのと同じコストがかかります。
Javaには、 リテラルやコンストラクターの表記法など、配列の定義と初期化の方法がいくつか用意されています。 new Type[length]
コンストラクタを使用して配列を宣言する場合、各要素は次のデフォルト値で初期化されます。
-
0
のためのプリミティブ数値型 :byte
、short
、int
、long
、float
、およびdouble
。 -
char
型の'\u0000'
(ヌル文字)。 -
boolean
型の場合はfalse
- 参照型の場合は
null
プリミティブ型配列の作成と初期化
int[] array1 = new int[] { 1, 2, 3 }; // Create an array with new operator and
// array initializer.
int[] array2 = { 1, 2, 3 }; // Shortcut syntax with array initializer.
int[] array3 = new int[3]; // Equivalent to { 0, 0, 0 }
int[] array4 = null; // The array itself is an object, so it
// can be set as null.
配列を宣言する場合、 []
は宣言の先頭(型名の後ろ)、または特定の変数の宣言子の一部(変数名の後ろ)、またはその両方に表示されます。
int array5[]; /* equivalent to */ int[] array5;
int a, b[], c[][]; /* equivalent to */ int a; int[] b; int[][] c;
int[] a, b[]; /* equivalent to */ int[] a; int[][] b;
int a, []b, c[][]; /* Compilation Error, because [] is not part of the type at beginning
of the declaration, rather it is before 'b'. */
// The same rules apply when declaring a method that returns an array:
int foo()[] { ... } /* equivalent to */ int[] foo() { ... }
次の例では、両方の宣言が正しく、問題なくコンパイルおよび実行できます。しかし、 Javaコーディング規約とGoogle Javaスタイルガイドの両方では、変数名の後ろに括弧で囲まれたフォームが推奨されません。 大括弧は配列タイプを識別し、タイプ指定とともに表示されます。メソッドリターンシグネチャにも同じものを使用する必要があります。
float array[]; /* and */ int foo()[] { ... } /* are discouraged */
float[] array; /* and */ int[] foo() { ... } /* are encouraged */
推奨されない型は、変数名の後ろに角括弧が付いたCの構文に精通している移行Cユーザーに対応するためのものです。
Javaでは、サイズ0
配列を持つことができます:
int[] array = new int[0]; // Compiles and runs fine.
int[] array2 = {}; // Equivalent syntax.
しかし、それは空の配列なので、そこから要素を読み込むことも、割り当てもできません:
array[0] = 1; // Throws java.lang.ArrayIndexOutOfBoundsException.
int i = array2[0]; // Also throws ArrayIndexOutOfBoundsException.
このような空の配列は通常は戻り値として便利です。そのため、 NullPointerException
つながる可能性のあるnull
値ではなく、呼び出しコードが配列を扱うだけで心配する必要がありnull
。
配列の長さは、負でない整数でなければなりません:
int[] array = new int[-1]; // Throws java.lang.NegativeArraySizeException
配列のサイズは、 length
というpublic finalフィールドを使用して決定できます。
System.out.println(array.length); // Prints 0 in this case.
注意 : array.length
は、値が割り当てられた配列要素の数を返すArrayList.size()
とは異なり、配列の実際のサイズを返しますが、値が割り当てられた配列要素の数は返しません。
多次元配列の作成と初期化
多次元配列を作成する最も簡単な方法は次のとおりです。
int[][] a = new int[2][3];
a[0]
とa[1]
2つの3つの長さのint
配列を作成します。これは、長方形の多次元配列の古典的なCスタイルの初期化に非常によく似ています。
同時に作成して初期化することができます:
int[][] a = { {1, 2}, {3, 4}, {5, 6} };
矩形の多次元配列のみがサポートされているCとは異なり 、内部配列は同じ長さである必要はなく、定義する必要もありません。
int[][] a = { {1}, {2, 3}, null };
ここでa[0]
は1長のint
型配列ですがa[1]
は2長int
型配列でありa[2]
はnull
です。このような配列は、 ギザギザ配列または不揃い配列と呼ばれ、配列の配列です。 Javaの多次元配列は配列の配列として実装されます。つまり、 array[i][j][k]
は((array[i])[j])[k]
と等価です。 C#と異なり 、シンタックスarray[i,j]
はJavaではサポートされていません。
Javaにおける多次元配列表現
参照型配列の作成と初期化
String[] array6 = new String[] { "Laurel", "Hardy" }; // Create an array with new
// operator and array initializer.
String[] array7 = { "Laurel", "Hardy" }; // Shortcut syntax with array
// initializer.
String[] array8 = new String[3]; // { null, null, null }
String[] array9 = null; // null
上記のString
リテラルとプリミティブに加えて、配列の初期化のためのショートカット構文は、標準Object
型でも機能します。
Object[] array10 = { new Object(), new Object() };
配列は共変であるため、参照型配列はサブクラスの配列として初期化できますが、要素をString
以外のものに設定しようとするとArrayStoreException
がスローされます。
Object[] array11 = new String[] { "foo", "bar", "baz" };
array11[1] = "qux"; // fine
array11[1] = new StringBuilder(); // throws ArrayStoreException
ショートカット構文には暗黙の型のObject[]
が含まれているため、ショートカット構文を使用することはできません。
配列は、 String[] emptyArray = new String[0]
を使用してゼロ要素で初期化することができます。たとえば、メソッドがオブジェクトの実行時の型を必要とする場合、 Collection
からArray
を作成するために、このような長さがゼロの配列が使用されます。
プリミティブ型と参照型の両方で空の配列の初期化(たとえばString[] array8 = new String[3]
)は、各データ型のデフォルト値で配列を初期化します 。
ジェネリック型配列の作成と初期化
ジェネリッククラスでは、 タイプ消去のためにジェネリック型の配列をこのように初期化することはできません 。
public class MyGenericClass<T> {
private T[] a;
public MyGenericClass() {
a = new T[5]; // Compile time error: generic array creation
}
}
代わりに、次の方法のいずれかを使用して作成できます(これらは未チェックの警告を生成します)
Object
配列を作成し、ジェネリック型にキャストする:a = (T[]) new Object[5];
これは最も簡単な方法ですが、基になる配列は依然として
Object[]
型であるため、このメソッドは型の安全性を提供しません。したがって、配列を作成するこの方法は、公開クラスに公開されていない汎用クラス内でのみ使用することをお勧めします。クラスパラメータで
Array.newInstance
を使用Array.newInstance
と:public MyGenericClass(Class<T> clazz) { a = (T[]) Array.newInstance(clazz, 5); }
ここでは、
T
のクラスを明示的にコンストラクタに渡す必要があります。Array.newInstance
の戻り値の型は常にObject
です。ただし、新しく作成された配列は常にT[]
型であるため安全です。したがって、安全に外部化できます。
初期化後の配列の塗りつぶし
Arrays.fill()
を使用して、初期化後に同じ値を持つ配列を塗りつぶすことができます:
Arrays.fill(array8, "abc"); // { "abc", "abc", "abc" }
fill()
は配列の指定された範囲の各要素に値を代入することもできます:
Arrays.fill(array8, 1, 2, "aaa"); // Placing "aaa" from index 1 to 2.
Javaバージョン8のメソッドsetAll
とそのConcurrent
同等のparallelSetAll
使用すると、配列の各要素を生成された値に設定できます。これらのメソッドには、インデックスを受け取り、その位置に望ましい値を返すジェネレータ関数が渡されます。
次の例では、整数配列を作成し、すべての要素をそれぞれのインデックス値に設定します。
int[] array = new int[5];
Arrays.setAll(array, i -> i); // The array becomes { 0, 1, 2, 3, 4 }.
配列の宣言と初期化の分離
配列要素のインデックスの値は、整数(0,1,2,3,4、...)で、配列の長さ(インデックスは0から始まる)より小さくなければなりません。それ以外の場合は、 ArrayIndexOutOfBoundsExceptionがスローされます。
int[] array9; // Array declaration - uninitialized
array9 = new int[3]; // Initialize array - { 0, 0, 0 }
array9[0] = 10; // Set index 0 value - { 10, 0, 0 }
array9[1] = 20; // Set index 1 value - { 10, 20, 0 }
array9[2] = 30; // Set index 2 value - { 10, 20, 30 }
配列初期化のショートカット構文を使用して配列を再初期化することはできません
配列イニシャライザは、フィールド宣言またはローカル変数宣言、または配列作成式の一部としてしか指定できないため、配列初期化子を使用してショートカット構文を使用して配列を再初期化することはできません 。
しかし、新しい配列を作成し、それを古い配列を参照するために使用される変数に割り当てることは可能です。この結果、その変数が参照する配列は再初期化されますが、変数の内容はまったく新しい配列になります。これを行うには、 new
演算子を配列初期化子とともに使用し、配列変数に代入します。
// First initialization of array
int[] array = new int[] { 1, 2, 3 };
// Prints "1 2 3 ".
for (int i : array) {
System.out.print(i + " ");
}
// Re-initializes array to a new int[] array.
array = new int[] { 4, 5, 6 };
// Prints "4 5 6 ".
for (int i : array) {
System.out.print(i + " ");
}
array = { 1, 2, 3, 4 }; // Compile-time error! Can't re-initialize an array via shortcut
// syntax with array initializer.
コレクションからの配列の作成
java.util.Collection
2つのメソッドは、コレクションから配列を作成します。
Object[] toArray()
は、次のように使用できます。
Set<String> set = new HashSet<String>();
set.add("red");
set.add("blue");
// although set is a Set<String>, toArray() returns an Object[] not a String[]
Object[] objectArray = set.toArray();
<T> T[] toArray(T[] a)
は次のように使用できます。
Set<String> set = new HashSet<String>();
set.add("red");
set.add("blue");
// The array does not need to be created up front with the correct size.
// Only the array type matters. (If the size is wrong, a new array will
// be created with the same type.)
String[] stringArray = set.toArray(new String[0]);
// If you supply an array of the same size as collection or bigger, it
// will be populated with collection values and returned (new array
// won't be allocated)
String[] stringArray2 = set.toArray(new String[set.size()]);
それらの違いは、型指定されていない結果と比較しただけのことではありません。それらのパフォーマンスも異なる場合があります(詳しくは、このパフォーマンス分析セクションをお読みください)。
-
Object[] toArray()
ベクトル使用arraycopy
型チェックよりもはるかに高速である、arraycopy
で使用されるT[] toArray(T[] a)
-
T[] toArray(new T[non-zero-size])
は実行時に配列をゼロアウトする必要がありますが、T[] toArray(new T[0])
はそうしません。このような回避は、後者を前者よりも早く呼び出す。詳細な分析はこちら: 古代の知恵の配列 。
Stream
の概念が導入されたJava SE 8以降では、 Stream.toArray
メソッドを使用して新しいArrayを作成するために、コレクションによって生成されたStream
を使用することができます。
String[] strings = list.stream().toArray(String[]::new);
例としては、(2つの答えから採取した1 、 2 )に文字列[]「へのArrayListを『変換』 Javaでスタックオーバーフローに関する。
文字列への配列
Java 1.5以降では、指定された配列の内容をすべての要素を反復処理せずにString
表現で取得できます。多次元配列の場合、 Arrays.toString(Object[])
またはArrays.deepToString(Object[])
を使用してArrays.toString(Object[])
:
int[] arr = {1, 2, 3, 4, 5};
System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5]
int[][] arr = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
System.out.println(Arrays.deepToString(arr)); // [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Arrays.toString()
メソッドは、 Object.toString()
メソッドを使用して配列内のすべての項目のString
値を生成します。プリミティブ型配列の横には、すべてのタイプの配列に使用できます。例えば:
public class Cat { /* implicitly extends Object */
@Override
public String toString() {
return "CAT!";
}
}
Cat[] arr = { new Cat(), new Cat() };
System.out.println(Arrays.toString(arr)); // [CAT!, CAT!]
クラスに対してオーバーライドされたtoString()
が存在しない場合、 Object
から継承されたtoString()
が使用されます。通常、出力はそれほど役に立ちません。たとえば、次のようになります。
public class Dog {
/* implicitly extends Object */
}
Dog[] arr = { new Dog() };
System.out.println(Arrays.toString(arr)); // [Dog@17ed40e0]
配列からのリストの作成
Arrays.asList()
メソッドを使用して、指定された配列の要素を含む固定サイズのList
を返すことができます。結果のList
は、配列の基本型と同じ型の型になります。
String[] stringArray = {"foo", "bar", "baz"};
List<String> stringList = Arrays.asList(stringArray);
注 :このリストは、元の配列の( ビュー )に基づいています。つまり、リストを変更すると配列が変更され、その逆もあります。ただし、リストのサイズを変更すると、配列の長さが変更されるため、例外がスローされます。
リストのコピーを作成するには、 Collection
を引数として取るjava.util.ArrayList
のコンストラクタを使用します。
String[] stringArray = {"foo", "bar", "baz"};
List<String> stringList = new ArrayList<String>(Arrays.asList(stringArray));
Java SE 7以降では、角括弧<>
(型引数の空集合)を使用できます。これはダイヤモンドと呼ばれます。コンパイラは、コンテキストから型引数を決定できます。つまり、型情報はArrayList
のコンストラクタを呼び出すときにArrayList
することができ、コンパイル時に自動的に推論されます。これはJava Genericsの一部である型推論と呼ばれます。
// Using Arrays.asList()
String[] stringArray = {"foo", "bar", "baz"};
List<String> stringList = new ArrayList<>(Arrays.asList(stringArray));
// Using ArrayList.addAll()
String[] stringArray = {"foo", "bar", "baz"};
ArrayList<String> list = new ArrayList<>();
list.addAll(Arrays.asList(stringArray));
// Using Collections.addAll()
String[] stringArray = {"foo", "bar", "baz"};
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, stringArray);
// Using Streams
int[] ints = {1, 2, 3};
List<Integer> list = Arrays.stream(ints).boxed().collect(Collectors.toList());
String[] stringArray = {"foo", "bar", "baz"};
List<Object> list = Arrays.stream(stringArray).collect(Collectors.toList());
Arrays.asList()メソッドの使用に関する重要な注意点
このメソッドは、返す
List
のインスタンスである、Arrays$ArrayList
(の静的内部クラスArrays
)とではないjava.util.ArrayList
。結果として得られるList
は固定サイズです。つまり、要素の追加または削除はUnsupportedOperationException
ため、UnsupportedOperationException
がスローされます。stringList.add("something"); // throws java.lang.UnsupportedOperationException
配列を基にした
List
を新しいList
コンストラクタに渡すことで、新しいList
を作成できます。これにより、データの新しいコピーが作成されます。このコピーは、サイズが変更可能で、元の配列でサポートされていません。List<String> modifiableList = new ArrayList<>(Arrays.asList("foo", "bar"));
int[]
ようなプリミティブ配列上の<T> List<T> asList(T... a)
を呼び出すと、実際の要素ではなく元のプリミティブ配列だけがList<int[]>
ソース配列のこの動作の理由は、ジェネリック型パラメータの代わりにプリミティブ型を使用できないため、この場合はジェネリック型パラメータ全体がプリミティブ配列全体に置き換えられます。プリミティブ配列を
List
に変換するには、まず、プリミティブ配列を対応するラッパー型の配列に変換します(つまり、int[]
ではなくInteger[]
Arrays.asList
を呼び出します)。したがって、これは
false
:int[] arr = {1, 2, 3}; // primitive array of int System.out.println(Arrays.asList(arr).contains(1));
一方、これは
true
:Integer[] arr = {1, 2, 3}; // object array of Integer (wrapper for int) System.out.println(Arrays.asList(arr).contains(1));
配列は
Integer[]
として解釈されるので、これもtrue
:System.out.println(Arrays.asList(1,2,3).contains(1));
多次元配列とジグザグ配列
複数の次元を持つ配列を定義することは可能です。単一の索引を提供することによってアクセスされる代わりに、各ディメンションの索引を指定することによって、多次元配列にアクセスします。
多次元配列の宣言は、各ディメンションの[]
を通常の配列のdeclerationに追加することで行うことができます。たとえば、2次元のint
配列を作成するには、宣言にint[][]
などの別のブラケットセットを追加します。これは、3次元配列( int[][][]
)などでも続きます。
3行3列の2次元配列を定義するには:
int rows = 3;
int columns = 3;
int[][] table = new int[rows][columns];
この構造体を使用して配列にインデックスを付け、値を割り当てることができます。割り当てられていない値は配列の型のデフォルト値です。この場合はint
型の場合は0
です。
table[0][0] = 0;
table[0][1] = 1;
table[0][2] = 2;
一度に次元をインスタンス化し、非長方形配列を作成することもできます。これらは、より一般的にギザギザアレイと呼ばれます 。
int[][] nonRect = new int[4][];
ジャグ配列のいずれかのディメンションを定義することは可能であるが、それは定義されている必要があり 、前のレベルだということに注意することが重要です。
// valid
String[][] employeeGraph = new String[30][];
// invalid
int[][] unshapenMatrix = new int[][10];
// also invalid
int[][][] misshapenGrid = new int[100][][10];
Javaで多次元配列を表現する方法
画像ソース: http : //math.hws.edu/eck/cs124/javanotes3/c8/s5.html
ギザギザの配列リテラルの初期化
多次元配列とギザギザ配列は、リテラル式で初期化することもできます。以下は、2x3のint
配列を宣言して取り込みます:
int[][] table = {
{1, 2, 3},
{4, 5, 6}
};
注意 :ギザギザのサブ配列はnull
でもかまいません。たとえば、次のコードでは、最初のサブ配列がnull
、2番目のサブ配列の長さが0であり、3番目のサブ配列の長さが1で、最後の部分配列が2つの長さの配列である2次元int
配列が宣言され、
int[][] table = {
null,
{},
{1},
{1,2}
};
多次元配列の場合、そのインデックスによって下位次元の配列を抽出することができます。
int[][][] arr = new int[3][3][3];
int[][] arr1 = arr[0]; // get first 3x3-dimensional array from arr
int[] arr2 = arr1[0]; // get first 3-dimensional array from arr1
int[] arr3 = arr[0]; // error: cannot convert from int[][] to int[]
ArrayIndexOutOfBoundsException
ArrayIndexOutOfBoundsException
は、配列の存在しないインデックスがアクセスされているときにスローされます。
配列は0からarray.length - 1
インデックスであるため、最初の要素のインデックスは0
で、最後の要素のインデックスは配列の容量から1
引いた値です(つまりarray.length - 1
)。
したがって、インデックスi
による配列要素の要求は、 0 <= i < array.length
があります。そうでない場合、 ArrayIndexOutOfBoundsException
がスローされます。
次のコードは、 ArrayIndexOutOfBoundsException
がスローされる単純な例です。
String[] people = new String[] { "Carol", "Andy" };
// An array will be created:
// people[0]: "Carol"
// people[1]: "Andy"
// Notice: no item on index 2. Trying to access it triggers the exception:
System.out.println(people[2]); // throws an ArrayIndexOutOfBoundsException.
出力:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 2
at your.package.path.method(YourClass.java:15)
アクセスされている不正なインデックスも例外に含まれています(この例では2
)。この情報は例外の原因を見つけるのに役立ちます。
これを避けるには、インデックスが配列の制限内にあることを確認するだけです。
int index = 2;
if (index >= 0 && index < people.length) {
System.out.println(people[index]);
}
配列の長さの取得
配列は、指定された型の要素のサイズまで格納するためのスペースを提供するオブジェクトです。配列のサイズは、配列の作成後には変更できません。
int[] arr1 = new int[0];
int[] arr2 = new int[2];
int[] arr3 = new int[]{1, 2, 3, 4};
int[] arr4 = {1, 2, 3, 4, 5, 6, 7};
int len1 = arr1.length; // 0
int len2 = arr2.length; // 2
int len3 = arr3.length; // 4
int len4 = arr4.length; // 7
配列内のlength
フィールドには、配列のサイズが格納されます。これはfinal
フィールドであり、変更することはできません。
このコードは、配列のlength
と配列が格納するオブジェクトの量の差を示します。
public static void main(String[] args) {
Integer arr[] = new Integer[] {1,2,3,null,5,null,7,null,null,null,11,null,13};
int arrayLength = arr.length;
int nonEmptyElementsCount = 0;
for (int i=0; i<arrayLength; i++) {
Integer arrElt = arr[i];
if (arrElt != null) {
nonEmptyElementsCount++;
}
}
System.out.println("Array 'arr' has a length of "+arrayLength+"\n"
+ "and it contains "+nonEmptyElementsCount+" non-empty values");
}
結果:
Array 'arr' has a length of 13
and it contains 7 non-empty values
配列の等価性の比較
配列型はequals()
(およびhashCode()
)の実装をjava.lang.Objectから継承しているため、 equals()
はまったく同じ配列オブジェクトと比較した場合にのみtrueを返します。それらの値に基づいて配列が等しいかどうかを比較するには、 java.util.Arrays.equals
使用します。これは、すべての配列型に対してオーバーロードされます。
int[] a = new int[]{1, 2, 3};
int[] b = new int[]{1, 2, 3};
System.out.println(a.equals(b)); //prints "false" because a and b refer to different objects
System.out.println(Arrays.equals(a, b)); //prints "true" because the elements of a and b have the same values
要素型が参照型である場合、 Arrays.equals()
は配列要素に対してequals()
を呼び出して等価性を判定します。特に、要素型がそれ自身が配列型の場合、IDの比較が使用されます。多次元配列を比較するには、以下のようにArrays.deepEquals()
使用してArrays.deepEquals()
。
int a[] = { 1, 2, 3 };
int b[] = { 1, 2, 3 };
Object[] aObject = { a }; // aObject contains one element
Object[] bObject = { b }; // bObject contains one element
System.out.println(Arrays.equals(aObject, bObject)); // false
System.out.println(Arrays.deepEquals(aObject, bObject));// true
セットとマップではequals()
とhashCode()
使用されるため、配列は一般にset要素やマップキーとしては役に立ちません。配列要素に関してequals()
とhashCode()
を実装するヘルパークラスにラップするか、 List
インスタンスに変換してList
を格納します。
ストリームへの配列
オブジェクトの配列をStream
変換する:
String[] arr = new String[] {"str1", "str2", "str3"};
Stream<String> stream = Arrays.stream(arr);
Arrays.stream()
を使用してプリミティブの配列をStream
に変換すると、配列はStreamのプリミティブな特殊化に変換されます。
int[] intArr = {1, 2, 3};
IntStream intStream = Arrays.stream(intArr);
Stream
を配列内の要素の範囲に制限することもできます。開始インデックスはインクルーシブで、終了インデックスは排他的です。
int[] values = {1, 2, 3, 4};
IntStream intStream = Arrays.stream(values, 2, 4);
Stream
クラスには、 Arrays.stream()
と同様のメソッドStream.of()
ます。違いは、 Stream.of()
はvarargsパラメータを使用するため、次のような記述が可能です。
Stream<Integer> intStream = Stream.of(1, 2, 3);
Stream<String> stringStream = Stream.of("1", "2", "3");
Stream<Double> doubleStream = Stream.of(new Double[]{1.0, 2.0});
配列の反復処理
enhanced forループ(別名foreach)または配列インデックスを使用して、配列全体を反復処理できます。
int[] array = new int[10];
// using indices: read and write
for (int i = 0; i < array.length; i++) {
array[i] = i;
}
// extended for: read only
for (int e : array) {
System.out.println(e);
}
ここでは、配列のIteratorを使用する直接的な方法はないが、配列ライブラリを使用してIterable
オブジェクトを取得するためのリストに簡単に変換できることにIterable
することはIterable
です。
ボックス化された配列の場合、 Arrays.asListを使用します :
Integer[] boxed = {1, 2, 3};
Iterable<Integer> boxedIt = Arrays.asList(boxed); // list-backed iterable
Iterator<Integer> fromBoxed1 = boxedIt.iterator();
プリミティブ配列(java 8を使用)では、ストリーム(特にこの例ではArrays.stream - > IntStream )を使用します。
int[] primitives = {1, 2, 3};
IntStream primitiveStream = Arrays.stream(primitives); // list-backed iterable
PrimitiveIterator.OfInt fromPrimitive1 = primitiveStream.iterator();
ストリーム(Java 8なし)を使用できない場合は、Googleのグアバライブラリを使用することができます。
Iterable<Integer> fromPrimitive2 = Ints.asList(primitives);
2次元配列またはそれ以上では、両方の技法をやや複雑な方法で使用することができます。
例:
int[][] array = new int[10][10];
for (int indexOuter = 0; indexOuter < array.length; indexOuter++) {
for (int indexInner = 0; indexInner < array[indexOuter].length; indexInner++) {
array[indexOuter][indexInner] = indexOuter + indexInner;
}
}
for (int[] numbers : array) {
for (int value : numbers) {
System.out.println(value);
}
}
インデックスベースのループを使用せずに配列を任意の不均一な値に設定することは不可能です。
もちろん、インデックスを使って反復するときにwhile
やdo-while
ループを使うこともできます。
注意すべき点は 、配列インデックスを使用する場合は、インデックスが0
からarray.length - 1
(両端を含む)の0
にあることを確認してください。配列の長さについてハードコードされた仮定をしないでください。そうでなければ、配列の長さは変わりますが、ハードコードされた値は変わりません。
例:
int[] numbers = {1, 2, 3, 4};
public void incrementNumbers() {
// DO THIS :
for (int i = 0; i < numbers.length; i++) {
numbers[i] += 1; //or this: numbers[i] = numbers[i] + 1; or numbers[i]++;
}
// DON'T DO THIS :
for (int i = 0; i < 4; i++) {
numbers[i] += 1;
}
}
複雑な計算を使用してインデックスを取得するのではなく、インデックスを使用して反復処理を行い、異なる値が必要な場合はそれらを計算するのが最適です。
例:
public void fillArrayWithDoubleIndex(int[] array) {
// DO THIS :
for (int i = 0; i < array.length; i++) {
array[i] = i * 2;
}
// DON'T DO THIS :
int doubleLength = array.length * 2;
for (int i = 0; i < doubleLength; i += 2) {
array[i / 2] = i;
}
}
配列の逆順へのアクセス
int[] array = {0, 1, 1, 2, 3, 5, 8, 13};
for (int i = array.length - 1; i >= 0; i--) {
System.out.println(array[i]);
}
一時的な配列を使ってコードの繰り返しを減らす
コードを繰り返す代わりに一時配列を反復処理すると、コードがきれいになります。複数の変数に対して同じ操作が実行される場合に使用できます。
// we want to print out all of these
String name = "Margaret";
int eyeCount = 16;
double height = 50.2;
int legs = 9;
int arms = 5;
// copy-paste approach:
System.out.println(name);
System.out.println(eyeCount);
System.out.println(height);
System.out.println(legs);
System.out.println(arms);
// temporary array approach:
for(Object attribute : new Object[]{name, eyeCount, height, legs, arms})
System.out.println(attribute);
// using only numbers
for(double number : new double[]{eyeCount, legs, arms, height})
System.out.println(Math.sqrt(number));
このコードは、ループが入力されるたびに配列が作成され、プリミティブ変数が配列にコピーされて変更できないため、パフォーマンス重視のセクションでは使用しないでください。
配列のコピー
Javaは、配列をコピーするいくつかの方法を提供します。
forループ
int[] a = { 4, 1, 3, 2 };
int[] b = new int[a.length];
for (int i = 0; i < a.length; i++) {
b[i] = a[i];
}
このオプションをプリミティブ配列の代わりにオブジェクト配列に使用すると、コピーの代わりに元の内容を参照してコピーが埋められます。
Object.clone()
配列はJavaのObject
ため、 Object.clone()
使用できます。
int[] a = { 4, 1, 3, 2 };
int[] b = a.clone(); // [4, 1, 3, 2]
配列のObject.clone
メソッドは、 浅いコピーを実行します 。つまり、ソース配列と同じ要素を参照する新しい配列への参照を返します。
Arrays.copyOf()
java.util.Arrays
は配列のコピーを別の配列に簡単に実行する方法を提供します。ここでは基本的な使い方です:
int[] a = {4, 1, 3, 2};
int[] b = Arrays.copyOf(a, a.length); // [4, 1, 3, 2]
Arrays.copyOf
は、配列の型を変更できるオーバーロードも用意されています。
Double[] doubles = { 1.0, 2.0, 3.0 };
Number[] numbers = Arrays.copyOf(doubles, doubles.length, Number[].class);
System.arraycopy()
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
指定された位置から始まる、指定されたソース配列の配列を、コピー先配列の指定された位置にコピーします。
以下の使用例
int[] a = { 4, 1, 3, 2 };
int[] b = new int[a.length];
System.arraycopy(a, 0, b, 0, a.length); // [4, 1, 3, 2]
Arrays.copyOfRange()
主に配列の一部をコピーするために使用されますが、配列全体を次のようにコピーすることもできます:
int[] a = { 4, 1, 3, 2 };
int[] b = Arrays.copyOfRange(a, 0, a.length); // [4, 1, 3, 2]
キャスティング配列
配列はオブジェクトですが、その型は含まれるオブジェクトの型によって定義されます。したがって、 A[]
をT[]
にキャストA[]
ことはできませんが、特定のA[]
各AメンバをT
オブジェクトにキャストする必要があります。一般的な例:
public static <T, A> T[] castArray(T[] target, A[] array) {
for (int i = 0; i < array.length; i++) {
target[i] = (T) array[i];
}
return target;
}
したがって、 A[]
配列が与えられたとA[]
。
T[] target = new T[array.Length];
target = castArray(target, array);
Java SEは、この目的のためにArrays.copyOf(original, newLength, newType)
メソッドを提供します。
Double[] doubles = { 1.0, 2.0, 3.0 };
Number[] numbers = Arrays.copyOf(doubles, doubles.length, Number[].class);
配列から要素を削除する
Javaは配列から要素を削除するためのjava.util.Arrays
直接的なメソッドを提供しません。これを実行するには、元の配列を要素なしで新しい配列にコピーするか、または配列を別の構造に変換して削除することができます。
ArrayListの使用
配列をjava.util.List
に変換し、要素を削除して、リストを次のように配列に戻すことができます。
String[] array = new String[]{"foo", "bar", "baz"};
List<String> list = new ArrayList<>(Arrays.asList(array));
list.remove("foo");
// Creates a new array with the same size as the list and copies the list
// elements to it.
array = list.toArray(new String[list.size()]);
System.out.println(Arrays.toString(array)); //[bar, baz]
System.arraycopyの使用
System.arraycopy()
を使用して元の配列のコピーを作成し、必要な要素を削除できます。以下に例を示します。
int[] array = new int[] { 1, 2, 3, 4 }; // Original array.
int[] result = new int[array.length - 1]; // Array which will contain the result.
int index = 1; // Remove the value "2".
// Copy the elements at the left of the index.
System.arraycopy(array, 0, result, 0, index);
// Copy the elements at the right of the index.
System.arraycopy(array, index + 1, result, index, array.length - index - 1);
System.out.println(Arrays.toString(result)); //[1, 3, 4]
Apache Commons Langの使用
要素を簡単に削除するには、 Apache Commons Langライブラリ、特にArrayUtils
クラスの静的メソッドremoveElement()
を使用できます。以下に例を示します。
int[] array = new int[]{1,2,3,4};
array = ArrayUtils.removeElement(array, 2); //remove first occurrence of 2
System.out.println(Arrays.toString(array)); //[1, 3, 4]
配列の共分散
オブジェクト配列は共変です。つまり、 Integer
はNumber
サブクラスと同じように、 Integer[]
はNumber[]
サブクラスです。これは直感的なように見えるかもしれませんが、驚くべき振る舞いにつながります:
Integer[] integerArray = {1, 2, 3};
Number[] numberArray = integerArray; // valid
Number firstElement = numberArray[0]; // valid
numberArray[0] = 4L; // throws ArrayStoreException at runtime
Integer[]
はNumber[]
サブクラスですが、 Integer
のみを保持でき、 Long
要素を割り当てようとすると実行時例外がスローされます。
この動作は配列固有の動作であり、代わりに汎用List
を使用することで回避できます。
List<Integer> integerList = Arrays.asList(1, 2, 3);
//List<Number> numberList = integerList; // compile error
List<? extends Number> numberList = integerList;
Number firstElement = numberList.get(0);
//numberList.set(0, 4L); // compile error
配列の型のサブクラスである限り、すべての配列要素が同じ型を共有する必要はありません。
interface I {}
class A implements I {}
class B implements I {}
class C implements I {}
I[] array10 = new I[] { new A(), new B(), new C() }; // Create an array with new
// operator and array initializer.
I[] array11 = { new A(), new B(), new C() }; // Shortcut syntax with array
// initializer.
I[] array12 = new I[3]; // { null, null, null }
I[] array13 = new A[] { new A(), new A() }; // Works because A implements I.
Object[] array14 = new Object[] { "Hello, World!", 3.14159, 42 }; // Create an array with
// new operator and array initializer.
Object[] array15 = { new A(), 64, "My String" }; // Shortcut syntax
// with array initializer.
どのように配列のサイズを変更しますか?
簡単な答えはあなたがこれをすることができないということです。アレイが作成されると、そのサイズは変更できません。代わりに、適切なサイズの新しい配列を作成し、既存の配列から新しい配列に要素をコピーすることによってのみ、配列のサイズを変更することができます。
String[] listOfCities = new String[3]; // array created with size 3.
listOfCities[0] = "New York";
listOfCities[1] = "London";
listOfCities[2] = "Berlin";
たとえば、上記のように定義されたlistOfCities
配列に新しい要素を追加する必要があるとします。これを行うには、以下が必要です。
- サイズ4の新しい配列を作成し、
- 古い配列の既存の3つの要素をオフセット0,1,2で新しい配列にコピーし、
- オフセット3の新しい配列に新しい要素を追加します。
上記を行うにはさまざまな方法があります。 Java 6以前は、最も簡潔な方法は次のとおりでした。
String[] newArray = new String[listOfCities.length + 1];
System.arraycopy(listOfCities, 0, newArray, 0, listOfCities.length);
newArray[listOfCities.length] = "Sydney";
以降のJava 6からは、 Arrays.copyOf
とArrays.copyOfRange
方法がより簡単にこれを行うことができます:
String[] newArray = Arrays.copyOf(listOfCities, listOfCities.length + 1);
newArray[listOfCities.length] = "Sydney";
アレイをコピーする他の方法については、次の例を参照してください。サイズを変更するときは、オリジナルとは異なる長さの配列コピーが必要であることに注意してください。
配列のサイズ変更のより良い代替方法
上記のように配列のサイズを変更すると、2つの大きな欠点があります:
- それは非効率的です。配列を大きくする(または小さくする)には、既存の配列要素の多くまたはすべてをコピーし、新しい配列オブジェクトを割り当てます。配列が大きければ大きいほど、それは高くなります。
- 古い配列への参照を含む "ライブ"変数を更新できる必要があります。
1つの方法は、最初に十分な大きさの配列を作成することです。これは、配列を割り当てる前にサイズを正確に判断できる場合にのみ有効です。それができない場合は、配列のサイズ変更の問題が再度発生します。
もう1つの方法は、Java SEクラスライブラリまたはサードパーティのライブラリによって提供されるデータ構造クラスを使用する方法です。たとえば、Java SEの「コレクション」フレームワークでは、さまざまな実行時プロパティを持つList
API、 Set
APIおよびMap
APIの実装が多数提供されています。 ArrayList
クラスは、参照の更新の問題なしで効率的なサイズ変更を提供しながら、プレーン配列(O(N)ルックアップ、O(1)getおよびset、O(N)ランダム挿入および削除など)のパフォーマンス特性に最も近いArrayList
。
( ArrayList
のサイズ変更の効率は、各リサイズでバッキング配列のサイズを2倍にするという戦略から来ていますが、典型的なユースケースでは、時折リサイズするだけです。リストのライフタイムにわたって償却すると、インサートごとにO(1)
です。プレーンな配列のサイズを変更するときにも同じ方法を使用することができます)。
配列内の要素を見つける
配列内の値の場所を見つける方法はたくさんあります。次の例のスニペットは、配列が次のいずれかであるとみなしています。
String[] strings = new String[] { "A", "B", "C" };
int[] ints = new int[] { 1, 2, 3, 4 };
さらに、それぞれindex
またはindex2
を必要な要素のインデックスに設定しindex
。要素が存在しない場合は-1
設定しindex
。
Arrays.binarySearch
使用(ソートされた配列の場合のみ)
int index = Arrays.binarySearch(strings, "A");
int index2 = Arrays.binarySearch(ints, 1);
Arrays.asList
使用(非プリミティブ配列の場合のみ)
int index = Arrays.asList(strings).indexOf("A");
int index2 = Arrays.asList(ints).indexOf(1); // compilation error
Stream
使用
int index = IntStream.range(0, strings.length)
.filter(i -> "A".equals(strings[i]))
.findFirst()
.orElse(-1); // If not present, gives us -1.
// Similar for an array of primitives
ループを使った線形探索
int index = -1;
for (int i = 0; i < array.length; i++) {
if ("A".equals(array[i])) {
index = i;
break;
}
}
// Similar for an array of primitives
org.apache.commonsなどの第三者のライブラリを使用した線形検索
int index = org.apache.commons.lang3.ArrayUtils.contains(strings, "A");
int index2 = org.apache.commons.lang3.ArrayUtils.contains(ints, 1);
注意:直接線形検索を使用する方が、リスト内で折り返すより効率的です。
配列に要素が含まれているかどうかを調べる
上記の例は、計算されたインデックスがゼロ以上であるかどうかを単にテストすることによって、配列に要素が含まれているかどうかをテストするために適合させることができます。
あるいは、いくつかの簡潔なバリエーションもあります。
boolean isPresent = Arrays.asList(strings).contains("A");
boolean isPresent = Stream<String>.of(strings).anyMatch(x -> "A".equals(x));
boolean isPresent = false;
for (String s : strings) {
if ("A".equals(s)) {
isPresent = true;
break;
}
}
boolean isPresent = org.apache.commons.lang3.ArrayUtils.contains(ints, 4);
配列の並べ替え
配列の並べ替えは、 配列 APIを使って簡単に行うことができます。
import java.util.Arrays;
// creating an array with integers
int[] array = {7, 4, 2, 1, 19};
// this is the sorting part just one function ready to be used
Arrays.sort(array);
// prints [1, 2, 4, 7, 19]
System.out.println(Arrays.toString(array));
文字列配列のソート:
String
は数値データではなく、辞書順と呼ばれる独自の順序を定義します。アルファベット順とも呼ばれます。 sort()
メソッドを使用してStringの配列をソートすると、配列はComparableインターフェイスで定義された自然順番にソートされます。
増加する注文
String[] names = {"John", "Steve", "Shane", "Adam", "Ben"};
System.out.println("String array before sorting : " + Arrays.toString(names));
Arrays.sort(names);
System.out.println("String array after sorting in ascending order : " + Arrays.toString(names));
出力:
String array before sorting : [John, Steve, Shane, Adam, Ben]
String array after sorting in ascending order : [Adam, Ben, John, Shane, Steve]
注文の減少
Arrays.sort(names, 0, names.length, Collections.reverseOrder());
System.out.println("String array after sorting in descending order : " + Arrays.toString(names));
出力:
String array after sorting in descending order : [Steve, Shane, John, Ben, Adam]
オブジェクト配列のソート
オブジェクト配列をソートするには、ソートの順序を定義するために、すべての要素がComparable
またはComparator
どちらかのインタフェースを実装する必要があります。
いずれかのsort(Object[])
メソッドを使用して自然順序でオブジェクト配列をソートできますが、配列内のすべての要素がComparable
実装Comparable
必要があります。
さらに、 e1.compareTo(e2)
は配列の要素e1とe2に対してClassCastException
をスローしてはいけません。あるいは、次の例に示すようにsort(T[], Comparator)
メソッドを使用してカスタム配列でObject配列をソートすることもできます。
// How to Sort Object Array in Java using Comparator and Comparable
Course[] courses = new Course[4];
courses[0] = new Course(101, "Java", 200);
courses[1] = new Course(201, "Ruby", 300);
courses[2] = new Course(301, "Python", 400);
courses[3] = new Course(401, "Scala", 500);
System.out.println("Object array before sorting : " + Arrays.toString(courses));
Arrays.sort(courses);
System.out.println("Object array after sorting in natural order : " + Arrays.toString(courses));
Arrays.sort(courses, new Course.PriceComparator());
System.out.println("Object array after sorting by price : " + Arrays.toString(courses));
Arrays.sort(courses, new Course.NameComparator());
System.out.println("Object array after sorting by name : " + Arrays.toString(courses));
出力:
Object array before sorting : [#101 Java@200 , #201 Ruby@300 , #301 Python@400 , #401 Scala@500 ]
Object array after sorting in natural order : [#101 Java@200 , #201 Ruby@300 , #301 Python@400 , #401 Scala@500 ]
Object array after sorting by price : [#101 Java@200 , #201 Ruby@300 , #301 Python@400 , #401 Scala@500 ]
Object array after sorting by name : [#101 Java@200 , #301 Python@400 , #201 Ruby@300 , #401 Scala@500 ]
プリミティブとボックス型の間の配列の変換
場合によっては、 プリミティブ型からボックス型への変換が必要になることがあります。
配列を変換するには、ストリームを使用することができます(Java 8以上)。
int[] primitiveArray = {1, 2, 3, 4};
Integer[] boxedArray =
Arrays.stream(primitiveArray).boxed().toArray(Integer[]::new);
下位バージョンでは、プリミティブ配列を反復して明示的にボックス化された配列にコピーすることができます:
int[] primitiveArray = {1, 2, 3, 4};
Integer[] boxedArray = new Integer[primitiveArray.length];
for (int i = 0; i < primitiveArray.length; ++i) {
boxedArray[i] = primitiveArray[i]; // Each element is autoboxed here
}
同様に、ボックス化された配列は、そのプリミティブの対応する配列に変換できます。
Integer[] boxedArray = {1, 2, 3, 4};
int[] primitiveArray =
Arrays.stream(boxedArray).mapToInt(Integer::intValue).toArray();
Integer[] boxedArray = {1, 2, 3, 4};
int[] primitiveArray = new int[boxedArray.length];
for (int i = 0; i < boxedArray.length; ++i) {
primitiveArray[i] = boxedArray[i]; // Each element is outboxed here
}