配列とList

Listとは

第1講で,Javaでは,配列ではなく List を使うように述べました. ここでは,Listの使い方を学びます.

Java言語に限らずListとはデータ構造の一つで,順序を持つ複数のデータを扱います. 要するに配列を置き換えるものです.Listの特徴は次の通りです.

基本的な特徴は配列と同じです. しかし,配列にはない特徴もあります.例えば,サイズを生成時に決める必要がない, やサイズが自動的に増減するなどです.Listは配列よりも優れた特徴を持つ型ですから, 配列ではなく,Listを積極的に使っていきましょう.

Listの種類

Java言語では順序を持つデータの集合をListと呼びます. その実現方法は配列を用いる方法,リンクリストを用いる方法の2種類があります. それぞれの実現方法には向いている点,向いていない点が存在します. そのため,Javaにはそれぞれの実現方法でListを実現する型が 存在します.ArrayListLinkedListの2つの型です.どちらを使っても処理の違いはありませんが, 処理内容によっては実行速度が違ってくる可能性があります. とはいえ,その違いが効いてくるのは,もっと大規模で,実行速度が重視される場合ですから,今は気にしなくても良いでしょう.

リンクリストの詳細については,FAQリンクリストとは何ですかを参照してください.

基本的にArrayListLinkedListも使い方に違いはありません. 以下の例では,ArrayListで説明していますが,ArrayListLinkedListに置き換えても 同じ説明が成り立ちますので,適宜読み替えてください.

なお,ArrayListを使うときには,import文が必要です.import java.util.ArrayList;クラス宣言の前に書きましょう.

Listの宣言方法

Java言語でListを使うには,今までとは少し異なる宣言方法が必要です. データ構造にどのような型の変数を格納するかを型宣言に含める必要があります. 例えば,ArrayListString型や,Integer型を格納しようとすると,次のような宣言が必要になります.

// String型を格納する ArrayList.
ArrayList<String> listForStrings =
    new ArrayList<String>();
// Integer型を格納する ArrayList.
ArrayList<Integer> listForIntegers =
    new ArrayList<Integer>();

上記のように,ArrayList<格納する型> という型として宣言しなければならず,また, 実体を作成するときも,new ArrayList<格納する型>() のように作成しなければいけません. 格納する型が異なるとコンパイルエラーが発生します.

なお,実体を作成するときの格納する型は,下のように省略できます.以下のコードは上に挙げたコードと全く同じ結果になります.

ArrayList<String> listForStrings = new ArrayList<>();
ArrayList<Integer> listForIntegers = new ArrayList<>();

Listの操作

一般的に Listに対して行える操作は4種類です. その操作は,CRUDと呼ばれます.作成(Create),読み取り(Read),更新(Update),削除(Delete)の4種類です. 以降の説明は,次に示すArrayList型の変数に対してメソッドを呼び出すものとして読み進めてください.

ArrayList<String> list = new ArrayList<>();
// ArrayList<String> list = new ArrayList<String>();

Listにデータを追加する (add)

String value1 = // ...
list.add(value1);
list.add("Haruaki Tamada");
// list.add(9); // => コンパイルエラーが発生する.
    // 9 は String型ではないため.
list.add("9"); // => OK."9"は文字列.

データを追加するには,上のサンプルのようにaddメソッドを用います. データ集合の最後に追加されていきます.用意されている長さを超えて追加しようとすると,自動的に長さが伸びていくため, 理論上は,無制限にデータを追加できます.

Listにあるデータを取得する (get)

String item1 = list.get(0);
    // => listのインデックスも配列と同じように0から始まる.
String item2 = list.get(1);
String item100 = list.get(100);
    // => 範囲を超えてアクセスしようとすると,実行時に
    // IndexOutOfBoundsException というエラーが発生する.

上のサンプルのように,getメソッドを呼び出すことで,List内の特定の要素を取得できます. 返り値の型は,ArrayList型の変数の宣言時に指定した型でなければいけません.

Listにあるデータを更新する (set)

list.set(1, "TAMADA, Haruaki");

特定の要素を置き換える場合は,setメソッドが利用できます. インデックスと置き換え後のデータを指定すると,データの更新が可能です. 指定したインデックスに要素が存在しない場合は,IndexOutOfBoundsExceptionというエラーが発生します.

Listにあるデータを削除する (remove)

list.remove(1);

指定したインデックスの要素を削除したい場合は,removeメソッドを 利用します.removeを使って削除したあと,後ろの要素は詰められます. すなわち,次のコードで全ての要素を順番に削除できます.

while(!list.isEmpty()){ // listが空じゃない間繰り返す.
    list.remove(0);     // 一番最初の要素を削除する.
}

Listのサイズを取得する (size)

List の現在のサイズ(長さ)を取得する場合は,sizeメソッドを利用しましょう. 文字列の長さ,配列の長さと混同しやすいため,注意してください.

Listの要素の繰り返し (Iterator)

Java言語での Listの繰り返しは,次の3種類が利用できます.

典型的な方法

一番典型的な方法です.ただし,ループの途中で listの要素数を変化させることは 混乱の元になるので,ループ内での Listへの値の追加・削除は行わない方が良いでしょう.

for(Integer i = 0; i < list.size(); i++){
    String item = list.get(i);
    // ここに繰り返しの処理を書く.
}

Iterator

Iterator 型を利用する方法.Javaらしい書き方.

for(Iterator<String> iterator = list.iterator();
        iterator.hasNext(); ){
    String item = iterator.next();
    // ここに繰り返しの処理を書く.
}

拡張for文

拡張for文と呼ばれる書き方.実質的には,Iteartor型を利用する方法と同じ. コンパイラがIterator型を利用する方法に変換してコンパイルする.最近はこの書き方が多い.

for(String item: list){
    // ここに繰り返しの処理を書く.
}

サンプルプログラム

コマンドライン引数に受け取ったString型の値を全て ArrayListに入れ,ArrayListから順に取り出し,出力するプログラムを書きましょう.

import java.util.ArrayList;
public class ArgsPrinter2{
  void run(String[] args){
    ArrayList<String> list = this.buildList(args);
    this.printList(list);
  }
  ArrayList<String> buildList(String[] array){
    ArrayList<String> arrayList = new ArrayList<>();
    for(Integer i = 0; i < array.length; i++){
      arrayList.add(array[i]);
    }
    return arrayList;
  }
  void printList(ArrayList<String> arrayList){
    for(String item: arrayList){
      System.out.println(item);
    }
  }
  public static void main(String[] args){
    ArgsPrinter2 printer = new ArgsPrinter2();
    printer.run(args);
  }
}

このプログラムを書き,コンパイル,実行してみましょう.実行時にコマンドライン引数に値を指定して実行してみましょう.

例題

50個のDouble型の 0〜1の乱数をArrayListに入れて,出力してみましょう. クラス名は,DoubleValuePrinterとしてください. 乱数の発生方法は,Big & Smallを参照してください. なお,リストを生成する部分,出力する部分を別のメソッドにしてみましょう. クラス名はDoulbeValuePrinterとしましょう. 

完成すれば,コマンドライン引数で発生させる乱数の個数を指定できるようにしてください. 指定されなければ,50個としてください.

出力例

$ java DoubleValuePrinter
  1: 0.24279591112755294
  2: 0.7216985840426494
  3: 0.5978665614812361
... 途中省略
 48: 0.15288776496056167
 49: 0.8335019950136539
 50: 0.8114170360899468
$ java DoubleValuePrinter 3
  1: 0.20585052641970603
  2: 0.578743233682112
  3: 0.107553196759134
例題の解答例