Javaによるプログラミングに関する世界観3B(コレクションと配列)です。
以下はJavaのコレクションクラスの基本構造。
構造 | クラス | 解説 |
---|---|---|
List | ArrayList LinkedList | 順番付けされたリスト。 ArrayListは取得が速く編集の遅い配列的なリスト。 LinkedListは取得が遅く編集が速い連結リスト。 |
Map | HashMap TreeMap | キーによって対応する値を得られるマップ。 TreeMapは自動ソートされる。 |
Set | HashSet TreeSet | 順番付けされないセット。 TreeSetは自動ソートされる。 |
Javaの配列は動的にサイズを変更したり、要素を追加したりすることができない。リストを使うことで、動的にサイズが変化するベクターコンテナを使うことができる。
配列とリストとハッシュも参照のこと。
Java初級者のための必須Java標準APIまとめと簡易解説 | つかびーの技術日記を参考に執筆・引用しました。
クラス | 解説 |
---|---|
java.util.List<E> java.util.Map<K, V> java.util.Set<E> | コレクションの基本インターフェース。 |
java.util.ArrayList<E> java.util.HashMap<K, V> java.util.HashSet<E> | コレクションインターフェースの実装。 |
java.util.Collections | コレクションを操作・処理するクラス。 |
java.util.Arrays | 配列を操作・処理するクラス。 |
java.util.Comparator<T> java.lang.Comparable<T> | 要素と要素を比較するためのインターフェース。 ソートをかけるために必要となる。 |
基本的に、以下のようにインターフェースの基本型としてListを使い、ArrayListなどのインターフェースの実装をnewしてListの変数に詰め込む。
List<String> hogeList = new ArrayList<String>();
このようにすることで、「Listの操作だけで十分なオブジェクト」ということを明確にできる。
以下はListの主なメソッド。
メソッド | 解説 |
---|---|
ArrayList() ArrayList(Collection) ArrayList(int) | ArrayListのコンストラクタ。 引数を指定しなかった場合、初期サイズ10の空のArrayListを作成する。 intを引数に指定する場合、指定した数値が配列のサイズになる。 |
LinkedList() LinkedList(Collection) | LinkedListのコンストラクタ。 |
void add(int, Object) boolean add(Object) | 挿入する位置を指定して要素を追加 |
boolean addAll(Collection) boolean addAll(int, Collection) | 複数の要素を追加 |
void clear() | すべての要素をクリア |
Object get(int) | インデックスを指定して要素を取得 |
Object remove(int) | 要素を削除 |
Object set(int, Object) | 要素を別の要素に置換 |
int size() | 要素の個数を取得 |
(Javaの道:コレクション・フレームワーク(2.List)を参考に執筆・引用しました。)
たとえば、ArrayListは以下のように使う。
List<String> hogeList = new ArrayList<String>(); hogeList.add("Hoge hoge hoge"); hogeList.add("Fuga fuga fuga"); System.out.println(hogeList.get(0)); System.out.println(hogeList.get(1));
また、HashMapは以下のように使う。
Map<String, String> hogeMap = new HashMap<String, String>(); hogeMap.put("hoge", "Hoge hoge hoge"); hogeMap.put("fuga", "Fuga fuga fuga"); System.out.println(hogeMap.get("hoge")); System.out.println(hogeMap.get("fuga"));
詳しくは【Java】 java.util コレクションフレームワーク (List, Set, Map ) - Qiitaが参考になります。
java.util.Arraysは配列を操作するクラスで、new int[]のように作った配列に対して、検索やソートなどの操作を実行することができます。
また、Arrays.asList()を使うことで、配列をリストに変換できます。
java.util.Collectionsは、コレクションを操作するクラスで、ソートなどをかけることができる。
コレクションをソートしたいなら、
Collections.sort(lst1);
とする。lst1はリストの変数名。
後述するComparatorインターフェースを使うことにより、ソートの方式を変更できる。
ほかにも、Collections.reverse()で逆順の並べ替えができる。
java.util.Comparatorは配列やリストをソートする際に、大小関係を比較するためのインターフェース。
降順で配列をソートしたいなら、
Arrays.sort(ar1, Comparator.reverseOrder());
のようにする。ar1は配列の変数名。
また、Comparatorインターフェースを実装したクラスの中でcompare()メソッドをオーバーライドすれば、自分で比較用のメソッドを実装することもできる。たとえばクラスの中の何かのメンバやパラメータに基づいて比較するといったことが可能。
class Hoge { String str_hoge; Hoge (String a_hoge) { str_hoge = a_hoge; } public String getHoge() { return str_hoge; } } class HogeComparator implements Comparator<Hoge>{ @Override public int compare(Hoge hoge1, Hoge hoge2) { return hoge1.getHoge().compareTo(hoge2.getHoge()); } }
これで、以下のようにできる。
Hoge[] hoges = new Hoge[3]; hoges[0] = new Hoge("Assy"); hoges[1] = new Hoge("Zaidou"); hoges[2] = new Hoge("Schwarz"); Arrays.sort(hoges, new HogeComparator());
sort()にComparatorインターフェースを指定しない場合は、要素のクラスがjava.lang.Comparableインターフェースを実装している場合、その中のcompareTo()メソッドが比較に使われる。StringはComparableインターフェースを実装しているため、List<String>はComparatorインターフェースを指定しなくても昇順にソートできる。
Iteratorインタフェースは、コレクションの中を繰り返し順番に取得・参照する仕組み。
以下のようになる。
for (Iterator i = lst1.iterator(); i.hasNext();) { System.out.println(i.next()); }
Iteratorインターフェースは、次の要素が存在するかしないかを確認するためのhasNext()、次の要素を取得するnext()、最後に呼び出された要素を削除するremove()を使うことができる。
Listインタフェースを実装したクラスの場合、Iteratorを拡張したListIteratorを利用出来る。ListIteratorは、順番に次の要素へとアクセスするしかできないIteratorと異なり、前の要素にもアクセスできる。また、Iteratorでは削除しかできないが、ListIteratorは追加・変更もできる。ListIteratorは現在位置を知ることもできる。
イテレータとジェネレータを参照のこと。
C++では、テンプレートやジェネリクスを使って、intであろうとfloatであろうと、どんな型にも対応できる汎用クラスを書くことができます。
Javaにも、同様にジェネリクスは存在し、C++と同様<>で囲まれた型に対応するジェネリクス型を使うことができます。
しかしながら、Javaの場合、継承関係を用いることで、いくらか同様のことは実現できます。
なぜなら、Javaでは暗黙のうちにクラスがObjectクラスから継承されており、すべてのクラスはObjectクラスをスーパークラスとしているため、たとえば「Object型のオブジェクトを格納・取り出しを行うクラス」を作ってしまえば、すべてのオブジェクトをObject型として扱った上で、そのObject型を汎用的に操作することができるからです。
しかしながら、この方法では、どんな型でも入ってしまい、逆に言えばどんな型が入っているのかが分かりません。静的型付け言語であるのに、型安全が保証されず、キャストを注意深くする必要があり、せっかくの静的型付け言語を使う意味がないのです。よって、今のJavaやC#では、ジェネリックでないリスト(C#の場合ArrayList)を使ってなんでも格納・取り出しするのではなく、型安全の保証されたジェネリクス型を使うことが推奨されています。
本当のことを言えば、C/C++でもvoid型のポインタを使って、それをint型やfloat型のポインタに型キャストすれば、同じことはできます。しかしながら、この場合、intやfloatとしてその変数を扱えるかどうかは、実行時にキャストできるかにかかっています。コンパイルは通ったとしても、予期せぬエラーを引き起こすことがあります。これはmalloc()などの一部のAPIを除いて、推奨されないプログラミングスタイルです。このような時にテンプレートやジェネリクス型を使えば、型安全を保証することができます。
後日注記:Object型による型キャストの方式を用いると、もしStringを意図しておきながらIntegerが使われた場合、コンパイル時にエラーが出ず、実行時にエラーになります。ジェネリクスを用いることで、エラーをコンパイル時に判明させることができます。
C++(ジェネリック)、C++(STL・ライブラリ)も参照のこと。