コレクションフレームワーク
Collection
Section titled “Collection”プログラムを書いていると、多数のデータを扱う機会が多々あります。その場合に、全てデータ構造を自分で用意してもプログラムすることができますが、Javaにはもともとそのデータ構造を表現できるクラス群が用意されています。
これらのクラスはコレクションフレームワークと呼ばれ、うまく使用することによってプログラムを簡単に書くことができます。
Collectionに属するクラス
Section titled “Collectionに属するクラス”コレクションフレームワークに含まれるクラス/インターフェースは次のようなものがあります。
| クラス/インターフェース | 意味 |
|---|---|
| java.util.List | 順序付きリストのインターフェース |
| java.util.ArrayList | 配列を用いたjava.util.Listの実装 |
| java.util.LinkedList | リンクリストを用いたjava.util.Listの実装 |
| java.util.Set | 重複を許さない集合のインターフェース |
| java.util.HashSet | ハッシュを用いたjava.util.Setの実装 |
| java.util.TreeSet | 二分探索木を用いたjava.util.Setの実装 |
また、コレクションに深い関わりのあるMapというデータ構造があります。
このデータ構造は、キーと値のペアを表現するためのもので、辞書のようなものです。
辞書は単語をキーにして意味や用例などの値を取得することができます。
| クラス/インターフェース | 意味 |
|---|---|
| java.util.Map | キーと値のペアを表す構造のインターフェース |
| java.util.HashMap | キーの検索にハッシュを用いたjava.util.Mapの実装 |
| java.util.TreeMap | キーの検索に二分探索木を用いたjava.util.Mapの実装 |
これらのクラスやインターフェースはJavaで元々用意されているもので、機能に不満があれば自分で機能を追加したクラスを作成することもできます。
しかし、ここに挙げたもので事足りるのであれば、わざわざ再開発しないで安全で高速なこれらのクラスを使用することをお勧めします。
Collectionインターフェース
Section titled “Collectionインターフェース”java.util.Collectionはインターフェースです。
よって、そのままインスタンスを生成することができません。
import java.util.Collection;
public class C0S1 { public static void main(String[] args) { Collection c = new Collection(); }} > javac C0S1.java C0S1.java:5: java.util.Collection は abstract です。インスタンスを生成することはできません。 Collection c = new Collection(); ^ エラー 1 個そのため、Collectionインターフェースを実装するクラスを使用する必要があります。
import java.util.Collection;import java.util.ArrayList;import java.util.LinkedList;import java.util.HashSet;import java.util.TreeSet;
public class C0S2 { public static void main(String[] args) { Collection c1 = new ArrayList(); Collection c2 = new LinkedList(); Collection c3 = new HashSet(); Collection c4 = new TreeSet();
System.out.println("c1 = " + c1.getClass()); System.out.println("c2 = " + c2.getClass()); System.out.println("c3 = " + c3.getClass()); System.out.println("c4 = " + c4.getClass()); }} > javac C0S2.java > java C0S2 c1 = class java.util.ArrayList c2 = class java.util.LinkedList c3 = class java.util.HashSet c4 = class java.util.TreeSet注意すべき点は、java.util.Mapはコレクションを実装しません。
Collectionが持つ機能
Section titled “Collectionが持つ機能”java.util.Collectionが持つメソッドのうち、特に使用されると思われるものを抜粋します。
add(Object o)
Section titled “add(Object o)”コレクションにオブジェクトを追加する
import java.util.Collection;import java.util.ArrayList;
public class C1S1 { public static void main(String[] args) { Collection c = new ArrayList(); c.add("hoge"); c.add("foo"); c.add("bar"); System.out.println(c.toString()); }} > java C1S1 [hoge, foo, bar]remove(Object o)
Section titled “remove(Object o)”コレクションからオブジェクトを削除する
import java.util.Collection;import java.util.ArrayList;
public class C1S2 { public static void main(String[] args) { Collection c = new ArrayList(); c.add("hoge"); c.add("foo"); c.add("bar"); c.remove("foo"); System.out.println(c.toString()); }} > java C1S2 [hoge, bar]contains(Object o)
Section titled “contains(Object o)”コレクションに対象のオブジェクトが含まれているか検査する
import java.util.Collection;import java.util.ArrayList;
public class C1S3 { public static void main(String[] args) { Collection c = new ArrayList(); c.add("hoge"); c.add("foo"); System.out.println(c + " contains \"foo\" ? > " + c.contains("foo")); System.out.println(c + " contains \"bar\" ? > " + c.contains("bar")); }} > java C1S3 [hoge, foo] contains "foo" ? > true [hoge, foo] contains "bar" ? > falsesize()
Section titled “size()”コレクションに格納されているオブジェクトの個数を取得する。
import java.util.Collection;import java.util.ArrayList;
public class C1S4 { public static void main(String[] args) { Collection c = new ArrayList(); c.add("hoge"); c.add("foo"); c.add("bar"); System.out.println(c.size()); }} > java C1S4 3isEmpty()
Section titled “isEmpty()”コレクションが空かどうか検査する。
import java.util.Collection;import java.util.ArrayList;
public class C1S5 { public static void main(String[] args) { Collection c = new ArrayList(); System.out.println(c + " is Empty ? > " + c.isEmpty()); c.add("hoge"); c.add("foo"); c.add("bar"); System.out.println(c + " is Empty ? > " + c.isEmpty()); }} > java C1S5 [] is Empty ? > true [hoge, foo, bar] is Empty ? > falsejava.util.Listは順序つきのリストを表現するデータ構造で、次のような実装クラスを持ちます。
- java.util.ArrayList
- java.util.LinkedList
- java.util.Vector
このコレクションは、主に可変長配列の一つの実現方法として使用する場面が多いと思われます。
また、java.util.List自体はインターフェースなので、使用する際は実装クラスをインスタンス化しましょう。
List特有のメソッド
Section titled “List特有のメソッド”java.util.Listはjava.util.Collectionをスーパーインタフェースに持ちますが、List特有のメソッドがいくつか追加されています。
add(int index, Object o)
Section titled “add(int index, Object o)”指定したインデックスにオブジェクトを挿入する。
import java.util.List;import java.util.LinkedList;
public class C2S1 { public static void main(String[] args) { List l = new LinkedList(); l.add("hoge"); l.add("foo"); l.add("bar"); l.add(1, "*inserted*"); System.out.println(l.toString())); }} > java C2S1 [hoge, *inserted*, foo, bar]get(int index)
Section titled “get(int index)”指定したインデックスのオブジェクトを取得する。
import java.util.List;import java.util.ArrayList;
public class C2S2 { public static void main(String[] args) { List l = new ArrayList(); l.add("hoge"); l.add("foo"); l.add("bar"); System.out.println(l.get(1)); }} > java C2S2 fooset(int index, Object element)
Section titled “set(int index, Object element)”指定したインデックスにオブジェクトを格納する。
import java.util.List;import java.util.ArrayList;
public class C2S3 { public static void main(String[] args) { List l = new ArrayList(); l.add("hoge"); l.add("foo"); l.add("bar"); l.set(1, "moge"); System.out.println(l.toString()); }} > java C2S3 [hoge, moge, bar]indexOf(Object o)
Section titled “indexOf(Object o)”指定したオブジェクトが格納されている先頭のインデックスを取得する。
指定したオブジェクトがリストの中に存在しない場合、-1を返す。
import java.util.List;import java.util.ArrayList;
public class C2S4 { public static void main(String[] args) { List l = new ArrayList(); l.add("hoge"); l.add("foo"); l.add("bar"); System.out.println("index of foo = " + l.indexOf("foo")); System.out.println("index of moge = " + l.indexOf("moge")); }} > java C2S4 index of foo = 1 index of moge = -1lastIndexOf(Object o)
Section titled “lastIndexOf(Object o)”指定したオブジェクトが格納されている末尾のインデックスを取得する。
指定したオブジェクトがリストの中に存在しない場合、-1を返す。
import java.util.List;import java.util.ArrayList;
public class C2S5 { public static void main(String[] args) { List l = new ArrayList(); l.add("hoge"); l.add("foo"); l.add("bar"); l.add("foo"); System.out.println("index of bar = " + l.indexOf("bar")); System.out.println("last index of bar = " + l.lastIndexOf("bar")); System.out.println("index of foo = " + l.indexOf("foo")); System.out.println("last index of foo = " + l.lastIndexOf("foo")); }} > java C2S5 index of bar = 2 last index of bar = 2 index of foo = 1 last index of foo = 3subList(int fromIndex, int toIndex)
Section titled “subList(int fromIndex, int toIndex)”リストの部分リストを取得する。
コピーではなくビューを返すので、部分リストに行った変更は元のリストにも反映される。
import java.util.List;import java.util.ArrayList;
public class C2S6 { public static void main(String[] args) { List l = new ArrayList(); l.add("hoge"); l.add("foo"); l.add("bar"); l.add("hogehoge"); l.add("foofoo"); l.add("barbar"); List subl = l.subList(2, 5); subl.set(0, "*replaced*");
System.out.println("list = " + l.toString()); System.out.println("sub list = " + subl.toString()); }} > java C2S6 list = [hoge, foo, *replaced*, hogehoge, foofoo, barbar] sub list = [*replaced*, hogehoge, foofoo]各実装クラスの使い分け
Section titled “各実装クラスの使い分け”| 実装クラス | 使用する場面 |
|---|---|
| java.util.ArrayList | 基本的にはこのクラスを使用する |
| java.util.LinkedList | 挿入や削除を頻繁に行う場合 |
| java.util.Vector | ほとんどの場面で使用しない |
java.util.Vectorを使用しないのは、このクラスがレガシーコレクションと呼ばれる古い実装であるからです。
他の2個と違いVectorだけはスレッドセーフですが、それを理由にこのクラスの使用を考えるのはやめておいたほうが無難です。
ArrayList
Section titled “ArrayList”Listの実装のうち、通常の配列を用いた実装で、特別重要なメソッドは存在しません。
ArrayList以外のクラスを使用する特別な理由がない限り、ArrayListを使用するのが無難です。
LinkedList
Section titled “LinkedList”Listの実装のうち、リンクリストを用いた実装で、リンクリスト特有のメソッドがいくつか追加されています。
スタックやキューなどのデータ構造を表現したい場合は、LinkListを使用すると便利です。
addFirst(Object o) / addLast(Object o)
Section titled “addFirst(Object o) / addLast(Object o)”リストの先頭/末尾にオブジェクトを追加する
import java.util.LinkedList;
public class C3S1 { public static void main(String[] args) { LinkedList l = new LinkedList(); l.add("hoge"); l.add("foo"); l.add("bar"); l.addFirst("*first*"); l.addLast("*last*"); System.out.println(l.toString()); }} > java C3S1 [*first*, hoge, foo, bar, *last*]getFirst() / getLast()
Section titled “getFirst() / getLast()”リストの先頭/末尾のオブジェクトを取得する。
import java.util.LinkedList;
public class C3S2 { public static void main(String[] args) { LinkedList l = new LinkedList(); l.add("hoge"); l.add("foo"); l.add("bar"); System.out.println(l.getFirst()); System.out.println(l.getLast()); }} > java C3S2 hoge barremoveFirst() / removeLast()
Section titled “removeFirst() / removeLast()”リストの先頭/末尾のオブジェクトを削除し、削除したオブジェクトを取得する。
import java.util.LinkedList;
public class C3S3 { public static void main(String[] args) { LinkedList l = new LinkedList(); l.add("hoge"); l.add("foo"); l.add("bar"); l.add("hogehoge"); l.add("foofoo"); l.add("barbar"); System.out.println("first = " + l.removeFirst()); System.out.println("last = " + l.removeLast()); System.out.println("rest = " + l.toString()); }} > java C3S3 first = hoge last = barbar rest = [foo, bar, hogehoge, foofoo]サンプル - Stackの実装
Section titled “サンプル - Stackの実装”import java.util.LinkedList;
public class Stack { private final LinkedList list;
public Stack() { list = new LinkedList(); }
public void push(Object o) { list.addFirst(o); }
public Object pop() { return list.removeFirst(); }}public class StackTest { public static void main(String[] args) { Stack stack = new Stack(); stack.push("hoge"); stack.push("foo"); stack.push("bar"); System.out.println(stack.pop()); System.out.println(stack.pop()); System.out.println(stack.pop()); }} > java StackTest bar foo hogeListと配列の相互変換
Section titled “Listと配列の相互変換”Listは配列に近いデータ構造であるため、これらを相互に変換したい場合があります。
配列からListへの変換
Section titled “配列からListへの変換”Listから配列に変換する場合は、java.util.ArraysのasList(Object[])メソッドを使用します。
import java.util.List;import java.util.Arrays;
public class ArrayToList { public static void main(String[] args) { String[] array = {"hoge", "foo", "bar"};
List list = Arrays.asList(array);
System.out.println(list.toString()); }}Listから配列への変換
Section titled “Listから配列への変換”配列からListに変換する場合は、java.util.ListのtoArray(Object[])メソッドを使用します。
import java.util.List;import java.util.ArrayList;
public class ListToArray { public static void main(String[] args) { List list = new ArrayList(); list.add("hoge"); list.add("foo"); list.add("bar");
String[] array = (String[]) list.toArray(new String[list.size()]);
for (int i = 0; i < array.length; i++) { System.out.println(array[i]); } }}java.util.Setは集合を表現するデータ構造で、次のような実装クラスを持ちます。
- java.util.HashSet
- java.util.TreeSet
その他にも J2SDK 1.4 からいくつかの実装クラスが追加されています。
Set特有のメソッド
Section titled “Set特有のメソッド”java.util.Setはjava.util.Collectionをスーパーインタフェースに持ち、特別重要なメソッドは追加されていません。
各実装クラスの使い分け
Section titled “各実装クラスの使い分け”| 実装クラス | 使用する場面 |
|---|---|
| java.util.HashSet | 順序を考慮しなくてよい場合 |
| java.util.TreeSet | java.lang.Comparableを実装している、順序を持つオブジェクトを扱う場合 |
TreeSetの実装には2分探索木が使用されますので、データに順序関係のない場合は使用できませんし、データが元々整列している場合はパフォーマンスが低下します。
HashSet
Section titled “HashSet”この実装では各クラスのhashCode()メソッドを呼び出し、その値を用いてデータの格納位置を決定させるため、hashCode()メソッドが正しく実装されている必要があります。
また、順序は保証されません。
import java.util.Set;import java.util.HashSet;
public class C4S1 { public static void main(String[] args) { Set s = new HashSet(); s.add("hoge"); s.add("foo"); s.add("bar"); s.add("moge"); System.out.println(s.toString()); }} > java C4S1 [foo, bar, hoge, moge]TreeSet
Section titled “TreeSet”この実装ではjava.lang.ComparableインターフェースのcompareTo(Object o)メソッドを呼び出し、その結果を用いてデータの格納位置を決定させるため、java.lang.Comparableインターフェースを実装していないオブジェクトを扱えません。
また、この集合はcompareToメソッドで定義された順序どおりに整列されます。
import java.util.Set;import java.util.TreeSet;
public class C4S2 { public static void main(String[] args) { Set s = new TreeSet(); s.add("hoge"); s.add("foo"); s.add("bar"); s.add("moge"); System.out.println(s.toString()); }} > java C4S2 [bar, foo, hoge, moge]この場合では、java.lang.Stringの定める順序 (アルファベット順) で整列しました。
java.util.Mapはキーと値のペアを表現するデータ構造です。
MapはCollectionではありませんが、Collectionと深いかかわりがあります。
java.util.Map自体はインターフェースでインスタンス化できませんので、次のような実装クラスを使用します。
- java.util.HashMap
- java.util.TreeMap
Mapが持つ機能
Section titled “Mapが持つ機能”java.util.Collectionが持つメソッドのうち、特に使用されると思われるものを抜粋します。
put(Object key, Object value)
Section titled “put(Object key, Object value)”指定したキーと値のペアを追加する。
import java.util.Map;import java.util.HashMap;
public class C5S1 { public static void main(String[] args) { Map m = new HashMap(); m.put("hoge", "123"); m.put("foo", "456"); m.put("bar", "789"); System.out.println(m.toString()); }} > java C5S1 {foo=456, bar=789, hoge=123}get(Object key)
Section titled “get(Object key)”指定したキーに対応する値を取得する。
キーが登録されていない場合はnullを返す。
import java.util.Map;import java.util.HashMap;
public class C5S2 { public static void main(String[] args) { Map m = new HashMap(); m.put("hoge", "123"); m.put("foo", "456"); m.put("bar", "789"); System.out.println(m.get("hoge")); System.out.println(m.get("foo")); System.out.println(m.get("bar")); System.out.println(m.get("moge")); }} > java C5S2 123 456 789 nullcontainsKey(Object key)
Section titled “containsKey(Object key)”指定したキーが存在するか検査する。
import java.util.Map;import java.util.HashMap;
public class C5S3 { public static void main(String[] args) { Map m = new HashMap(); m.put("hoge", "123"); m.put("foo", null); System.out.println("hoge => " + m.get("hoge") + " : " + m.containsKey("hoge")); System.out.println("foo => " + m.get("foo") + " : " + m.containsKey("foo")); System.out.println("bar => " + m.get("bar") + " : " + m.containsKey("bar")); }} > java C5S3 hoge => 123 : true foo => null : true bar => null : falseremove(Object key)
Section titled “remove(Object key)”指定したキーを持つエントリを削除する。
import java.util.Map;import java.util.HashMap;
public class C5S4 { public static void main(String[] args) { Map m = new HashMap(); m.put("hoge", "123"); m.put("foo", "456"); m.put("bar", "789"); m.remove("foo"); System.out.println(m.toString()); }} > java C5S4 {bar=789, hoge=123}keySet()
Section titled “keySet()”キーの集合を取得する。
import java.util.Map;import java.util.Set;import java.util.HashMap;
public class C5S5 { public static void main(String[] args) { Map m = new HashMap(); m.put("hoge", "123"); m.put("foo", "456"); m.put("bar", "789"); Set s = m.keySet(); System.out.println(s.toString()); }} > java C5S5 [foo, bar, hoge]entrySet()
Section titled “entrySet()”エントリ (キーと値のペア1つ) の集合を取得する。
import java.util.Map;import java.util.Set;import java.util.HashMap;
public class C5S6 { public static void main(String[] args) { Map m = new HashMap(); m.put("hoge", "123"); m.put("foo", "456"); m.put("bar", "789"); Set s = m.entrySet(); System.out.println(s.toString()); }} > java C5S6 [foo=456, bar=789, hoge=123]values()
Section titled “values()”値のグループを取得する。
import java.util.Map;import java.util.Collection;import java.util.HashMap;
public class C5S7 { public static void main(String[] args) { Map m = new HashMap(); m.put("hoge", "123"); m.put("foo", "456"); m.put("bar", "789"); Collection c = m.values(); System.out.println(c.toString()); }} > java C5S7 [456, 789, 123]isEmpty()
Section titled “isEmpty()”マップが空かどうか検査する。
import java.util.Map;import java.util.HashMap;
public class C5S8 { public static void main(String[] args) { Map m = new HashMap(); System.out.println(m + " is Empty ? > " + m.isEmpty()); m.put("hoge", "123"); m.put("foo", "456"); m.put("bar", "789"); System.out.println(m + " is Empty ? > " + m.isEmpty()); }} > java C5S8 {} is Empty ? > true {foo=456, bar=789, hoge=123} is Empty ? > falsesize()
Section titled “size()”マップに格納されているエントリの個数を取得する。
import java.util.Map;import java.util.HashMap;
public class C5S9 { public static void main(String[] args) { Map m = new HashMap(); m.put("hoge", "123"); m.put("foo", "456"); m.put("bar", "789"); System.out.println(m.size()); }} > java C5S9 3HashMap
Section titled “HashMap”キーの検索にハッシュを使用するマップです。
この実装では各クラスのhashCode()メソッドを呼び出し、その値を用いてデータの格納位置を決定させるため、hashCode()メソッドが正しく実装されている必要があります。
また、順序は保証されません。
import java.util.Map;import java.util.HashMap;
public class C6S1 { public static void main(String[] args) { Map m = new HashMap(); m.put("hoge", "123"); m.put("foo", "456"); m.put("bar", "789"); m.put("moge", "abc"); System.out.println(m.toString()); }} > java C6S1 {foo=456, bar=789, hoge=123, moge=abc}TreeMap
Section titled “TreeMap”キーの検索に二分探索木を使用するマップです。
この実装ではjava.lang.ComparableインターフェースのcompareTo(Object o)メソッドを呼び出し、その結果を用いてデータの格納位置を決定させるため、java.lang.Comparableインターフェースを実装していないオブジェクトを扱えません。
また、この集合はcompareToメソッドで定義された順序どおりに整列されます。
import java.util.Map;import java.util.TreeMap;
public class C6S2 { public static void main(String[] args) { Map m = new TreeMap(); m.put("hoge", "123"); m.put("foo", "456"); m.put("bar", "789"); m.put("moge", "abc"); System.out.println(m.toString()); }} > java C6S2 {bar=789, foo=456, hoge=123, moge=abc}Iterator
Section titled “Iterator”Collectionの中に含まれる各要素にアクセスする場合、java.util.Iteratorを使用することになります。
Collectionの中身すべてにアクセス
Section titled “Collectionの中身すべてにアクセス”java.util.Collectionインターフェースには次のようなメソッドがあります。
::java.util.Iterator iterator()
:::コレクションの要素の反復子を取得する。
このメソッドを使用してIteratorを取得することにより、簡単にCollectionの各要素にアクセスできます。
java.util.Iteratorが持つメソッドのうち、特に重要な2つを挙げます。
::boolean hasNext()
:::次の要素が存在するか検査する。
::Object next()
:::次の要素を取得する。
この2つのメソッドを使用すると、配列の各要素にアクセスするのと同様に、Collectionの各要素にアクセス可能です。
import java.util.Collection;import java.util.Iterator;import java.util.HashSet;
public class Iteration { public static void main(String[] args) { Collection c = new HashSet(); c.add("hoge"); c.add("foo"); c.add("bar");
for (Iterator i = c.iterator(); i.hasNext();) { String element = (String) i.next(); System.out.println(element); } }} > java Iteration foo bar hogeMapの中身全てにアクセス
Section titled “Mapの中身全てにアクセス”MapはCollectionではないため、先ほどと同様の方法ではイテレーションできません。
そこで、entrySet()メソッドを使用して、Collectionに変換してから行います。
import java.util.Iterator;import java.util.Map;import java.util.HashMap;
public class MapIteration { public static void main(String[] args) { Map m = new HashMap(); m.put("hoge", "123"); m.put("foo", "456"); m.put("bar", "789");
for (Iterator i = m.entrySet().iterator(); i.hasNext();) { Map.Entry entry = (Map.Entry) i.next(); String key = (String) entry.getKey(); String value = (String) entry.getValue(); System.out.println(key + " => " + value); } }} > java MapIteration foo => 456 bar => 789 hoge => 123Collectionのソート
Section titled “Collectionのソート”SortedCollection
Section titled “SortedCollection”Listのソート
Section titled “Listのソート”Listに値を格納していくと、最終的な順序は格納した順番と同じものになります。
この中身をソートしたい場面があるかもしれません。
ソートは速いアルゴリズムを書くのが比較的面倒で、バグを生み出しやすくなります。ここでは、**java.util.Collections.sort(List)**を使用したソートを紹介します。
import java.util.List;import java.util.Arrays;import java.util.Collections;
public class SortList { public static void main(String[] args) { List l = Arrays.asList(new String[]{ "hoge", "foo", "bar", "hogehoge", "foofoo", "barbar", "1", "2", "3", "100", "200" });
System.out.println(l.toString());
Collections.sort(l); System.out.println(l.toString()); }} > java SortList [hoge, foo, bar, hogehoge, foofoo, barbar, 1, 2, 3, 100, 200] [1, 100, 2, 200, 3, bar, barbar, foo, foofoo, hoge, hogehoge]Set/Mapのソート
Section titled “Set/Mapのソート”java.util.HashSetやjava.util.HashMapを使用すると、値またはキーの順序はランダムになります。
これらをソートする一番簡単な方法は、java.util.TreeSetやjava.util.TreeMapを使用するというものです。
import java.util.Set;import java.util.HashSet;import java.util.TreeSet;
public class SortSet { public static void main(String[] args) { Set s = new HashSet(); s.add("hoge"); s.add("foo"); s.add("bar"); s.add("hogehoge"); s.add("foofoo"); s.add("barbar");
System.out.println(s.toString());
s = new TreeSet(s); System.out.println(s.toString()); }} > java SortSet [foo, barbar, bar, hoge, hogehoge, foofoo] [bar, barbar, foo, foofoo, hoge, hogehoge]Comparetor
Section titled “Comparetor”次のプログラムを見てください。
import java.util.List;import java.util.Arrays;import java.util.Collections;
public class LexicographicSort { public static void main(String[] args) { List l = Arrays.asList(new String[]{ "100", "200", "10", "20", "30", "1", "2", "3" });
Collections.sort(l); System.out.println(l.toString()); }} > java LexicographicSort [1, 10, 100, 2, 20, 200, 3, 30]結果を見ても分かるように、数値も辞書式配列でソートされるために正しくソートされません。
そこで、java.util.Comparatorというインターフェースを使用して、比較の方法を定義します。
import java.util.Comparator;import java.util.List;import java.util.Arrays;import java.util.Collections;
public class NumericSort { public static void main(String[] args) { List l = Arrays.asList(new String[]{ "100", "200", "10", "20", "30", "1", "2", "3" });
Collections.sort(l, new NumericComparator()); System.out.println(l.toString()); }}
class NumericComparator implements Comparator { public int compare(Object o1, Object o2) { String s1 = (String) o1; String s2 = (String) o2; return Double.valueOf(s1).compareTo(Double.valueOf(s2)); }} > java NumericSort [1, 2, 3, 10, 20, 30, 100, 200]ここで作成したNumericComparatorクラスは、TreeSetやTreeMapのコンストラクタに引数に取ることによって、各要素の順序付けを決めるルールにも使用することができます。
特殊なCollection
Section titled “特殊なCollection”Collectionフレームワークに含まれるクラスは、基本的に変更可能でスレッドアンセーフです。
変更不可能なコレクションや、スレッドセーフなコレクションを作成するには、自分でわざわざクラスを作成するまでもなく、簡単な方法で実現できます。
変更不可能なCollection
Section titled “変更不可能なCollection”メソッドからコレクションを返す際に、防御的コピーを毎回とったり、配列に変換したりするのは面倒です。その場合に、変更不可能なコレクションとしてメソッドから返すという方法が考えられます。
そこで、java.util.CollectionsクラスのunmodifiableCollection(Collection)というメソッドを使用すると、現在使用しているCollectionを変更不可能にすることができます。
import java.util.Collections;import java.util.Collection;import java.util.ArrayList;
public class Unmodifiable { public static void main(String[] args) { // modifiable collection Collection c = new ArrayList(); c.add("hoge"); System.out.println(c); c.add("foo"); System.out.println(c); c.add("bar"); System.out.println(c);
// *** make it unmodifiable *** c = Collections.unmodifiableCollection(c);
// ERROR ! c.add("moge"); System.out.println(c); }} > java Unmodifiable [hoge] [hoge, foo] [hoge, foo, bar] Exception in thread "main" java.lang.UnsupportedOperationException at java.util.Collections$UnmodifiableCollection.add(Unknown Source) at Unmodifiable.main(Unmodifiable.java:20)mogeが追加される前に例外が発生していることを確認できます。
このほかにもjava.util.Collectionsには以下のようなメソッドがあります。
- List unmodifiableList(List)
- Map unmodifiableMap(Map)
- Set unmodifiableSet(Set)
スレッドセーフなCollection
Section titled “スレッドセーフなCollection”次のプログラムを実行した結果を見てみます。
import java.util.List;import java.util.LinkedList;
public class Loop extends Thread {
private final List list; private static int total = 0;
private Loop(List list) { this.list = list; }
public void run() { int count = 0;
// consume while (list.remove(null)) { count++; }
System.out.println("consume " + count + " elements");
synchronized (this.getClass()) { total += count; } }
public static void main(String[] args) throws InterruptedException { // *** thread UNSAFE list *** List l = new LinkedList();
// prepare 100,000 elements for (int i = 0; i < 100000; i++) l.add(null);
Thread[] loops = new Thread[] { new Loop(l), new Loop(l), new Loop(l), new Loop(l), new Loop(l) };
// start 5 threads for (int i = 0; i < loops.length; i++) loops[i].start();
// wait for (int i = 0; i < loops.length; i++) loops[i].join();
System.out.println("total " + total + " elements"); }} > java Loop consume 9201 elements consume 66894 elements consume 100000 elements consume 98501 elements consume 77424 elements total 352020 elements要素は100,000個しか用意しなかったにもかかわらず、352,020個の要素を取り出しています。
これは、コレクションが基本的にスレッドセーフでないために発生する問題です。
そこで、java.util.CollectionsクラスのsynchronizedList(List)というメソッドを使用すると、現在使用しているListをスレッドセーフにすることができます。
先ほどのプログラムのうち、mainメソッドの先頭 - Listを生成する部分で、使用しているListをスレッドセーフなものに変えてみます。
import java.util.Collections;import java.util.List;import java.util.LinkedList;
public class SynchronizedLoop extends Thread {
private final List list; private static int total = 0;
private SynchronizedLoop(List list) { this.list = list; }
public void run() { int count = 0;
// consume while (list.remove(null)) { count++; }
System.out.println("consume " + count + " elements");
synchronized (this.getClass()) { total += count; } }
public static void main(String[] args) throws InterruptedException { // *** thread SAFE list *** List l = Collections.synchronizedList(new LinkedList());
// prepare 100,000 elements for (int i = 0; i < 100000; i++) l.add(null);
Thread[] loops = new Thread[] { new SynchronizedLoop(l), new SynchronizedLoop(l), new SynchronizedLoop(l), new SynchronizedLoop(l), new SynchronizedLoop(l)};
// start 5 threads for (int i = 0; i < loops.length; i++) loops[i].start();
// wait for (int i = 0; i < loops.length; i++) loops[i].join();
System.out.println("total " + total + " elements"); }} consume 19691 elements consume 19702 elements consume 21211 elements consume 19698 elements consume 19698 elements total 100000 elements用意した要素の数と、取り出した要素の数が等しくなりました。
このほかにもjava.util.Collectionsには以下のようなメソッドがあります。
- Collection synchronizedCollection(Collection)
- Set synchronizedSet(Set)
- Map synchronizedMap(Map)