Apache commonsが便利な件(commons-collections編-1)
http://d.hatena.ne.jp/daisuke-m/20080702/1214982943
再びユーティリティ系。ということで今回はcommons-collections。
commons-collections は、Javaコレクションフレームワーク(っていうと大仰だけど、要はListとかSetとかMap。以下JCF)まわりのユーティリティ。
ところで、JCFまわりは Java1.4から5.0へのバージョンアップにあたり、ジェネリクスが導入されて、非常に使いやすくなりました。今やジェネリクスのないコレクションなんて、怖くて触りたくない病に罹っている今日この頃です。
しかし、このcommons-collectionsは下位互換を理由にジェネリクス化されていません。ジェネリクス世代としては、敬遠せざるを得ません><。
が、どうやら本家Apacheからフォークしてcommons-collectionsをgenerics化したライブラリがあります。
ここでは、1番のライブラリにフォーカスを当ててご紹介します。
CollectionUtils, ListUtils, SetUtils, MapUtils
毎度おなじみ、ユーティリティ。コレクションを「集合」として扱い、そのunion, intersection, disjunction,
subtract の算出なんてのも。あんま使わない?
ListUtils.EMPTY_LIST 等が定数定義されている。コードで語るためのおなじみですね。
他に、ListUtils.isEqualList() は「2つのリストの要素と順番が一致するかどうか」トカトカ。
CollectionUtils.get(x, 3)は、xがMapだったとしても3番目の要素が取れるとか、ちょっと変態なモノも。気になれば、読んでみるほうが早い。
さて、実はcommons-collectionの要はUtilsではない。様々なコレクションの実装が目玉です。また、List, Set,
Map の他に、新しいパラダイムのコレクションを定義し、その実装を提供しています。今回は新パラダイムの紹介。
Bag
追加された要素の数を数えるコレクションです。
Bag bag = ...; bag.add(a); bag.add(a); bag.add(b); bag.add(c); assert bag.getCount(a) == 2; assert bag.getCount(b) == 1; assert bag.getCount(c) == 1;
このインターフェイスはCollectionを継承していますが、リスコフの置換原則に違反しています。二度目以降にaddされたオブジェクトはコレクション内に保持されず、単純にカウンタを増やすだけ、という動作をします。BagのインスタンスをCollectionとして扱う際は、注意しましょう。
Buffer
この型もCollectionを継承したもので、「要素を特定の順番に基づいて取得・削除する」操作を定義しています。代表的なのは「スタック」と「キュー」ですね。FIFOとFILO。その他の順序に基づかせることも可能です。どんな順番なのかは、実装が定義します。
JCFにはStackも定義されていますが、可能ならばStack型とQueue型ににBufferをimplementsしたいところですw
Buffer buffer = ...; // FILO(Queue)だと仮定する buffer.add(a); buffer.add(b); buffer.add(c); assert buffer.get() == c; assert buffer.remove() == c; assert buffer.remove() == b; assert buffer.remove() == a; buffer.get(); // BufferUnderflowException
MultiMap (MultiHashMap)
Mapは、同じキーのエントリを複数持つことができず、既に同じキーのエントリを保持していた場合は、上書きされる仕様となっています。
しかしこのMultiMapは、複数のエントリを持つことができます。従って、このタイプは、Mapのサブタイプではありません。サブタイプ化すると、リスコフの置換原則に違反してしまいます。
Number key = new Integer(5); MultiMap<Number,String> mm = new MultiHashMap<Number,String>(); mm.put(key, "a"); mm.put(key, "b"); mm.put(key, "c"); Collection<String> coll = mm.get(key); // == [a,b,c]
BidiMap
Bidirectional map の略です。双方向マップ、ですね。
何が双方向なのか。一般的なMapは、keyからvalueを引くことができますが、valueからkeyを引くことはできません。頑張れば(エントリを全検索…)取得することもできますが、パフォーマンスは最悪です。
BidiMapでは双方向のルックアップが、同じパフォーマンスで可能です。ただし、Mapの「keyが衝突したときに上書きされてしまう」という特性がvalue側にも適用されてしまいます。
BidiMap bm = ...; bm.put(a, b); assert bm.get(a) == b; assert bm.getKey(b) == a; bm.put(a, c); assert bm.get(a) == c; assert bm.getKey(c) == a; bm.put(b, c); assert bm.get(a) == null; assert bm.get(b) == c; assert bm.getKey(c) == b;
BoundedCollection, BoundedMap
容量制限付きコレクション。
BoundedCollection bc = ...; // 制限2 assert bc.isFull() == false; assert bc.maxSize() == 2; bc.add(a); bc.add(b); assert bc.isFull() == true; bc.add(c); // BufferOverflowException
さて、長くなってきたので、次回に続く。次回は各実装についてー。