DATE : 2006/04/03 (Mon)
「ArrayList と LinkedList の使い分け」では、ランダムアクセスという言葉が出てきました。
リストの要素にアクセスする際には、ランダムアクセスとシーケンシャルアクセス(順次アクセス)の2通りがあります。
ランダムアクセスとは、適当な位置の要素に直接アクセスすることを言い、シーケンシャルアクセスとは、先頭の要素から順番にアクセスすることを言います。(参考文献:ランダムアクセス・順次アクセス(IT 用語辞典 e-Words))
ここでは、リストへランダムアクセスする方法とシーケンシャルアクセスする方法をメモしておきます。
簡単な見分け方
簡単に言うと、java.util.List#get(int) などで要素番号を指定して要素にアクセスするとランダムアクセス、java.util.List#iterate() などから java.util.Iterator を取得して要素にアクセスするとシーケンシャルアクセスになります。
コード例
以下のコードは、List へシーケンシャルアクセスを行った(つもり)の例です。
List list = new ArrayList();
// get メソッドでアクセス
for (int i = 0; i < list.size(); i++) {
Object obj = list.get(i);
// obj を処理
}
// Iterator でアクセス
for (Iterator i = list.iterator(); i.hasNext(); ) {
Object obj = i.next();
// obj を処理
}
しかし、 get メソッドでアクセスする方法は、実はランダムアクセスとなります。
人間から見れば get メソッドでアクセスする方法も Iterator でアクセスする方法も、シーケンシャルアクセスしているように見えます。
しかし、処理されている List 側から見れば、get メソッドでアクセスする方法はランダムアクセスなのです。
と言うのも、get メソッドでアクセスする場合、List の内部は次にアクセスされるであろう要素を知りません。ただ単に i という要素番号が外部から渡されてくるだけです。
それに対し Iterator は、次にアクセスされる要素の番号を内部に持っています。
なので、要素番号を使ってアクセスするとランダムアクセス、Iterator を使ってアクセスするとシーケンシャルアクセスになるというわけです。
LinkedList はランダムアクセスに弱いので、Iterator を使ってアクセスした方が良いと言えそうです。
For-Each ループ
ところで、Iterator を使った処理はちょっと見づらくありませんか?
List list = new ArrayList();
for (Iterator i = list.iterator(); i.hasNext(); ) {
Object obj = i.next();
// obj を処理
}
そこで Java SE 5 からは、「For-Each ループ」が導入されました。
上のコードは、以下のように書き直せます。
List list = new ArrayList();
for (Object obj : list) {
// obj を処理
}
参考文献
DATE : 2006/04/03 (Mon)
(;・∀・)といっても、趣味全開のページですので、あまり面白いものはありませんよ。
今後はブログを中心として、ある程度の分量の文章や趣味で作ったものなどはウェブサイトに掲載、といった形で更新していこうと思います。
DATE : 2006/04/03 (Mon)
java.util.List は、Java プログラミングの中でも頻繁に使用します。特に List の中でも、java.util.ArrayList を使う場面は結構あります。
しかし、List を実装したクラスとして、java.util.LinkedList もあります。
ということで、ArrayList と LinkedList の使い分けをメモしておきます。
ArrayList を使う場合
ArrayList は java.util.RandomAccess を実装しています。
RandomAccess インタフェースを実装しているリストは、ランダムアクセス(適当な位置の要素にアクセスする処理)を行った場合に、一定時間内に要素へアクセスできることを保証しています。
つまり、ランダムアクセスが行われる可能性がある場合は、 ArrayList を使います。
ただし、 ArrayList には弱点もあります。
ArrayList の内部は配列になっています。そのため、要素の追加や削除を行うと、配列の挿入部分を確保するためや空き部分を埋めるために、他の要素の移動を行います。また、あらかじめ指定した容量を超えて要素を追加すると、超えた分量を格納できるだけの配列を新たに生成します。
さらに、大きいオブジェクトを扱う際には、余分な空き領域も大きくなってしまいます。
そのため、大量に要素の追加・削除を行う場合には、処理が遅くなることがあります。
LinkedList を使う場合
ArrayList に対して LinkedList は RandomAccess インタフェースを実装しません。
その代わり、要素の追加・削除には強い構成となっています。
LinkedList の内部はリスト構造になっています。要素ごとに前の要素と後ろの要素を記録することでリストを構成しているため、前後になる要素の記録をいじれば追加や削除は完了します。また、ArrayList のように余分な空き領域はできません。
ただし、先頭や最後尾以外の要素にアクセスする場合は、要素を一つずつ辿っていく必要があります。つまり、ランダムアクセスを行う場合には処理に時間がかかります。
そのため、要素の追加・削除を大量に行う場合でランダムアクセスを行う可能性が低い場合は、 LinkedList を使います。最終的にどれだけの要素を追加するのか不透明な場合や大きなオブジェクトを扱う際には特に有効でしょう。
リストの受け渡しは List で
今回の記事では、 ArrayList と LinkedList の使い分けについてメモしました。
しかし、次のようなコードは避けたほうが無難です。
public LinkedList createList() {
// リストの生成処理
}
public void execute(ArrayList list) {
// リストで何か処理
}
もしも、createList で生成されるリストを ArrayList に変更した場合はどうなるでしょうか。createList で生成されたリストを受け取る側のコードも変更する必要が出てくるかもしれません。
また、 execute メソッドに LinkedList を渡したい場合はどうすればよいでしょうか。わざわざ LinkedList から ArrayList に要素を格納し直すのは大変です。
冒頭の通り、ArrayList や LinkedList は List インタフェースを実装しています。ですので、次のようにすれば ArrayList や LinkedList を意識せずにリストをやりとりできます。
public List createList() {
// リストの生成処理
}
public void execute(List list) {
// リストで何か処理
}
ただし、そのリストに特有のメソッドを使いたい場合や、処理速度・メモリ容量を意識する場合は、あえて ArrayList や LinkedList でやりとりする必要があります。
ちなみに、List は java.util.Collection を実装しています。もしも、リストだけでなくセット(java.util.Set)も扱えるのであれば、Collection でやりとりするのもいいかもしれません。
DATE : 2006/04/02 (Sun)
「30日でできる! OS 自作入門」(川合秀実著、毎日コミュニケーションズ、3800円)を読みました。
上のリンクから飛べる立ち読みコーナーで中身を見ていただければわかるのですが、語り口が軽妙ですし、とても分かりやすく書かれています。実際、読んでいて詰まることがあまりなく、すらすらと読み進めていくことができました。(あとがきによると、中学生でも読めるように全力を尽くしたそうです)
本の中では、アセンブラと C 言語を使って OS を作っていく過程が記されています。しかし、アセンブラにしても C にしても、解説を読めば分かるようになっているところはさすがです。
また、ハードウェア(CPU やレジスタ、メモリなど)の解説があるのも見逃せません。
さすがにこれで回路が組めるほど深い解説があるわけではありませんが、ハードウェアの上でソフトウェアがどう動作しているのか、ディスプレイにどのように出力しているのか、キーボードからの入力をどのように受け取っているのかということがしっかりと分かるように書かれています。
その流れの中で、C 言語のポインタについても触れられています。C 言語のポインタの理解があやふやになっていた私にとって、理解を固めるには最適な解説でした。
また、なんといっても、グラフィックを使った OS がしだいに出来上がっていくという構成が非常に面白いです。次はどのような機能ができあがるのかといった、ドキドキ感もあります。
本の中では、その都度 OS の機能を作ったり、直したりといったライブ的な作業を繰り返しています。冗長に思える部分もあるかもしれません。しかし、 OS のみならず、ソフトウェアを開発する上での考え方を垣間見ることができるのも一つの魅力でした。
プログラムの抽象化が進む世の中ですが、ハードウェアなしにソフトウェアは動きません。OS というハードウェアに近いソフトウェア開発を通して、ハードウェアの知識やプログラミングの考え方を学ぶには、とてもおすすめの本と言えそうです。
この本を足掛かりとして、ハードウェアや OS の本格的な本を読むのもいいかもしれませんね。
DATE : 2006/04/02 (Sun)
JUnit という、単体テストを行うフレームワークがあります。また、Java SE 5 からは、列挙型が導入されました。
Java の列挙型は型の保証や名前空間があってとても便利です。また、メソッドも定義できるため、多態性も容易に実現できます。
しかし、列挙型を JUnit でテストしようとした時にはたと困ってしまいました。
列挙型に定義したメソッドは通常通りにテストできます。しかし、列挙型定数の数が増えた場合には、未テストの定数が出てしまいます。もちろん、列挙型定数の数が増える前に、増えた分のテストコードを書けば問題ないのでしょうが、それを忘れてしまう可能性もあります。
そこで、列挙型のテストクラスには、列挙型定数の数をテストするコードを書くようにしました。
具体的には、次の1行をテストコードに追加します。なお、列挙型定数が4つある列挙型 EnumExample をテストすることを想定しています。
assertEquals(4, EnumExample.values().length());
values メソッドを使うと、EnumExample 内の列挙型定数が配列として定義順に返されます。この配列の要素数を利用することで、列挙型定数の数をテストするわけです。