Integer.parseInt("~");とInteger.valueOf("~");の違い

Javaの文字列⇒数値変換についてです。

と見せかけて本当の目的はSyntaxHighlighterの挙動確認です。(SyntaxHighlighterとは)

 

Integer.parseInt("~");とInteger.valueOf("~");の違いは返却値がプリミティブかラッパー型のオブジェクトかだけです。

数値変換した値はプリミティブとして扱うことがほとんどだと思うので、とりあえずInteger.parseInt("~");を使えば良いかと。

 

どちらも"~"の部分が空文字やnullの場合NumberFormatExceptionで落ちるので時と場合に応じて回避か例外処理を忘れずに。

		try {
			int i1 = Integer.parseInt("");
			int i2 = Integer.valueOf("1");
			System.out.println(i1 + " " + i2);
		} catch (NumberFormatException nfe) {
			// 例外処理
			System.out.println(nfe.getMessage());
		}

Collectionの活用 その2 リストの初期サイズ

今回もArrayListについてです。
リストを生成するとき、以下のようにコンストラクタの引数に初期サイズ(int型)を与えることができます。

List<String> list = new ArrayList<String>(100);

この初期サイズって別に設定しなくても要素の追加はできるので、わざわざ設定する必要はあるのでしょうか。

ネットから拾い集めた情報を元に整理したところ、以下の結論に至りました。

①あらかじめリストのサイズがわかっている場合
②リストのサイズが動的に変化し、最大要素数が少ない場合(1000要素以下くらい?)
③リストのサイズが動的に変化し、最大要素数が多い場合
①、③⇒初期サイズを指定する
②⇒初期サイズを指定しない(ただし、最大要素数と最小要素数の差が小さい場合は設定する)


上記を説明するためには、ArrayListの仕組みを理解しておく必要があります。
ArrayListは内部で配列を生成しており、
リストに要素が追加される際に配列のサイズが足りない場合配列を拡張する処理を行っています。

ArrayListの実装は以下の通りです。
1.初期サイズが指定された場合そのサイズの配列を生成。
  初期サイズが指定されていない場合、初期サイズ10の配列を生成。
2.要素がaddされる際に配列のサイズが足りない場合、以下の手順で配列を拡張します。
 (1)サイズの大きい配列(サイズは↓)を新たに生成
  int newCapacity = oldCapacity + (oldCapacity >> 1); ・・・(※)
 (2)古い配列から新しい配列に全要素をコピー
※「>>」はシフト演算子(ビット演算子

どうやらArrayListの初期サイズは上記の配列拡張処理よるパフォーマンスの低下を防ぐために設定するみたいです。
また、配列生成時に初期サイズ分のメモリ領域を確保するため、
無駄に大きいサイズを指定するとこの分のメモリ領域が無駄になってしまいます。

リストに追加する要素の数があらかじめ分かっている場合、
初期サイズを設定することで、配列の拡張を防いでかつ無駄なメモリ容量を食わずに済みます。

リストに追加する要素の数が場合によって変化する場合はどうかというと、
例えば条件によってリストに1000件要素を追加する場合と1件しか追加しない場合があるとして、
初期サイズを1000に設定したにも関わらず1要素しか追加しなかった場合、
用意したメモリ領域がほとんど無駄になってしまいます。
1000件の要素を追加する条件だったとしても、
配列拡張処理の処理時間なんて気にするほどの処理時間ではないので、
メモリ容量を優先して②の結論としました。
要素のサイズが10000を超えてくると、
さすがに処理時間を気にせざるを得なくなるので、③の結論としています。


システムの性能を意識したコーディングをする場合は参考にしてみてください。

 

(豆知識)
HashMapの実態も実は配列です。ハッシュコードをキー値と紐付けて配列から値を取ってくる仕組みのようです。
HashMapのデフォルト初期サイズは16で、
ArrayListと同様にインスタンス化の際に引数に初期サイズを指定することができます。

Collectionの活用 その1 Listの宣言は「List<E> list = new ArrayList<E>();」?

最近Javaの開発でListを多用しているのでCollectionインターフェースについて
整理しておこうと思います。

今回はListの宣言の仕方についてです。


私がプログラマー1年目の時に先輩に指摘されたのですが、
Stringのリストを宣言するときに、基本的には
List<String> list = new ArrayList<String>();
と書きます。

ArrayList<String> list = new ArrayList<String>();
ではダメなのでしょうか。

 

結論を言うと、基本的には

List<String> list = new ArrayList<String>();

で宣言しておくのが良いが、100点ではありません。


そもそも
①List<String> list = new ArrayList<String>();
と宣言するのと
ArrayList<String> list = new ArrayList<String>();
で宣言するのでは何が違うのでしょうか。


Listはインターフェースなので、Listを実装するArrayListクラスではListインターフェースが持つ抽象メソッドを全て実装しています。
addやgetなど、Listの基本的な機能の実装はArrayListクラスに書かれています。
実はArrayListクラスにはListが持つ抽象メソッド以外にも、独自の機能を持っています。
trimToSizeやensureCapacityなどがそれにあたるのですが、ArrayList独自の
使い道のよくわからない機能が実装されています。

これらArrayList独自の機能は①で宣言した場合は使用できず、②で宣言した場合に使用することができます。
言い換えると、trimToSizeやensureCapacityといったメソッドを使いたい場合、②で宣言しなければならいということです。

上記を踏まえると、①で宣言するよりも②で宣言したほうが多くの機能が使えるので
はじめのうちは②で宣言すればいいのではと思いがちです。


ArrayListクラスの機能を使うことが確定しているなら↑の考え方で問題ありません。
しかし、オブジェクト指向のインターフェースの考え方から、仕様と実装は分離するべきであり、オブジェクト指向言語であるJavaで書くなら型はListにするのが好ましいです。

なぜかというと、
もし開発の途中段階で、ArrayListをそのまま使うと不便な個所があり、Listインターフェースを実装した独自のクラスを作成(ここではOriginalArrayListとする)し、そちらを使いたいとなった場合、

①で宣言していた場合、修正はArrayList⇒OriginalArrayListのリファクタリングだけで
済む(Listの機能しか実装していないため)のですが、
②で宣言してしまっていた場合、ArrayListの独自メソッドを使用していた部分の実装は手で修正する必要がでてきます。


Listの例では上記のような修正が入ることはほとんどないですが、
実際の開発ではコーディングの途中、またはコーディング完了後にやっぱり実装を変更しようなんてことがしょっちゅうあります。その際修正範囲をできるだけ限定するためのインターフェースなのかなと思います。

インターフェースを使うメリットと実装側の機能をしっかり理解した上で変数宣言しましょう。

 


ブログの記事を書くのは初めてですが、ずいぶん長い文章になってしまいました…。
読みにくくすみません。
このブログを通してもっと情報をスマートに伝えられるスキルを身に着けるのが課題ですね。
精進します。