第14章 コレクション(OCJP-Bronze範囲外)

はじめに

コレクションはたくさんのオブジェクトを扱うためのデータ構造です。そのため型の扱いが面倒でしたが、Java1.5から総称型(ゼネリックス)という新しい仕組みが導入され、簡単になりました。コレクションと総称型を使うことによりプログラミングの生産性が飛躍的に向上します。この章ではコレクションと総称型(ゼネリックス)の基礎を理解しましょう。

目次

  • コレクションとは
  • List系
  • Set系
  • Map系
  • Queue系
  • 総称型(ゼネリクス)の利用
  • AutoboxingとUnboxing(Auto-unboxing)
  • 章のまとめ
  • 追加テスト

目標

Arrayクラスの使い方を理解し、コーディングができるようになること。

コレクションとは

コレクションとは、複数のオブジェクトを扱うためのデータ構造のことです。複数のオブジェクトを格納して出し入れすることができます。以前学習した配列に強力な機能が加わったものだと思ってください。
コレクション内に格納されたオブジェクトはエレメントと呼びます。また、コレクションのデータ構造のタイプとしては4つあります。

コレクションのタイプ

タイプ 説明
List 先頭のエレメントから最後のエレメントまでが直線的に連結している構造。
Set 同じものは含まないという構造。エレメント間のつながりはない。
Map エレメントがそれぞれキーを持ち、キーの値で検索できる。エレメント間のつながりはない。
Queue 「先入先出し」、「先入れ後出し」など一定の順序で出し入れできる

これらは、線形リストやハッシュ、スタック、キューなどと呼ばれている幾つかの有用なデータ構造を、Java言語で簡単に利用できるよう、ひとつのパッケージに収めたものです。JDKではjava.utilパッケージの中にあります。

また、コレクションを便利に使うためのユーティリティとして以下の2つのクラスがあります。

クラス名 説明
Collections 主にListをソート(並び替え)したり、検索したりする。
Arrays 基本データ型を含む配列型についてソートと検索を行う

コレクションのインターフェース・クラス図

コレクションを構成するインターフェースとクラスの一覧は以下です。

各コレクションクラスがエレメントとして格納するのはオブジェクトの参照です。

List系

List系のクラスはインターフェースとして「List」を実装します。 配列のように「要素番号」でエレメントにアクセスします。エレメント数はあらかじめ指定する必要はなく、エレメントが増えるに従って自動的に拡大します。主なList系のクラスとしては次の3つがあります。

  • ArrayListクラス
    List系の代表格で、最もよく利用されるクラス。エレメントサイズが自動拡張される配列のようなもの。

  • LinkedListクラス
    挿入や削除を高速に処理できるリスト構造に加えて、キュー構造やスタック構造の基本機能を持つ双方向リスト。

  • Vectorクラス
    ArrayListと同じ構造。マルチスレッド環境で安全に使用できる。ArrayListより効率が劣る。

Set系

Set系のクラスはインターフェースとして「Set」を実装しています。Setは同じエレメントを2つ持たないという特徴があります。addメソッドでエレメントを取り出すことができますが、同じエレメントを重複して格納することはできません。主なSet系のクラスとしては次の3つがあります。

  • HashSetクラス
    Set系の代表格。エレメントは相互に連結していないので順序はない。何か特定の順序で取り出せるといったことはできない。

  • LinkedHashSet
    エレメントは相互に連結しており、挿入順に順序がつくSetです。

  • TreeSet
    辞書順や数の大小などエレメントが自然順序で順序づけられる。

Map系

Map系のクラスはインターフェースとして「Map」を実装しています。格納するとき、「キー」と「エレメント」をセットにして格納します。高速に検索できるように、「キー」はハッシュ関数で位置を示す特殊な値に変換されます。後から「キー」を指定してエレメントを高速に取り出すこともできます。主なMap系のクラスとしては次の4つがあります。

  • HashMap
    Mapの代表格でよく利用されるクラス。

  • LinkedHashSet
    挿入した順序を保持してくれるMap。「キー」を全部取り出して先頭から処理する場合も挿入順に「キー」を取り出すことができる。

  • TreeMap
    「キー」の自然順序で順序づけられる。あるキーよりも大きいキーを持つエレメント」のような検索が可能。

  • Hashtable
    HashMapと同じだが、マルチスレッド環境で安全に利用できる。HashMapより効率が劣る。

Queue系

Queue系のクラスはインターフェースとして「Queue」を実装しています。最初に入力したデータが最初に出力されるものを「キュー」といいます。最後に入力したデータが最初に出力されるものを「スタック」といいます。代表格に「LinkedList」があります。(List系)

  • PriorityQueue
    自然順序または特に定義した順序により、優先度の高いものを先に出力する「キュー」です。

総称型(ゼネリクス)の利用

リスト構造は銭型リストともいい、各エレメントが次のエレメントへのポインタを持っている構造です。配列では1つの要素を削除すると、空いた場所を詰めるため全体をシフトする必要がありますが、リストではポインタをつなぎ替えるだけで削除が可能です。

リストではポインタをたどることにより、次のエレメントを探すことができます。以下のようにポインタをもうひとつ持つと、逆方向にもたどることができる双方向リストになります。

ArrayListクラスは配列と同じ構造をしていますが、上記のような双方向リストを持っています。その為、配列よりも高速にランダムにアクセスすることが可能です。

ArrayListは要素数を初めから指定しなくても、追加する際に自動的に拡張する特徴があります。またJava1.5からはオブジェクトの型を安全に操作できるように総称型(ゼネリクス)が導入されました。

Object型を出し入れする書き方(Java1.4まで)

ArrayListを利用するためには、最初にnew演算子でArrayListオブジェクトを作成します。

ArrayList ary = new ArrayList();

ArrayListクラスの使い方は以下のAPI仕様書に書いてあります。
JavaSE 1.4(Array Listクラス)

※API(Application Programing Inerface)とは、メソッドの戻り値や引数構成が書かれた説明書です。

APIを確認すると、メソッドの引数や戻り値がObject型になっていることをが確認できます。
Object型とは全てのクラスのスーパークラスなので、どんな型でも扱えることを意味します。

メソッド 説明
void add(int index, Object element) リストの指定された位置に、指定されたエレメントを挿入します。
boolean add(Object o) リストの最後に、指定されたエレメントを追加します。
Object get(int index) リスト内の指定された位置にあるエレメントを返します。

新たにDogクラスArrayExec01クラスを作成し、以下入力してください。

Dog.java

package arrayPk;

class Dog {
String name;
public Dog(String name){
this.name = name;
}
}

ArrayExec01.java

package arrayPk;

import java.util.ArrayList;

//実行するためのクラス
public class ArrayExec01 {

public static void main(String[] args) {

ArrayList ary = new ArrayList();
ary.add( new Dog("コロ")); //リストへ追加
ary.add( new Dog("ポチ")); //リストへ追加

Dog d = (Dog)ary.get(0); //リストから0番地のエレメントを取り出してキャストしてから代入する
System.out.println(d.name);

}
}

実行結果

Dog d = (Dog)ary.get(0);

上記に着目してください。getメソッドのAPI仕様を確認してみるとわかりますが、getメソッドの戻り値はObject型です。その為、Dog型の変数に代入するためには、ダウンキャストが必要です。
ダウンキャストを忘れるとコンパイルエラーになってしまいます。プログラマーが使うたびにダウンキャストしなければならない為、少々面倒です。

Object型を出し入れする書き方(Java1.5以降)

java1.5以降からは総称型(ゼネリクス)が追加されました。総称型の仕組みは、クラスやメソッドを定義するときは具体的な型名の代わりにEとかTなどと書いておき、使用するときに具体的な型名を指定するというものです。
java1.5以降のAPIの仕様を確認すると以下になっています。

JavaSE 7(ArrayListクラス)

コレクションの各クラスは、メソッドの引数型や戻り値型が全て総称型になります。Eを具体的な型に直すのはこのクラスを実際に使って何かをするときです。プログラムを書く時点で、ユーザーが「Eは●●●型として扱う」と宣言します。

例)ArrayListクラスのオブジェクトを作成するとき(Dog型をエレメントとして扱うとき)

ArrayList<Dog> ary = new ArrayList<>();

例)メソッドの引数などにArrayList型(Dog型をエレメントとして指定)を指定するとき

public void abcMethod( ArrayList<Dog> a ){・・・}

例)メソッドの戻り値にArrayList型(Dog型をエレメントとして指定)を指定するとき

public ArrayList<Dog> abcMethod( ){・・・}

ArrayListを使用するときにクラス内の「E」にDog型を指定することで、Dog型しかArrayListのエレメントとして出し入れ出来ないようにコンパイラへ伝えることができます。コンパイラは指定された型であるかコンパイル時にチェックできるようになります。この仕組みをタイプセーフといいます。

新たにArrayExec02クラスを作成し、以下入力してください。

ArrayExec02.java

package chapter14;

import java.util.ArrayList;

//実行するためのクラス
public class ArrayExec02 {

public static void main(String[] args) {

ArrayList<Dog> ary = new ArrayList<>();
ary.add( new Dog("コロ")); //リストへ追加
ary.add( new Dog("ポチ")); //リストへ追加

Dog d = ary.get(0); //リストから0番地のエレメントを取り出してキャストしてから代入する
System.out.println(d.name);

}
}

実行結果

上記、総称型を利用することでinstanceOfによる型チェックやダウンキャストが不要になり、安全かつスマートに記述することができるようになりました。

ArrayListの使い方

ArrayListクラスはListインターフェースを実装する他に独自にpublicメソッドを複数持ちますが、あまり使われません。これらは他のコレクションクラス(MapクラスやSetクラスなど)にも言えますが、実装したインターフェースのメソッドのみを使うことで十分な処理ができます。
その為、以下のように利用される場合が多いです。

List<String> ls = new ArrayList<>();    //エレメントにString型のリストを扱う場合

Listインターフェースの主なメソッドは以下です。

メソッド 説明
boolean add(E e) 指定されたエレメントをこのリストの最後に追加します。
void add(int index, E element) このリスト内の指定された位置に、指定されたエレメントを挿入します。
E get(int index) このリスト内の指定された位置にあるエレメントを返します。
boolean contains(Object o) 指定のエレメントがこのリストに含まれている場合に true を返します。
E set(int index, E element) このリスト内の指定された位置にあるエレメントを、指定されたエレメントに置き換えます。
int indexOf(Object o) 指定されたエレメントがこのリスト内で最初に検出された位置のインデックスを返します。
boolean remove(Object o) 指定されたエレメントがこのリストにあれば、その最初のものをリストから削除します。

※Listインターフェースの他のメソッドは以下のAPIから確認ください
JavaSE 7(Listインターフェース)

また、リストの要素の並び替え(ソート)するのに使われるCollectionクラスの主なクラスメソッドには以下があります。

メソッド 説明
boolean sort(List list) リストを自然順序で並び替える。
boolean sort(List list, Comparator<? super T> c) リストを指定の順序で並び替える。
void reverse(List<?> list) リストのエレメントを逆順にする。

※Collectionクラスの他のメソッドは以下のAPIから確認ください
JavaSE 7(Collectionクラス)

新たにArrayExec03クラスを作成し、以下入力してください。

ArrayExec03.java

package chapter14;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

//実行するためのクラス
public class ArrayExec03 {

public static void main(String[] args) {

List<String> ls = new ArrayList<>();

ls.add("Monday"); //追加
ls.add("Wenesday"); //追加
ls.add("Thursday"); //追加
ls.add("Friday"); //追加
ls.add("Saturday"); //追加
ls.add("Sunda"); //追加
ls.add("Sunday"); //追加
disp(ls);

ls.add(1,"Tuesday"); //1番目のエレメントへ追加
disp(ls);

ls.set(2,"Wednesday"); //2番目のエレメントを変更
disp(ls);

ls.remove(6); //6番目のエレメントを削除
disp(ls);

Collections.sort(ls); //並び替え
disp(ls);
}

//リストの件数と内容を表示
public static void disp(List<String> ls){
System.out.print("件数:" + ls.size() + "\t");
for(String str : ls){
System.out.print( str + " ");
}
System.out.println("");
}
}

実行結果

AutoboxingとUnboxing(Auto-unboxing)

ArrayListをはじめとしたコレクションクラスでは、オブジェクトの参照を出し入れするクラスで、intやdoubleなどの基本データ型を格納することができません。基本データ型の値は、基本データ型に対応するラッパークラスと呼ばれるクラス型を使うとコレクションクラスで扱うことができます。

※ラッパークラスはjava.langパッケージに所属しています。

基本データ型 ラッパークラス
char Character
byte Byte
short Short
int Integer
long Long
float Float
double Double
boolean Boolean

基本データ型をラッパークラスに自動変換することをAutoboxingといい、その逆でラッパークラスを基本データ型へ自動変換することをUnboxing(Auto-unboxing)といいます。

新たにAutoBoxingExecクラスを作成し、以下入力してください。

AutoBoxingExec.java

package autoboxingPk;

//実行するためのクラス
public class AutoBoxingExec {

public static void main(String[] args) {

int i = 12345;
Integer j = i; //int型からInteger型に変換(Autoboxing)
int k = j; //Integer型からint型に変換(Unboxing)

disp(j); //int型からInteger型に変換(Autoboxing)

}

static void disp(Integer i) {
System.out.println(i);
}
}

実行結果

AutoboxingとUnboxing(Auto-unboxing)は、上記の「disp(j)メソッド」のように、メソッドへの引数の受け渡しの際にも使用することができます。

章のまとめ

以下の要点をしっかり理解してから次の章へ進んでください。

  • コレクションはオブジェクト(の参照)を格納するためのデータ構造である。
  • リスト、セット、マップ、キューの4種類がある。
  • ソートなどのためにCollectionsクラスとArraysというユーティリティークラスがある。
  • コレクションは総称型に対応している為、型名の後には<型>が必要である。
  • ArrayListクラスはサイズを拡張できる配列である。
  • 通常、「List ls = new ArrayList<>();」のようにインターフェース型の変数を利用する。
  • 基本データ型とラッパークラス間は自動的に変換される。

追加テスト