|
任天堂が、大画面の「ニンテンドーDSi LL」を発表。欲しいと思いますか?
|
J2SE 5.0を使ったカスタムジェネリックコレクションの作成はじめに私の前回の記事では、J2SE 5.0の新しいコレクションの機能を用いて、コレクションで使用する特定の型を指定する方法を説明しました。また、新たに導入された ジェネリックをサポートするクラスの作成まず、「ジェネリック型」をサポートするクラスの作り方を学ぶ必要があります。「ジェネリック型をサポートする」とは、クラスのインスタンス作成を行うたびに、そのインスタンスに関連付けるJavaの型を1つ以上指定できることを意味します。これを説明するために、リスト1の簡単なサンプルクラスについて考えてみましょう。 リスト1 ジェネリッククラス
package com.heatonresearch.examples.collections; public class Example<ONE, TWO, THREE> { private ONE one; private TWO two; private THREE three; public ONE getOne() { return one; } public void setOne(ONE one) { this.one = one; } public THREE getThree() { return three; } public void setThree(THREE three) { this.three = three; } public TWO getTwo() { return two; } public void setTwo(TWO two) { this.two = two; } public static void main(String args[]) { Example<Double, Integer, String> example = new Example<Double, Integer, String>(); example.setOne(1.5); example.setTwo(2); example.setThree("Three"); } } リスト1のクラスがどのように宣言されているかに注目してください。最初の部分にある山かっこ内に3つのジェネリックが記されています。これらのジェネリックは、実際の型が入るプレースホルダです。この種のクラスをインスタンス化するときは、"ONE"、"TWO"、および"THREE"の代わりとなる具体的なクラス名を指定できます。指定を行わない場合、このクラスはデフォルトのオブジェクト型を使用します。 次に示すのは、
Example<Double, Integer, String> example
= new Example<Double, Integer, String>();
上記のコードは、指定した
example.setOne(1.5);
example.setTwo(2);
example.setThree("Three");
これで、ジェネリックを用いたカスタムクラスの作り方がわかりました。この点を押さえておけば、ジェネリックを利用するカスタムのコレクションクラスを作るのはもっと簡単です。 キュークラスの作成キューというデータ構造が非常に役に立つ場合があります。キューの機能を理解するために、遊園地で乗り物の順番を待つ人々の列を想像してみましょう。新たに列に加わる人は、列の最後尾に並びます。順番を待っていれば、いつかは列の先頭にたどり着きます。並んでいる人々の順序が入れ替わることはありません。 これと同じ原理が、キュークラスにもあてはまります。キュークラスには、 次のコードは、Javaによる、ジェネリックを利用したキューの実装方法を示しています。 package com.heatonresearch.examples.collections; import java.util.*; public class Queue<T> { private ArrayList<T> list = new ArrayList<T>(); public void push(T obj) { list.add(obj); } public T pop() throws QueueException { if (size() == 0) throw new QueueException( "Tried to pop something from the queue, " + "when it was empty"); T result = list.get(0); list.remove(0); return result; } public boolean isEmpty() { return list.isEmpty(); } public int size() { return list.size(); } public void clear() { list.clear(); } } 上記のコードでは、このキュークラスのためにジェネリック型を1つ使うことを宣言しています。 public class Queue<T> ジェネリック型 このクラスの package com.heatonresearch.examples.collections; public class QueueException extends Exception { public QueueException(String msg) { super(msg); } } if (size() == 0) throw new QueueException( "Tried to pop something from the queue, " + "when it was empty"); キューが空でない場合、このメソッドはキューから最後の要素を取り出し、
T result = list.get(0);
list.remove(0);
return result;
一時的な変数 キュークラスの動作テスト次のクラスは、「ジェネリックな」キューの動作をテストするためのものです。 package com.heatonresearch.examples.collections; public class TestQueue { public static void main(String args[]) { Queue<Integer> queue = new Queue<Integer>(); queue.push(1); queue.push(2); queue.push(3); try { System.out.println("Pop 1:" + queue.pop()); System.out.println("Pop 2:" + queue.pop()); System.out.println("Pop 3:" + queue.pop()); } catch (QueueException e) { e.printStackTrace(); } } } 上記のコードで作られるキューは、
Queue<Integer> queue = new Queue<Integer>();
このテストでは、次に3つの整数をキューに追加します。 queue.push(1); queue.push(2); queue.push(3); キューに追加された数がプリミティブ型であることに注意してください。J2SEのオートボクシング機能のおかげで、プリミティブな 次に、 1 2 3 ここでは 先読み可能なスタックコレクションの作成 ここでは、もっと複雑なコレクション型として、オブジェクトを実際に取り出す前に先読みが可能な(つまり、「こっそり参照できる」)スタックをとりあげます。反復子またはJ2SE 5.0の新しい ここで紹介する リスト2 PeekableStackクラス
package com.heatonresearch.examples.collections; import java.util.*; public class PeekableStack<T> implements Iterable<T> { private int version; private ArrayList<T> list = new ArrayList<T>(); public int getVersion() { return version; } public void setVersion(int version) { this.version = version; } public Iterator<T> iterator() { PeekableStackIterator peekableStackIterator = new PeekableStackIterator(this, list); return peekableStackIterator; } public void push(T obj) { version++; list.add(obj); } public T pop() { // find the last element int last = list.size() - 1; // is the stack empty? if (last < 0) return null; // return the last element and remove it T result = list.get(last); list.remove(last); return result; } public int size() { return list.size(); } } リスト2の 先読み可能なスタックには、先ほどのキューと同じように この
int last = list.size() - 1;
この結果が0未満の場合は、スタックが空なので、 if (last < 0) return null; スタックに「最後の要素」が存在する場合は、リストからその要素を取り出します。この要素は、リストからの取り出しに成功した後で削除できます。 T result = list.get(last); list.remove(last); 最後に、リストから取り出したオブジェクトを返します。
return result;
PeekableStackIterator peekableStackIterator = new PeekableStackIterator(this, list); ご覧のとおり、この反復子のクラスは、コンストラクタの引数として現在のスタックとスタックが持つ要素のリストを受け取ります。これらの値は、 先読み可能スタックの反復子の作成 Javaの新しい リスト3 PeekableStackIteratorクラス
package com.heatonresearch.examples.collections; import java.util.*; public class PeekableStackIterator<T> implements Iterator { private int version; private PeekableStack<T> source; private List<T> list; private int position; PeekableStackIterator(PeekableStack<T> source, List<T> list) { this.source = source; this.list = list; this.version = source.getVersion(); this.position = 0; } public boolean hasNext() { if (version != source.getVersion()) throw new ConcurrentModificationException(); return (position < list.size()); } public T next() { // check for concurrent modification if (version != source.getVersion()) throw new ConcurrentModificationException(); // if its empty then return null if (!hasNext()) return null; // find the current position int last = list.size() - 1; last -= position; // if there are still elements left, return the // next one and update the current position. T result = null; if (last >= 0) { position++; result = list.get(last); } return result; } public void remove() { } } リスト3の反復子が、スタックの値を実際に変更することは一切ありません。その代わりに、要素のリストにおける現在の位置を絶えず把握し、常に、その次の位置にある要素を返します。現在の要素の位置についての情報を反復子のクラス自体が保持しているので、同じスタックに対して複数の反復子を利用することも可能です。 次のプログラムは、先読み可能スタックの動作をテストするためのものです。 package com.heatonresearch.examples.collections; import java.util.*; public class TestPeekableStack { public static void main(String args[]) { PeekableStack<Integer> stack = new PeekableStack<Integer>(); stack.push(1); stack.push(2); stack.push(3); for (int i : stack) { System.out.println(i); } System.out.println("Pop 1:" + stack.pop()); System.out.println("Pop 2:" + stack.pop()); System.out.println("Pop 3:" + stack.pop()); } } ご覧のとおり、スタックに3つの要素を追加しています。その後、新しい for( int i: stack) { System.out.println( i ); } この記事では、J2SEの新仕様であるジェネリックと 著者紹介Jeff Heaton(Jeff Heaton)
ライター、大学教員、コンサルタントとして活動中。4冊の著作があり、論文誌および雑誌で20を超える記事を発表。また、個人のWebサイトを管理し、人工知能とスパイダー/ボットプログラミングをはじめとする話題について情報発信を行っている。メールの宛先はjheaton@heatonresearch.com。
関連記事 関連テーマ 最新トップニュース
|
|