[Java] 配列のメモ


# 目的

Javaの配列を扱うメソッドはいくつかのクラスに分散しているので、整理してみます。
各メソッドの詳細な説明はしません。

# 基本的なこと

- 配列はオブジェクト
- 多次元配列は、配列の配列であらわす
- 配列は共変(covariant)
- Iterableではないけど、拡張forループに使用可能

# 配列のシャローコピー

## 既にある配列に全体、あるい一部をコピー

java.lang.System#arraycopy()

## 同じ配列インスタンスを新しく生成

配列オブジェクト.clone()

- キャスト不要
- CloneNotSupportedExceptionもキャッチ不要

```java
private int[] values;

public void setValues(int[] values) {
    // 防御的コピー
    this.values = values.clone();
}
```

## サイズを指定して、あるいは部分配列を新しく生成

- java.util.Arrays#copyOf()
- java.util.Arrays#copyOfRange()

# 配列のリスト化

java.util.Arrays#asList()

- リストのサイズが変わる操作は不可(add,remove,clearなど)
- 要素の値が変わる操作はOK。
  リストの要素を変えると、配列の要素も変わる
- 要素がプリミティブ型の場合には、可変引数で渡すことはできるけど、配列として渡すことはできないので注意。

## プリミティブ型配列のリスト化(案)

必要なプリミティブ型配列用の、以下のようなユーティリティメソッドを用意すればよいのかな。毎回オートボクシングがはしるけど。

```java
public static List<Integer> asList(final int[] array) {
    return new AbstractList<Integer>() {
        @Override
        public Integer get(int index) {
            return array[index];
        }

        @Override
        public Integer set(int index, Integer element) {
            int oldValue = array[index];
            array[index] = element;
            return oldValue;
        }

        @Override
        public int size() {
            return array.length;
        }
    };
}
```

※本当は java.util.RandomAccess を実装するべきかな。

# リストの配列化

- java.util.List#toArray()
- java.util.List#toArray(T[])

例１：

```java
List<String> list = getList();

// 引数無しは Object[] を返す
Object[] array = list.toArray();
```

例２：

```java
// 十分なサイズの配列を渡せば、その配列が返ってくる
String[] strings = list.toArray(new String[list.size()]);
```

例３：

```java
private static final String[] EMPTY_STRINGS = new String[0];

// ...

// サイズが足りない場合には、新しい配列を生成して返してくれる
String[] strings = list.toArray(EMPTY_STRINGS);
```

# 配列とストリームの相互変換

- java.util.Arrays#stream() - 配列のストリーム化
- java.util.stream.Stream#toArray() - ストリームの配列化

プリミティブ型の対応もあり(IntStream#toArray()とか)。

# リフレクション

配列オブジェクトのlengthフィールドやclone()メソッドは、Class#getDeclaredFields() や Class#getDeclaredMethods() 等では取得できないようです。

その代わりに、配列をリフレクションで扱うための java.lang.reflect.Array クラスがあります。

## オブジェクトが配列かどうか判定

java.lang.Class#isArray()

## 配列の要素の型を取得

java.lang.Class#getComponentType()

## 配列の長さを取得

java.lang.reflect.Array#getLength()

## 配列の要素を取得、設定

- java.lang.reflect.Array#get()
- java.lang.reflect.Array#set()

プリミティブ型を get(), set() で扱うとオートボクシング/アンボクシングされます。
プリミティブ型用の getInt(), getLong(), setInt(), setLong() などもあります。


## 配列の生成

java.lang.reflect.Array#newInstance()

# 配列の文字列化

配列オブジェクトの toString() メソッドは、Objectクラスのものと同様です。
配列の内容を表示したい場合などは、java.util.Arraysのメソッドを利用すると便利です。

- 1次元配列
java.util.Arrays#toString()
- 多次元配列
java.util.Arrays#deepToString()

ただし deepToString() に渡した配列オブジェクトが、自己循環参照をしている配列だった場合には、大変なことになります。
（スタックオーバーフローとか、無限ループとか）

# ソート

ソートは、java.util.Arraysクラスのメソッドで可能です。

- java.util.Arrays#sort()
- java.util.Arrays#parallelSort()

# シャッフル

ソートと同様に java.util.Arrays#asList() と java.util.Colllections#shuffle() を組み合わせて実現可能です。

```java
List<String> list = Arrays.asList(array);
Collections.shuffle(list);
```

プリミティブ型の場合には、自力でがんばるしかないかも。

# その他

その他のユーティリティ系メソッドは、基本的に java.util.Arrays クラスにあります。

- fill : 値埋め
- equals : 一致チェック
- deepEquals : ネスト配列の一致チェック
- binarySearch : バイナリサーチ(ソート済みである必要あり)
- hashCode : ハッシュ値計算
- deepHashCode : ネスト配列のハッシュ値計算

配列オブジェクトの equals() および hashCode() は Object クラスの実装と同じであるため、必要であれば上記 Arrays#equals() や Arrays#hashCode() などを利用してください。

また deep～() 系メソッドには、自己循環参照を渡さないようにする必要があります。

# 配列クラスの名前

配列クラスの各種名前を取得してみると、以下のような感じ。

```java
Integer[][] array = new Integer[0][0];
Class<?> clazz = array.getClass();
System.out.println("Name         : " + clazz.getName());
System.out.println("SimpleName   : " + clazz.getSimpleName());
System.out.println("CanonicalName: " + clazz.getCanonicalName());
System.out.println("TypeName     : " + clazz.getTypeName());
```

```
Name         : [[Ljava.lang.Integer;
SimpleName   : Integer[][]
CanonicalName: java.lang.Integer[][]
TypeName     : java.lang.Integer[][]
```

# 0件の配列

一般的に配列は値の書き換えが可能なため、（仮に final にしても）修正可能オブジェクトになります。

ただし0件の配列は変更可能な部分が存在しないので、イミュータブルオブジェクトです。
そのため、定数として扱ったり、マルチスレッドで共有使用しても問題ありません。

```java
private static final Object[] EMPTY_OBJS = new Object[0];
```

# ビットあるいは真偽値の配列

java.util.BitSet のほうがメモリ効率は良いはず。