第5回 連結リストの利用

第5回 連結リストを使ったプロ
グラム
~配列以外のデータ構造に挑戦!~
学習目標

Javaにおける参照の仕組みを説明できる


基本データ型と参照型の違いを説明できる
連結リストを使ったプログラムが書ける



連結リストを使う利点を説明できる
連結リストの概念を説明できる
連結リストへの追加、削除、検索のプログラム
をJavaを使って書ける
5.1 参照の仕組み


参照の仕組み練習問題
5.1.1 Javaにおける参照の仕組み



①メモリと番地
②参照とメモリの仕組み
③基本データ型と参照型
参照の仕組み練習問題

テキスト(5.1)の


リスト「参照練習問題1」
リスト「参照練習問題2」
がどのような出力をするか考えなさい。
参照練習問題1
public static void main(String args[]){
int x;
int y;
x = 3;
y = x;
y = 15;
System.out.println(x);
System.out.println(y);
}
参照練習問題2
public static void main(String args[]){
TestClass x;
TestClass y;
x = new TestClass();
x.id = 3;
y = x;
y.id = 15;
System.out.println(x.id);
System.out.println(y.id);
}
class TestClass {
int id;
}
答え


参照練習問題1
3
15
参照練習問題2
15
15
①メモリと番地

プログラムは、変数をメモリに格納します。


メモリには、
すべての場所に
番地がついています。
この番地のことを
参照といったり
します。
メモリ
番地
100
101
102
103
104
内容
②参照とメモリの仕組み(1)
メモリ
番地(100)
番地(101)
int x;
int y;
x = 3;
y = x;
y = 15;
System.out.println(x);
System.out.println(y);
x
y
番地
内容
100
3
0(初期値)
101
15
3
0(初期値)
102
103
104
②参照とメモリの仕組み(2)
メモリ
番地(100)
番地(101)
TestClass x;
TestClass y;
x = new TestClass();
x.id = 3;
y = x;
y.id = 15;
System.out.println(x.id);
System.out.println(y.id);
番地
内容
x
100
1000
null
0(初期値)
y
101
1000
null
オブジェクト用のメモリ
1000
1001
15
3
0(初期値)
③基本データ型と参照型


基本データ型
※これはJavaだけの話です。
→値が直接入るタイプ
参照型
→オブジェクト用のメモリに生成され、参照
が入るタイプ

どうやって見分けたらいいでしょうか?
基本データ型を覚えよ

Javaの基本データ型は8種類しかない。

整数が入るもの




boolean
小数が入るもの
float
double
int
long
short
真偽値が入るもの




文字が入るもの
byte(1バイト文字)
char(2バイト文字)

その他はすべて参照型


new 演算子でインスタンス化する必要が
あるもの
オブジェクト用のメモリ
実は配列も
番地
内容
参照型です。
[0]
1000
0
int[] idArray = new int[5];
メモリ
idArray
[1]
1001
0
[2]
1002
0
番地
内容
[3]
1003
0
101
1000(番地)
[4]
1004
0
まとめ
Javaにおける参照の仕組み



変数を宣言すると、メモリに領域が確保される。
=(代入)記号での代入文は、値のコピーが行わ
れる。
ただし、オブジェクトの場合は、値に参照が入っ
ているため、参照のコピーが行われる。


このとき、インスタンスのコピーは行われない。
インスタンスを生成するとオブジェクト用のメモリ
が確保される。
5.2 連結リスト


5.2.1 配列にこだわる必要はない
5.2.2 連結リストとは?
5.2.1
配列にこだわる必要はない

配列は使いやすいのですが、問題点もあ
ります。

考えてみましょう
配列は大きさが決まっている

配列は、作るときに大きさを決める必要が
あります。


その大きさ以上要素を入れることができませ
ん。
また、要素が入っていないと、メモリの無駄に
なります。
間に割り込んで
挿入するのが大変


テキストエディタ(メモ帳,mule…)
を作ることを考えましょう。
機能:


文字を挿入する
文字を削除する
配列で作ったとすると、、、

一文字一文字を
配列にいれて
管理します。
番地
内容
[0]
1000
‘お’
[1]
1001
‘は’
[2]
1002
‘よ’
[3]
1003
‘う’
[4]
1004
‘ざ’
[5]
1005
‘い’
[6]
1006
‘ま’
[7]
1007
‘す’
挿入はどうするか?
‘ご’を挿入したい
番地
内容
[0]
1000
‘お’
[1]
1001
‘は’
[2]
1002
‘よ’
[3]
1003
‘う’
[4]
1004
‘ご’
‘ざ’
[5]
1005
‘い’
‘ざ’
[6]
1006
‘い’
‘ま’
[7]
1007
‘す’
‘ま’
配列での実装の問題点

もし、これが、1万文字のレポートの最初の
所だったらどうなるか?

一文字挿入するごとに、1万回の移動が必要

遅すぎてだれも使ってくれません
5.2.2 連結リストとは?
100番地
102番地
104番地
106番地
108番地
‘お’
‘は’
‘よ’
‘ご’
‘ざ’
102
104
106
108
null
実際のメモリ上では
番地
内容
100
‘お’
101
102
102
‘は’
103
104
104
‘よ’
105
106
106
‘う’
107
①連結リストの特徴
(1)連結リストへの挿入

移動しなくて済む
100番地
102番地
104番地
106番地
108番地
‘お’
‘は’
‘よ’
‘ご’
‘ざ’
102
104
106
110
108
null
110番地
‘う’
106
(2)連結リストからの削除

もっと簡単
100番地
102番地
104番地
106番地
108番地
‘お’
‘は’
‘よ’
‘ご’
‘ざ’
102
104
106
106
108
null
③連結リストの利点・欠点

考えてみましょう
③連結リストの利点・欠点

利点

メモリをあらかじめ確保しておく必要が無い




必要十分な量だけその都度確保できる
メモリの許す限り大きくできる
挿入、削除が非常に早い
欠点


番地を保存しておくためのメモリ領域を取る
検索が遅い


最初から順繰りに辿っていかないといけない
配列ならば、バイナリサーチ(第8回)などの早い検索方法が
ある
5.3 連結リストを実装する

5.3.1 連結リストの実装



①連結リストの実装例
②クラス設計
5.3.2 連結リストのアルゴリズム



①追加アルゴリズム
②検索アルゴリズム
③削除アルゴリズム
①連結リストの実装例

前回作ったプログラムを連結リストで作り直す



ItemTypeクラスは変更なし
(前回の配列バージョンも残しておいてください)
例題5-1-(複数のクラスで一つのプログラムです)





「Example5_1.java」
「ItemType.java」
「ItemTypeList.java」
「LinkObject.java」
「LinkTerminal.java」
②クラス設計

前回同様、ExampleクラスとItemTypeList
クラスを分離します
ItemTypeList{
add();
Example{
delete();
main();
search();
}
display();
}
②クラス設計
(1)LinkObjectクラス

連結リスト用オブジェクトLinkObjectを作る
例題5-1
(Example5_1.java)
//連結リスト用オブジェクト
class LinkObject {
ItemType data;
//商品種類
LinkObject next; //次の要素への参照
}
??自分で自分を持ってるの??
(1)LinkObjectクラス

実は参照なので、図のようになります。
//連結リスト用オブジェクト
class LinkObject {
ItemType data;
LinkObject next;
}
100番地
102番地
22(id)
23(id)
コーラ
ソーダ
102番地
null
※
※
※実際にはItemTypeも参照です。
(2)LinkTerminalクラス
100番地
102番地
104番地
106番地
108番地
2221
2222
2223
2224
2225
コーラ
ソーダ
オレンジ
紅茶
緑茶
102番地
104番地
106番地
108番地
null
first
last
追加や検索をするために、Exampleクラスは、
常にリストの最初の番地(first)と最後の番地(last)
だけは、知っておく必要があります。
(2)LinkTerminalクラス

今回は引数として、配列ではなく、最初の
番地(first)と最後の番地(last)を渡す必要
があります
ItemTypeList{
add();
Example{
main();
}
delete();
•最初の番地
•最後の番地
search();
display();
}
(2)LinkTerminalクラス

引数用オブジェクトLinkTerminalを作り、そ
れを渡すことにします
//連結リストの始点と終点をもつクラス
public class LinkTerminal {
ItemTypeList{
LinkObject first;//始点
LinkObject last;//終点
}
Example
{
main();
}
add();
delete();
LinkTerminal
オブジェクト
search();
display();
}
5.3.2 連結リストのアルゴリズム



①追加アルゴリズム
②検索アルゴリズム
③削除アルゴリズム
追加(1)一般的な追加
public void add(LinkTerminal linkTerminal,ItemType addItemType){
100番地
102番地
108番地
addLink.data = addItemType;
2221
2222
2223
linkTerminal.last.next = addLink;
コーラ
ソーダ
緑茶
102番地
null
108番地
null
first
last
last
//追加する連結オブジェクトを生成する
LinkObject addLink = new LinkObject();
null
linkTerminal.last = addLink;
}
}
追加(2)一番最初の追加
public void insert(LinkTerminal linkTerminal ,
ItemType addItemType ){
LinkObject addLink = new LinkObject();
addLink.data = addItem;
}
if(linkTerminal.first == null){
linkTerminal. first = addLink;
linkTerminal. last = addLink;
}else{
linkTerminal.last.next = addLink;
linkTerminal.last = addLink;
}
102番地
2223
null
緑茶
null
first last
null
first last
検索(1)
商品種類が見つかる場合
public ItemType search(int searchId,
LinkTerminal linkTerminal){ searchId 2222
LinkObject current=linkTerminal.first;
while(current!=null){
100番地 102番地
if(current.data.id==searchId){
System.out.println(“見つかりました”) ;
2221
2222
return current.data;
}
紅茶
緑茶
current=current.next;
}
102番地
104番地
System.out.println(“見つかりませんでした”);
return null;
}
first currentcurrent
104番地
2223
水
null
last
検索(2)
商品種類が見つからない場合
public ItemType search(int searchId,
LinkTerminal linkTerminal){
LinkObject current= linkTerminal.first;
while(current!=null){
if(current.data.id==searchId){
System.out.println(“見つかりました”) ;
return current.data;
}
current=current.next;
}
System.out.println(“見つかりませんでした”);
return null;
}
searchId
3333
100番地
102番地
2221
2221
紅茶
緑茶
102番地
null
null
first currentcurrent last current
削除(1)
リストの中央で見つかる場合
public ItemType delete(int deleteId,
deleteId 2222
LinkTerminal linkTerminal){
LinkObject current=linkTerminal.first;
100番地
102番地
LinkObject previous;
while(current!=null){
2221
2222
if(current.data.id==deleteId){
previous.next=current.next;
紅茶
緑茶
return current.data;
}
previous=current;
102番地
104番地
null 104番地
current=current.next;
}
}
current
first
current
previous previous
×
104番地
2223
水
null
last
削除(2)
リストの最初で見つかる場合
public ItemType delete(int deleteId,
LinkTerminal linkTerminal){
LinkObject current=first;
LinkObject previous;
while(current!=null){
if(current.data.id==deleteId){
if(currrent==first){
first=current.next;
return current.data;
}
null
}
}
previous
deleteId
2221
100番地
102番地
104番地
2221
2222
2223
紅茶
緑茶
水
102番地
104番地
null
first
current
first
last
削除(3)
リストの最後で見つかる場合
public ItemType delete(int deleteId,
LinkTerminal linkTerminal){
deleteId 2223
LinkObject current= linkTerminal.first;
LinkObject previous;
100番地
102番地
104番地
while(current!=null){
if(current.data.id==deleteId){
2221
2222
2223
if(corrent== linkTerminal.last){
last=previous;
紅茶
緑茶
水
last.next=null;
return current.data;
}
null
null
104番地
null 102番地
previous=current;
current=current.next;
}
}
current
first
last
current current last
previous previous previous
}
×
ガベージ・コレクション

削除するときに無駄なメモリが残ってしまう
きがするのですが?
100番地
101番地
102番地
103番地
104番地
‘お’
‘は’
‘よ’
‘ご’
‘ざ’
101
102
103
104
null
Javaでは、いらないメモリを常に見張っている「ガベージコレクタ」
というすばらしい機能が標準でついています。
誰からも参照されていないオブジェクトを自動的にメモリから消します。