Java学習ノート_140717（コレクション）

# コレクション

コレクションフレームワークとは、複数のデータの集合を扱うための、Java標準ライブラリで提供されているフレームワークのこと。

| 名前 | 表記 | 説明 |インターフェイス |
|---|---|---|---|---|
| リスト | List | 順番に値を並べたもの。可変長。配列と類似。 | java.util.List |
| キュー | Queue | 要素の追加と取り出し用の便利なメソッドがある。 | java.util.Queue |
| セット | Set | ユニークな値の集合（重複して同一の値を持てない）。 | java.util.Set |
| マップ | Map | キーごとに値を持つバリュー（値）をもつ集合。キーは一意。 | java.util.Map |

## コレクションフレームワークの実装クラス

たくさんある。ArrayListとか、LinkedList、Vector、HashSet,HashMap。

用途に応じて適切な機能を持ったクラスを利用する（マルチスレッドでの待ち行列の処理などはBlocking Queueインターフェイスを実装したほうがいいなど）。


## ジェネリックス

ListやMapを使用する際に、ジェネリックスを用いることで、明示的に型を指定して、都度型キャストする手間や、例外（ClassCastException）がスローされないようにする。要はコード内の安全性を保つ仕組み。


## データの処理方法、FIFO、LIFO

FIFO（First In First Out） : FIFO用のデータ構造はQueueと呼ばれる
LIFO（Last In First Out） : LIFO用のデータ構造はStackと呼ばれる

## Listの利用

ListをListArrayを通して利用してみる。要素数はlengthじゃなくて、size()で、要素へのアクセスはget(num)。

```java:ListSample.java
import java.util.ArrayList;
public class ListSample {
	public static void main(String[] args) {
		//ジェネリックスで型引数を指定する
		ArrayList<String> list = new ArrayList<String>();
		list.add("Tokyo");
		list.add("London");
		list.add("Paris");
		list.add("New York");
		
		//getメソッドの返り値はObject型なので、型キャストする
		String item = (String)list.get((int)(Math.random()*list.size()));
		System.out.println("水曜どうでしょうの次の目的地は" + item + "です。");
		
		//リストの一覧を表示
		for(int i = 0; i < list.size(); i++){
			//型引数を指定したので、指定しない場合に必要な以下の型キャストは不要
			//String item2 = (String)list.get(i);

			String item2 = list.get(i);
			
			//System.out.println("[" + i  +"] : " + item2);
			System.out.printf("[%02d] : %s%n", i,item2);
		}
		
		
	}
}
```

ジェネリッククラスの宣言側の型引数は仮型引数（formal type parameter）、クラスを利用する側で指定する型引数は実型引数（actual type argument）と呼ばれる。クラス宣言の中にあるEが仮型引数、利用するStringが実型引数。EはElementのE。HashMapなどは複数の仮型引数を持つ（キーとバリュー）。

ジェネリックメソッドとかジェネリックコンストラクタとかジェネリック医薬品とか色々ある。

**コレクションフレームワークはインターフェイスで利用するのが一般的**らしい。以下は自作クラスのサンプル。for文の書き方を変えてみた。

```java:ClassUsesListSample.java
import java.util.ArrayList;
import java.util.List;

class Man{
	String name;
	int age;
	public Man(String name, int age) {
		this.name = name;
		this.age = age;
	}
	
	void greeting(){
		System.out.println("Hello, My name is " + name + ". I'm " + age + " years old.");
	}
}

class ClassUsesListSample {
	public static void main(String[] args) {
		List<Man> list = new ArrayList<Man>();
		list.add(new Man("Taro Hanamura", 30));
		list.add(new Man("Tomonori Arakawa", 150));
		list.add(new Man("Charles Darwin", 450));
		
		for(Man item : list){
			item.greeting();
		}
	}
}

```

## Setの利用

Setは重複のない値のコレクション。add(),iterator(),contains()などのメソッドがある。

SetをHashSetを通して利用してみる。

```java:SetSample.java
import java.util.HashSet;
import java.util.Set;
public class SetSample {
	public static void main(String[] args) {
		Set<String> set = new HashSet<String>();
		set.add("Tokyo");
		set.add("Osaka");
		set.add("Nagoya");
		set.add("Hakata");
		set.add("Sendai");
		set.add("Sapporo");
		
		for(String item : set){
			System.out.println(item);
		}
	}

}

```

注意するべきは、出力結果。Setには追加した要素の順序は保持されない。順序を保持したい場合はSortedSetを利用する。・・・と書いてあったので使おうと思ったけど何も考えないで書いたらコンパイルエラーになる。ドキュメントなどを見ると、以下のように書いたりすればいいらしい。

```java:SetSample.java
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
public class SetSample {
	public static void main(String[] args) {
		SortedSet<String> set2 = new TreeSet<String>();
		set2.add("Tokyo");
		set2.add("Osaka");
		set2.add("Nagoya");
		set2.add("Hakata");
		set2.add("Sendai");
		set2.add("Sapporo");
		
		//アルファベット順にソートされる
		for(String item : set2){
			System.out.println(item);
		}
	}
}
```

こっちも。

```java:CollectionSample.java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;

public class CollectionSample {
	public static void main(String[] args) {
		//Mapは同じキーが格納された場合上書きされる
		Map hashmap = new HashMap();
		hashmap.put("1", "hanamura");
		hashmap.put("2", "arakawa");
		hashmap.put("1", "justin");
		System.out.println(hashmap.toString());

		//keyの番号順にソート
		SortedMap treemap = new TreeMap();
		treemap.put(1, "kimura");
		treemap.put(5, "tanaka");
		treemap.put(2, "sato");
		treemap.put(1, "iguchi");
		System.out.println(treemap.toString());

		//Setは重複を許さない
		Set hashset = new HashSet();
		hashset.add("a");
		hashset.add("b");
		hashset.add("a");
		hashset.add("c");
		System.out.println(hashset.toString());

		//アルファベット順にソート
		SortedSet treeset = new TreeSet();
		treeset.add("a");
		treeset.add("b");
		treeset.add("a");
		treeset.add("c");
		System.out.println(treeset.toString());

		List list = new ArrayList();
		list.add("7");
		list.add("4");
		list.add("9");
		list.add("1");
		System.out.println(list.toString());
	}
}
```

### Object#hashCode

コラムでハッシュコードについて書いてあるのだけれど。同じインスタンスは同じハッシュコードを返す。ただし、「異なるインスタンスでも同じハッシュコードを返してもいいことになっている」とあり、よくわからなくなってる。Object#equalsメソッドでチェックされるとあるが、同じハッシュコードを吐く可能性があるなら、チェックの意味がなくなるんじゃないだろうか（天文学的な確率なのかもしれないが）。

## Mapの利用

Mapはキーとバリューをもった集合。いわゆる連想配列みたいなイメージでいいのかな。要素の追加はput()。要素一覧へのアクセスはkeySet(),要素へのアクセスはget(key)。

```java:MapSample.java
import java.util.HashMap;
import java.util.Map;
public class MapSample {
	public static void main(String[] args) {
		Map<String, String> map = new HashMap<String, String>();
		map.put("red", "赤");
		map.put("blue", "青");
		map.put("green", "緑");
		map.put("black", "黒");
		map.put("yellow", "黄");
		
		System.out.println(map.get("blue"));
		
		//Map#keySetメソッドで、キーの一覧を取得できる、それを用いた拡張for文
		for(String key : map.keySet()){
			String value = map.get(key);
			System.out.println(key + " : " + value);
		}
	}
}
```

### Iteratorを使った走査

拡張for文を使わない場合は、以下のようにもかける。

```java:IteratorSample.java
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class IteratorSample {
	public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		list.add("Tokyo");
		list.add("London");
		list.add("Paris");
		list.add("New York");
		
		//for(String item : list){
		//	System.out.println(item);
		//}
		
		Iterator<String> iter = list.iterator();
		while(iter.hasNext()){
			String item = iter.next();
			System.out.println(item);
		}
	}
}

```

```java:IteratorSample2.java
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class IteratorSample2 {
	public static void main(String[] args) {
		Map<String, String> map = new HashMap<String, String>();
		map.put("red", "赤");
		map.put("blue", "青");
		map.put("green", "緑");
		map.put("black", "黒");
		map.put("yellow", "黄");

		System.out.println(map.get("blue"));

		/*
		// Map#keySetメソッドで、キーの一覧を取得できる、それを用いたfor文
		 for(String key : map.keySet()){
		 String value = map.get(key);
		 System.out.println(key + " : " + value);
		 }
		 */
		
		//Iteratorを使った走査1
		Iterator<Map.Entry<String, String>> iter = map.entrySet().iterator();
		while(iter.hasNext()){
			Map.Entry<String, String> entry = iter.next();
			String key = entry.getKey();
			String value = entry.getValue();
			System.out.println(key + " : " + value);
		}
		
		/*
		//Iteratorを使った走査2 
		for (Iterator<Map.Entry<String, String>> it = map.entrySet().iterator(); it.hasNext();) {
			// entryを取得
			Map.Entry<String, String> entry = it.next();
			// キーを取得
			String key = entry.getKey();
			// 値を取得
			String value = entry.getValue();
			System.out.println(key + " : " + value);
		}
		*/
	}
}
```

説明

for文、下記リンクがわかりやすくまとまってて、それを見ながら書いてみたけど、正直なんのこっちゃ感ある。

* [JavaのList・Mapのfor文処理覚書 - よんちゅBlog](http://yonchu.hatenablog.com/entry/20110216/1297857882)

## Vector, Hashtable, Enumeration

Java 1.0から導入されている古いクラス（コレクションフレームワークはJava 1.2から）。コレクションフレームワークの実装と異なる点があるので利用する際は注意（Processingで使ったりしてたなぁという印象）。

| クラス名 | 特徴 |
|---------|------|
| Vectorクラス | Listの実装、内部で同期化を行う |
| Hashtableクラス | Mapの実装、内部で同期化を行う、キーと値にnullを格納できない |
| Enumerationインターフェイス | 一覧用のインターフェイスで、機能的にはIteratorに相当 |

Enumerationは昨日出てきた列挙型（enum）と綴りは同じだが、インターフェイスと型なので関連はないのかも（適当）。

```java:VectorSample.java
import java.util.Enumeration;
import java.util.Vector;

public class VectorSample {
	public static void main(String[] args) {
		Vector<String> list = new Vector<String>();
		list.add("Tokyo");
		list.add("London");
		list.add("Paris");
		list.add("Hongkong");
		list.add("Los Angeles");

		Enumeration<String> items = list.elements();
		while(items.hasMoreElements()){
			String item = items.nextElement();
			System.out.println(item);
		}
	}
}
```

## 配列とListの変換

```java:Array2List.java
import java.util.Arrays;
import java.util.List;

public class Array2List {
	public static void main(String[] args) {

		String[] array = new String[]{"Tokyo", "London" , "Paris"};

		//配列からリストへの変換
		List<String> list = Arrays.asList(array);
		System.out.println("<----List---->");
		for(String item : list){
			System.out.println(item);
		}

		//リストから配列への変換
		//慣例的に要素数０の配列を渡す
		String [] array2 = list.toArray(new String[0]);
		System.out.println("<----Array---->");
		for(String item : array2){
			System.out.println(item);
		}
	}
}
```

上記コードの`String [] array2 = list.toArray(new String[0]);`で、ジェネリックメソッド（メソッド単位でのジェネリックス宣言）が使用されている。

## ソートとComparator

CollectiosとArraysクラスはコレクションフレームワークを使う際に合わせて利用される。ともに末尾にsがついて、CollectionインターフェイスやArrayクラスとは別。

* Collectionsクラス : コレクションに対するユーティリティメソッドの提供
* Arraysクラス : 配列に対するユーティリティメソッドの提供

```java:CollectionsSample.java
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class CollectionsSample {
	public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		list.add("hoge");
		list.add("fuga");
		list.add("baba");

		for(String item : list){
			System.out.println(item);
		}

		Collections.sort(list);

		for(String item : list){
			System.out.println(item);
		}
	}
}
```

```java:ComparatorSample.java
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class ComparatorSample {
	public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		list.add("ppppppppppppp");
		list.add("hog");
		list.add("fuga");
		list.add("babababa");
		
		class MyComparator implements Comparator<String>{
			@Override
			public int compare(String arg0, String arg1) {
				return arg0.length() - arg1.length();
			}
		}

		Collections.sort(list , new MyComparator());

		for(String item : list){
			System.out.println(item);
		}
	}
}
```

Comparator#compare は、引数の２つの要素を比較して、結果を数字で返す。このメソッドを実装して、ソートで利用する。無名クラスを使用する場合もある。

```java
Collections.sort(list, new Comparator<String>(){
	public int compare(String arg0, String arg1){
		return arg0.length() - arg1.length();
	}
```

うーむ、面倒臭い感じする。PHPみたいな配列ソートのメソッドないものか。以下のオブジェクトを年齢でソートする例でつかめたような気がするけど。

* [java学習 ArrayListのソート、ソートのカスタム - yattの日記](http://d.hatena.ne.jp/yatt/20100520/1274372852)

## [その他] 再帰呼び出し (recursive call) 

説明で出てきたので、なんとなくminからmaxまでの整数値をカウントする関数書いてみた。でも再帰のメリットはない。

```java:RecursiveSample.java

public class RecursiveSample {
	public static void main(String[] args) {
		recursiveCount(1,100);
	}

	static void recursiveCount(int min, int max){
		if(min <= max){
			System.out.println(min);
			recursiveCount(++min, max);
		}
	}
}
```