はじめに
コレクションはたくさんのオブジェクトを扱うためのデータ構造です。そのため型の扱いが面倒でしたが、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.javapackage arrayPk;
class Dog {
String name;
public Dog(String name){
this.name = name;
}
}
ArrayExec01.javapackage 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の仕様を確認すると以下になっています。
コレクションの各クラスは、メソッドの引数型や戻り値型が全て総称型になります。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.javapackage 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 |
リストを自然順序で並び替える。 |
boolean sort(List |
リストを指定の順序で並び替える。 |
void reverse(List<?> list) | リストのエレメントを逆順にする。 |
※Collectionクラスの他のメソッドは以下のAPIから確認ください
JavaSE 7(Collectionクラス)
新たにArrayExec03クラス
を作成し、以下入力してください。
ArrayExec03.javapackage 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.javapackage 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<>();」のようにインターフェース型の変数を利用する。 - 基本データ型とラッパークラス間は自動的に変換される。