リスト内の一つ一つのデータはノード (node) または要素 (element) と呼ばれ、 ノードを実現するクラスの内容は以下のとおりである。 なお、簡単化のため、この例ではリストの中に入れるデータは文字列としている。 element はノードに入れる情報本体である。 next は同じ Node クラスのオブジェクトであり、 あるノードから見た「次のノードへの参照 (在りか) 」を示す変数である。
さらに、ノードの情報を初期化するコンストラクタ、 ノードの情報を登録/参照するメソッドを用意し、 次のようなクラスで表すことができる。 (ファイル名 Node.java)
public class Node {
private String element;
private Node next;
/** コンストラクタ */
public Node() { }
public Node(String element, Node next) {
this.element = element;
this.next = next;
}
/** 次のノードへの参照を登録する */
public void setNext(Node next) {
this.next = next;
}
/** 次のノードへの参照を返す */
public Node getNext() {
return next;
}
/** ノードの中身の情報を登録する */
public void setElement(String element) {
this.element = element;
}
/** ノードの中身の情報を返す */
public String getElement() {
return element;
}
}
上で定義したクラス Node を使い簡単なリストを作ることを考える。 リストは次のように宣言し、最初のノードを加えることができる。
Node head = null; // リストの先頭を保持する変数を宣言、最初は空
head = new Node("Copernicus", null); // 先頭にCopernicusを追加
次に2番目、3番目、4番目の要素を末尾に加えるには次のように書くことができる。
// リストの末尾に 2番目の要素 Newton を追加
head.setNext(new Node("Newton", null));
// リストの末尾に 3番目の要素 Heisenberg を追加
Node second = head.getNext();
second.setNext(new Node("Heisenberg", null));
// リストの末尾に 4番目の要素 Einstein を追加
Node third = second.getNext();
third.setNext(new Node("Einstein", null));
以上の処理を実行するとリストには
[head] → Copernicus → Newton → Heisenberg → Einstein → (null)
という順にデータが並んでいることになる。
リストを管理するための変数は、先頭を表す head だけである。 先頭の要素さえ分かっていれば、参照をたどることによってすべてのノードに到達できる。 リストのノードを順番にアクセスする方法で代表的なのは、 次のようにリストの先頭から順に調べていく方法である。
Node p = head;
while (p != null) {
リストのノード p に関する処理;
p = p.getNext();
}
次の例は、リストの中身を順番に表示する処理の例である。
// リスト全体を表示
Node p = head;
while(p != null) {
System.out.println(p.getElement());
p = p.getNext();
}
データが並んだリストから、ノードを削除する方法を考える。 削除対象のノードによって、以下の2とおりに処理を分けて考えると良い。
以下のノードが存在するリストがあるとする。
[head] → Copernicus → Newton → Heisenberg → Einstein → (null)
先頭のノード Copernics を削除することを考える。 (教科書 p.290 の図9-13参照) プログラムは以下のとおりである。head はリストの先頭を指しているとする。
if (head != null)
head = head.getNext();
先頭以外のノードの削除を考える。例えば Heisenberg を削除するとする。 (教科書 p.293 の図9-15参照)
まず、削除したい一つ前のノードを探す。この例では Newton である。 「一つ前」というのは不自然な設定であるが、 能率良く処理を行うためにはやむを得ない。 リストは、前に進むのは容易であるが、後ろに戻るのは困難であるからである。 削除したいノードを指定するようにすると、 一つ前のノードを求めるのに時間がかかる。 (この不便を解消するには双方向リストを用いる)
(あらかじめ、削除対象の Heisenberg の一つ前 (Newton) を探し、p に代入しておく)
if (p != null) {
Node target = p.getNext(); // target: 削除対象のノード
Node q = target.getNext(); // q: 削除対象の「次」のノード
p.setNext(q);
}
=================
各変数が指すノード:
[head] → Copernicus → Newton → Heisenberg → Einstein → (null)
[p] [target] [q]
=================
上で取り上げた、リストへのデータの追加、削除 リスト全体の表示を含めたプログラム全体を示す。 各自実行して動作を確認すること。
なお、クラス Node は、例題1のものを使う。
public class LinkedListSample {
public static void main(String[] args) {
Node head = null;
// 最初の要素 Copernicus の追加
head = new Node("Copernicus", null);
// リストの末尾に 2番目の要素 Newton を追加
head.setNext(new Node("Newton", null));
// リストの末尾に 3番目の要素 Heisenberg を追加
Node second = head.getNext();
second.setNext(new Node("Heisenberg", null));
// リストの末尾に 4番目の要素 Einstein を追加
Node third = second.getNext();
third.setNext(new Node("Einstein", null));
// リスト先頭の要素 (Copernics) を削除
if (head != null)
head = head.getNext();
Node p;
// リストの2番めの要素 (Heisenberg) を削除
p = head; // 削除対象の1つ前 (Newton) のノードをpと置く
if (p != null) {
Node target = p.getNext(); // target: 削除対象のノード
Node q = target.getNext(); // q: 削除対象の「次」のノード
p.setNext(q);
}
// リストの内容を表示
// この時点でのリストは Newton -> Einstein
p = head;
while(p != null) {
System.out.print(p.getElement());
p = p.getNext();
}
System.out.println();
}
}
提出先: /home/submit/JavaLib/[出題日]/[学籍番号]
[注意] 本課題では、1つクラスを1つのファイルとして作成することを想定している。 ファイル名は、「public宣言を行ったクラスの名前.java」 である。 ファイルが別々であっても、同じディレクトリにあるpublic宣言されたクラスは、 相互に利用することができる。 (詳しく知りたい人は、「パッケージ」について調べると良い)
リスト内のデータとリストのさまざまな機能 (データの追加、リスト全体の表示) を 1つのクラスとして設計し実装しなさい。
これまで利用してきた、Javaのクラスライブラリの LinkedList の機能縮小版を自分で作ると考えれば良い。 良いアルゴリズムや良いプログラムを考えるためには、 ソフトウェアの基本的なメカニズムは理解してもらいたい。 この勉強には基本的なデータ構造を自分で作成してみることが一番である。
クラス名 MyLinkedList とし、次のような機能を備えるようにする。
addLast, remove の2つのメソッドを作成しなさい。
作成したプログラムが正しく動作するかどうか、必要かつ十分なテストを行うこと。 少なくとも、次のような main メソッドで動作を確認すること。 (提出ファイル名: MyLinkedList.java)
public class MyLinkedListMain {
public static void main(String[] args) {
MyLinkedList list = new MyLinkedList();
list.addLast("Copernicus");
list.addLast("Newton");
list.addLast("Heisenberg");
list.addLast("Einstein");
// この時点でのリスト: Copernics -> Newton -> Heisenberg -> Einstein
list.print();
// Copernics と Einstein を削除
list.remove("Copernicus");
list.remove("Einstein");
System.out.println("=========[削除後]=========");
// この時点でのリスト: Newton -> Heisenberg
list.print();
}
}
クラス MyLinkedList の大枠は次のようになる。
public class MyLinkedList {
private Node head;
public MyLinkedList() {
head = null;
}
public void addLast(String elem) {
// ここを考える
}
public void remove(String elem) {
// ここを考える
}
public void print() {
Node p = head;
while (p != null) {
System.out.print(p.getElement() + " ");
p = p.getNext();
}
System.out.println();
}
}
ある文字列 string1 が、別の文字列 string2 の内容と等しいかどうかを判定するには、 equals を使う。例を以下に示す。
if (string1.equals(string2)) {
string1 と string2 が等しいときの処理
}