コレクションフレームワーク
この記事では、java.utilをして利用できる便利なクラスを学んで行きます。
コレクションフレームワークとは
そもそも、フレームワークとは概念的な意味で、
- 「何かの枠組み」
- 「システム開発を楽に行えるように用意された、プログラムとかのひな形」
のことです。つまり、
コレクションフレームワーク = 便利な機能をまとめたもの
みたいな考え方でいいと思います。
これらの 用意された便利な機能を、柔軟に扱うことを考えられているのがコレクション・フレームワークです。
Javaのコレクションフレームワーク
(他にも両端からしか値を出し入れできないDequeというのもあるんですが、用途が限られるので今回は割愛)。
java.utilパッケージとは
java.utilは Javaのフレームワークの一種で、Javaで使われる 、以下のような データを表現するのに必要なデータ構造が一通り揃っています。
- 配列操作
- イベント
- モデル
- 日付および時間...
その中でも代表的な「配列操作」をするために3つの便利なクラスがあります。それが、**「List」, 「Set」, 「Map」**です。
- リスト(List) 値が順番に並んで格納したもの
- セット(Set) 順序があるとは限らず格納され、同じ値のものが1つだけのもの
- マップ(Map)ペアで対応づけて格納し、キーごとに値が対応したもの
【List】順序を持ったコレクション
Listとは
リストは配列によく似た性質を持つ「0から始まるインデックスごとにデータが入ったもの
」です。
インデックスによって要素を挿入したり要素にアクセスしたりする位置を自由に変更することができます。
List と 配列 の違い
Listは、持っている特長が 配列 に非常によく似ているものの、Javaの「 通常の配列操作とは少々異なります。 」
- 【配列】
- 最初にサイズを決めるため、「後からサイズを拡張することができない」
- 【Listクラス】
- 要素を追加した分だけ「自動的にサイズが拡張される動的な配列」を作ることができる。
- Listクラスのメソッドを使うことで「値を追加したり途中に挿入したり、削除することができる。」
Listの定義の仕方
Listの基本的な定義の仕方はこちらになります。
リストインターフェース名<型> インスタンス名 = new コンストラクタ名<型>();
ちなみに、Java 1.4以降 ダイヤモンド演算子"<>"を用いて以下のように書くことが可能ですので、基本この型で書いていいと思います。
// ArrayListに<型>を指定しない
List<String> sampleList = new ArrayList<>();
ArrayList(可変長配列)
ArrayList とは
Listインタフェースを実装したコレクションクラスです。
「Array」 という名にあるように 「配列のような感覚」で扱うことができる。
JavaのArrayListは大きさが決まっていない配列のようなものとイメージしてください。ArrayListは、複数の値を管理する時に使います。
ArrayListの特徴
メリットはこちら
- ArrayListクラス は配列でリストを実装しており、「添え字による要素へのアクセス」が高速です。
- 要素の「追加」が、新たな要素を末尾へ加えるだけなので楽
短所はこちら
- 配列の途中の位置への要素の「挿入」や「削除」に関しては、 挿入・削除した位置以降の全ての要素の位置を移動させるという処理を行う必要があるため低速になる。
ArrayList の宣言・初期化
書き方はこちらです。
// 基本の定義の仕方
ArrayList<型> 変数名 = new ArrayList<型>();
// サンプルコード
List<String> sampleList = new ArrayList<String>();
## ArrayListクラスの使用例
2パターンありますので、見ていただきます。
やってることは同じですから実行結果は変わりませんのでお好みで。
```java
package practiceListClass;
//utilパッケージのArrayListクラスをimport
import java.util.ArrayList;
//utilパッケージのListクラスをimport
import java.util.List;
public class ArrayAboutMain {
public static void main(String args[]) {
// ここから =====================================
List<String> sampleList = new ArrayList<String>();
sampleList.add("1回目のリストに格納");
sampleList.add("2回目のリストに格納");
// ここまで =====================================
// リストに格納した全要素を順番に出力
for (int i = 0; i < sampleList.size(); i++) {
System.out.println(sampleList.get(i));
}
}
}
2パターン目。addの書き方が少し違うバージョン。
// ここから =====================================
List<String> sampleList = new ArrayList<String>();
sampleList = new ArrayList<String>() {
{
add("1回目のリストに格納");
add("2回目のリストに格納");
}
};
// ここまで =====================================
実行結果はこちら
1回目のリストに格納
2回目のリストに格納
上記のコードを解説すると
生成したArrayListクラスのインスタンスを、Listインタフェース型の変数sampleListに保持しています。
Listインタフェースでは、
- 要素の追加: addメソッド
- 値を取り出すとき: getメソッド
を使用してください。
LinkedList(連結リスト)
LinkedListとは
LinkedListは、要素同士を前後双方向のリンクで参照するリンクリストを表します。
LinkedListの特徴
- 要素の挿入/削除はリンクの付け替えで済むため、ArrayListに較べても高速
- インデックス値によるランダムなアクセスは苦手
LinkedListクラス はリスト構造を使用して実装しています。
このため、「添え字による要素へのアクセス」は、毎回先頭から順番に要素をたどっていきながら
目的の位置を探す(添え字の番号まで移動していく)必要があるため、低速です。
LinkedList の宣言・初期化
// 基本の定義の仕方
LinkedList<型> 変数名 = new LinkedList<型>();
// サンプルコード
List<String> sampleList = new LinkedList<String>();
LinkedList の使用例
ArrayListとやってることは変わりませんが、コピペ用でこちらも載せておきます。
こちらも2パターンありますので、見ていただきます。
やってることは同じですから実行結果は変わりませんのでお好みで。
package practiceListClass;
//utilパッケージのArrayListクラスをimport
import java.util.ArrayList;
//utilパッケージのListクラスをimport
import java.util.List;
public class ArrayAboutMain {
public static void main(String args[]) {
// ここから =====================================
List<String> sampleList = new LinkedList<String>();
sampleList.add("1回目のリストに格納");
sampleList.add("2回目のリストに格納");
// ここまで =====================================
// リストに格納した全要素を順番に出力
for (int i = 0; i < sampleList.size(); i++) {
System.out.println(sampleList.get(i));
}
}
}
2パターン目。addの書き方が少し違うバージョン。
// ここから =====================================
List<String> sampleList = new LinkedList<String>();
sampleList = new LinkedList<String>() {
{
add("1回目のリストに格納");
add("2回目のリストに格納");
}
};
// ここまで =====================================
実行結果はこちら
1回目のリストに格納
2回目のリストに格納
上記のコードを解説すると
生成したArrayListクラスのインスタンスを、Listインタフェース型の変数sampleListに保持しています。
Listインタフェースでは、
- 要素の追加: addメソッド
- 値を取り出すとき: getメソッド
を使用してください。
List系クラス チートシート
各クラスの特徴を踏まえて、用途に応じてクラスを選択することが Listインタフェースを使いこなす
ポイントとなります。
以下のように、
- 挿入/削除操作が多い状況ではLinkedList
- それ以外の場合はArrayList
という使い分けになると思います。
クラス | 概要 | 長所 | 短所 | 使う場面 |
---|---|---|---|---|
ArrayList | 複数の値を管理する時に使う、「大きさの決まっていない配列のようなもの」 | 添え字による要素へのアクセス」が高速 要素の「追加」が、新たな要素を末尾へ加えるだけなので楽 |
要素を追加するスピードが遅い | 配列内の要素に対してランダムなアクセスを必要とし、配列内の要素に対して挿入/削除の操作があまり必要ない場合 |
LinkedList | 要素同士を前後双方向のリンクで参照するリンクリストを表します。 | 要素の挿入/削除はリンクの付け替えで済むため高速 | ・特定の要素にアクセスするスピードが遅い ・インデックス値によるランダムなアクセスは苦手 |
要素数が多くて、且つ要素の挿入・削除を頻繁に行うことが予想 でき、配列内の要素に対してランダムなアクセスを必要としない場面 |
【Map】キーと値とのマッピングを保持する
連想配列を取り扱うものに Map というコレクションがあります。
Mapとは
Mapは「キー」と「値」の二つの要素から成り立っています。
「 キー は値につける名前のようなもの 」で、値ひとつひとつに「キー」が存在しており、「キー」と「値」がペアになっているのがMapの特徴です。
キーは一意(同じものが複数存在しない)ですが、値は同じものが複数あってもいいです。
Map系クラスの特徴
マップはMapインターフェースを元に実装されていて、**HashMap、TreMap、LinkedHashMap **の3つがよく使われます。
Mapクラス | 概要 |
---|---|
HashMap | ハッシュを使ったMapのデフォルト。 要素数によらない高速な検索ができます。 順序が保持されていないので目的の要素のキーを指定してそれに紐づく値を取得するのが早い |
TreeMap | 順序がキーの順番になっている。キーが数値の場合は小さい順に要素が保持されます。 キーが文字列の場合は、文字コードの順(辞書順、アルファベット順)に要素が保持される。 |
LinkedHashMap | キーの挿入順を保持する。 コンストラクタの引数の指定によって、 挿入順ではなくアクセス順を保持することもできます。デフォルトは挿入順です。 要素を追加(put)した順番に、そのままの順番で要素が保持されます。 または、コンストラクタの引数に順序付けモードを指定して、アクセス順(getした順番)に保持されるようにすることもできます。 |
Mapの定義方法
Mapの初期化は以下のようにして行います。
ArrayList等と同様の記述のしかたで書くことが出来、ダイヤモンド演算子を用いての記述も可能です。
基本の定義の仕方がこちらです。
マップインターフェース名<型> インスタンス名 = new コンストラクタ名<型>();
HashMap の定義
// HashMap(Mapの代わりに左辺を同じHashMapにしても可)
Map<String, Integer> hashMap = new HashMap<String, Integer>();
TreeMap の定義
// TreeMap(Mapの代わりに左辺を同じTreeMapにしても可)
Map<String, Integer> treeMap = new TreeMap<String, Integer>();
LinkedHashMap の定義
// LinkedHashMap(Mapの代わりに左辺を同じLinkedHashMapにしても可)
Map<String, Integer> treeMap = new LinkedHashMap<String, Integer>();
Map の使用例
まずはこちらのコードを御覧ください、説明はコメントアウトで記述してありますので、詳し異解説は下記に書きます。
package about_map;
// Mapのインポート
import java.util.HashMap;
import java.util.Map;
public class MainAboutMap {
public static void main(String[] args) {
// ここから =====================================
// 1 newの時にキーと値の両方を型として指定
Map<String, String> sampleHashMap = new HashMap<String, String>();
// 2「put」を使用して値を追加する
sampleHashMap.put("ニビジム", "タケシ");
sampleHashMap.put("ハナダジム", "カスミ");
sampleHashMap.put("クチバジム", "マチス");
// ここまで =====================================
// 3 「get」でキー値を指定して出力
System.out.println(sampleHashMap.get("ニビジム"));
System.out.println(sampleHashMap.get("ハナダジム"));
System.out.println(sampleHashMap.get("クチバジム"));
System.out.println(sampleHashMap.get("タマムシジム")); //null になる
}
}
出力結果
タケシ
カスミ
マチス
null
解説
こちらのコードの手順は、、
- Mapをインポートする
- newの時にキーと値の両方を型として指定
- 「put」を使用して値を追加する
- 「get」でキー値を指定して出力
といった記述をしています。
① newの時にキーと値の両方を型として指定
生成したHashMapクラスのインスタンスをMap[インタフェース]型の変数sampleHashMapに保持。
// 1 newの時にキーと値の両方を型として指定
Map<String, String> sampleHashMap = new HashMap<String, String>();
② 「 put 」を使用して値を追加する
Map[インタフェース]では値の追加は「 add 」ではなく、
「 put 」という名前のメソッドで要素の追加を行います。
putメソッドには引数を2つ指定します。
第1引数がキー、第2引数がキーに紐づく値です。
// 2「put」を使用して値を追加する
sampleHashMap.put("ニビジム", "タケシ");
sampleHashMap.put("ハナダジム", "カスミ");
sampleHashMap.put("クチバジム", "マチス");
③ 「get」でキー値を指定して出力
値を取り出すときは、 getメソッド を使用します。
Map.getを使うと、Mapにputされているキー・値の対応付けの中から、キーに対応する値を取り出せます。キーがputされていない場合はnullが戻ります。
// 3 「get」でキー値を指定して出力
System.out.println(sampleHashMap.get("ニビジム"));
System.out.println(sampleHashMap.get("ハナダジム"));
System.out.println(sampleHashMap.get("クチバジム"));
System.out.println(sampleHashMap.get("タマムシジム")); //null になる
【補足】 put の別パターン
putする時の別パターンでこちらの方法でも値の追加が可能です。
コメントアウト部分を上記のコードの部分を境に入れ替えて使用してください。
やっていることは上記のコードと同じなので、出力結果も同じです。
// ここから =====================================
// 1 newの時にキーと値の両方を型として指定
Map<String, String> sampleHashMap = new HashMap<String, String>();
// 2「put」を使用して値を追加する
sampleHashMap = new HashMap<String, String>() {
{
put("ニビジム", "タケシ");
put("ハナダジム", "カスミ");
put("クチバジム", "マチス");
}
};
// ここまで =====================================
【Set】 重複要素のないコレクション
Setとは
Setは、Listのと同様に 「データをまとめたもの」になります。
ですが、双方にはこのような違いがあります。
- Mapのようなキーと値の関連付けをしない
- インデックスが存在しない
- 同じ値のものは2つ以上持てない
SQL にある 「DISTINCT」や、「ORDER BY」 のようなイメージだと思います。
Setの使い所
以上に書いた「 同じ値のものは2つ以上持てない 」などの特性をいかし、このような使い所が考えられます。
- 商品を安い順に並べる
- 会社の部署はいくつあるのか調べる
- ポケモンのタイプは何種類あるか調べる
- この学校の生徒はどこの出身者がいるかみる。
といったとき Set が役立つのではないかと思います。
Setの種類
Setインタフェースを実装するものには、HashSetクラス
, TreeSetクラス
, LinkedHashSetクラス
などがあります。
それぞれの配列の並べ方は以下にです。
クラス | 概要 | 要素の並び ( 1 ~ 5 の値が格納されている場合) |
---|---|---|
HashSet | ランダム | 2, 4, 3, 5, 1 |
TreeSet | 昇順 | 1, 2, 3, 4, 5 |
LinkedHashSet | 追加した順 | 1, 3, 4, 5, 2 |
Setの定義
Set の基本の型
Setも List、Map 等と同様の書き方ができます。
基本的な書き方は以下です。
セットインターフェース名<型> インスタンス名 = new コンストラクタ名<型>();
HashSet、TreeSet、LinkedHashSet はそれぞれ次のように定義します。
HashSet の定義
※Setの代わりに左辺を同じHashSetにしてもOK
Set<String> hashSet = new HashSet<String>();
TreeSet の定義
※Setの代わりに、左辺を同じTreeSetにしてOK
Set<String> treeSet = new TreeSet<String>();
LinkedHashSet の定義
※Set の代わりに、左辺を同じLinkedHashSet にしてもOK
Setサンプルコード
以下のコードは、各種setを用いて出力結果を比べたものです。
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.TreeSet;
public class Main {
public static void main(String[] args) {
HashSet<String> vegetableSet = new HashSet<String>();
//適当に値を入れていく
vegetableSet.add("大根");
vegetableSet.add("人参");
vegetableSet.add("ブロッコリー");
vegetableSet.add("すいか");
vegetableSet.add("かぼちゃ");
System.out.println("HashSetはランダムで表示"する");
//拡張for文 で変数vs を vegetableSet から一つづつ取り出す
for (String vs : vegetableSet) {
System.out.println(vs);
}
TreeSet<String> fruitsSet = new TreeSet<String>();
//適当に値を入れていく
fruitsSet.add("もも");
fruitsSet.add("ミカン");
fruitsSet.add("りんご");
fruitsSet.add("梨");
System.out.println("TreeSet は昇降順に表示する");
for (String fs : fruitsSet) {
System.out.println(fs);
}
LinkedHashSet<String> fishSet = new LinkedHashSet<String>();
fishSet.add("カツオ");
fishSet.add("カンパチ");
fishSet.add("鯵");
fishSet.add("ブリ");
System.out.println("LinkedHashSet は追加した順に表示する");
for (String fs : fishSet) {
System.out.println(fs);
}
TreeSet<String> meetSet = new TreeSet<String>();
//適当に値を入れていく
meetSet.add("牛");
meetSet.add("豚");
meetSet.add("鹿");
meetSet.add("豚");
meetSet.add("牛");
meetSet.add("牛");
meetSet.add("牛");
meetSet.add("牛");
meetSet.add("豚");
meetSet.add("豚");
meetSet.add("羊");
meetSet.add("豚");
meetSet.add("鹿");
meetSet.add("羊");
meetSet.add("羊");
meetSet.add("豚");
meetSet.add("羊");
meetSet.add("豚");
meetSet.add("鹿");
meetSet.add("羊");
System.out.println("set には同じ値が入らないので重複分は出力されない");
for (String ms : meetSet) {
System.out.println(ms);
}
}
}
出力結果
HashSetはランダムで表示
人参
大根
すいか
かぼちゃ
ブロッコリー
TreeSet は昇降順に表示する
もも
りんご
ミカン
梨
LinkedHashSet は追加した順に表示する
カツオ
カンパチ
鯵
ブリ
set には同じ値が入らないので重複分は出力されない
牛
羊
豚
鹿
これらをうまく利用することで、システムのいろいろな表現ができそうです。
実際に手を動かしながら覚えていきたいと思います。