はじめに
この文書は、javaを書き始めた人が最初に躓きそうなデータの持ち方について説明していきます。概念から段階的に詳細に落とし込んでいきますので、楽しく読んでみてください。貴方の理解の助けになったらうれしいな。また、この文章は特定の言語実装、プロセッサの仕様を論じてはいません。あくまでも概念とアイデアの説明だと割り切って読んでください。
データってなんだっけ?
データ。すごく広すぎる言葉ですよね。ファイルであっても、DBであっても、Queueであっても何かの情報がコンピュータ上にのりさえすれば、それは「データ」そんなデータ全般の話は僕の手にあまるので、もうちょっと狭くします。この文章での「データ」はプロセッサにフェッチされた命令が利用するデータです。ユーザプログラマ観点で言えば、変数に配列する値1つ1つをデータとよんでいます。
int a = 0
boolean b = false
aもbもデータです。そしてこのデータをプロセッサがどうやって利用するのかは、この記事の配列編を読んでください。とにかく大切なことは
- データのを主記憶からひっぱりだしてくるには、そのデータの「場所」=「アドレス」を知る必要があるよ
- 配列では、先頭アドレスとオフセットを表すindexそして、格納されてるデータのサイズがわかれば自動的にアドレスが分かるよ。
文字列型配列の実現
さて、この方法、いつでも有効なんでしょうか?実はそうでもないんですよね。有効なのは「最初からデータのサイズが決まっている」場合だけなんです。java を例にとるとint/long/float/boolean/double/byte/char ですね。これらの型はあらかじめ1つのデータのサイズが決まっています。この様な型をjavaではプリミティブ型と呼びます。
- プリミティブ型は1つのデータのサイズが固定されている。
- プリミティブ型はデータ長が固定されているから、配列に直接格納できる。
ところで、皆さんがよく扱うデータは常に長さが固定されているのでしょうか?よく使いたいのは「文字列」なのでは?「文字列長」っていつでも固定されていますか?あれれ?おかしいぞ。
String[] str = {"文字1","moji2","moji"};
この配列は先の説明と食い違うのです。配列ってメモリ領域を区切りなく確保するだけだよね?。こう思えた人。凄いですね。String はデータ長が一定ではないのです。どうやって格納しているのでしょうか。
解決方法1
1文字1データとして、配列に格納。各文字列の「長さ」を管理する。という方法。ただし、strの長さは[3]であるのに、配列の「値が決まるまで」連続領域の大きさが決まらない・・・という配列の性質=利点を台無しにする問題が発生!! この方式では「配列」ではなくなってしまいます。
解決方法2
実際にはこちらの方法です。ポイントは「配列のデータ」に直接文字列を要れないこと!!です。え?領域を用意してデータを用意しないんですか?はい。いれません。直接入れる必要はないんです。
もう一度復習します。
- プロセッサはデータを利用する時に主記憶からデータを読みだす必要がある。
- そのデータを読みだすためにはデータの配置場所=アドレスを知る必要がある。
です。つまり、 「アドレスさえわかれば、ぶっちゃけ方法はなんでもいい」 のです。
「文字列」の場合、メモリ上の適当な領域にデータを配置。配置した場所を配列で管理するのです。配列のデータとしては、符号なしintだけ格納できればそれでいいので、「常に同じ長さのメモリ領域をデータの個数文だけ確保する」という配列のルールを維持することが可能です。文字列データにアクセスする場合は以下の順序でデータをフェッチします。
- 配列の開始アドレスを起点として取得する
- 添え字*4byteを算出し、配列のデータ開始アドレスを決定
- そこから4byte分のデータをint型として読みだし
- 読みだした値が示すアドレスを文字列の先頭アドレスとして利用
- 2byte*文字個数分だけのデータフェッチ(ここ本当はちょと違う)
1手間余計にかかりますが、きちんと「配列のルール」をまもりつつ、データを取得することが可能です。
名称の導入
新しく出てきたものには名前を付けていきます。配列の中に格納している実際のデータを格納している「アドレス」情報を 「参照」 といいます。また、文字列型のように「サイズが一様に決定できないデータ」を「オブジェクト型」と呼びます。オブジェクト型については今後、後続の記事で深堀していくので、今は「へーほーふーん」でなんとなく名前だけ覚えておいてください。
中締め
ここまでの話をまとめると
- データのサイズが「型」によって一定に決まっているデータをプリミティブ型というよ
- 逆にデータサイズを一様に決められないデータをオブジェクト型というよ
- 配列は「1データのサイズ*データ数」のメモリ領域を連続で用意するよ
- オブジェクト型を無理やり配列の中にいれると、配列のいいところが台無しになるよ
- オブジェクト型の配列は「参照」を格納しておくよ
- 実際のオブジェクトは、配列とは全然関係ない場所に置かれているよ
- データは「アドレスさえわかれば利用できるよ」
ところで、オブジェクト型に配列って必要?
配列編の配列のメリット・デメリットをもう一度思い出すと。
メリット
機能実装が容易高速に動作する
今回見たように、プリミティブ型の配列に比べて処理が面倒くさいですよね。なのでこれらのメリットはそうとう薄まります。
デメリット
ではデメリットはどうなるの??
- サイズの動的変更ができない
- プログラムの可読性が悪い
こっちのデメリットはそのままに、メリットが薄まるわけです。なんだか損ですよね。なので「だったらもうちょっと遅くてもプログラムを書くのが楽な方がいいな」つまり
- サイズの動的変更したい
- プログラムの可読性を上げたい
って欲求の方が勝ってくるんですよね。
4次元ポケットぉぉぉ
配列のもともとの欲求は、データを集合で扱いたい のであって、メモリの使い方とかそんなことはどうでもいいのです。「データの場所=アドレス」さえ決定することができればなんでもいいのです。そこで、こんな機能があったら便利ですよね。「4次元ポケット」
合言葉で、参照を格納しておく。データが必要になったら、4次元ポケットに「この合言葉のデータがほしい」っていうと、その合言葉に対応する参照を取り出せる。。。そんな4次元ポケットが僕は欲しい。もちろん、4次元ポケットなんだから、「サイズなんて考えなくていいんだよね」ってやつ。。ないかなぁ~
あるよ (田中要次)
ネタがちょっと古い データを合言葉で格納して、必要に応じて取り出せるサイズ無制限の「4次元ポケット」それがMapです。1
ここではMapの一種HashMapを使ってみましょう。
import java.util.HashMap;
//<中略>
public static void main(String[] args){
HashMap map = new Map();//4次元ポケットを装備!!
map.put("心の叫び1","王様の耳はロバの耳");//4次元ポケットにデータを格納。合言葉は「心の叫び1」
map.put("心の叫び2","世界の中心で愛を叫びたい");//4次元ポケットにデータを格納。合言葉は「心の叫び2」
map.put("ドラミちゃん","めっちゃかわEEE");//4次元ポケットにデータを格納。合言葉は「ドラミちゃん」
System.out.println("データを取り出してみる。"+map.get("ドラミちゃん"));//4次元ポケット!! 合言葉「ドラミちゃん」のデータを取り出して!!
}
//<後略>
}
これを実行すると。
データを取り出してみる。めっちゃかわEEE
こんな感じで、好きに出し入れできるのです。なんか便利じゃありませんか?
//以下ちょっとせつめい入れますが今は、置いといて//
-
Java ではMap, Perlでは連想配列,PythonではDictionary(辞書)として提供されています。 ↩