【三方包系列】倚天劍:apache-common-collections

接著上次的話題,再介紹apache-common系列中另一個重要的成員:apache-common-collections。包中對Java中的集合類進行了一定的補充,定義了一些全新的集合,當然也是實現了Collection接口的,比如Bag,BidiMap。同時擁有新版本的原有集合,比如FastArrayList。最后,更為重要的是一系列utils類,提供了我們常用的集合操作,可以大大方便我們的日常編程。

用apache-common-collections極大提升代碼可讀性和開發速度,實為武器中的倚天劍。

天下武功,唯快不破

我們也根據包的內容分為四塊,

  • 新的集合
  • 新的概念
  • 新版本的老集合
  • 有用的工具類

下面就開始:

新的集合

Bag

Bag定義了一種集合:收集一個對象出現的次數。
例如Bag:{a,a,b,c}
調用bag.getCount(a)返回2,意味著里面有2個a。
調用bag.uniqueSet()則返回一個set,值為{a,b,c}。

這個集合可以用來計數,而如果用set則無法實現,用map可以把count作為value來勉強實現,但顯然在add操作的時候并不優雅,需要:

map.put(key,map.get(key)+1);

并且還需要擔心map中存的不是一個整數。
用Bag計算森林中的兔子:

HashBag bag = new HashBag();
bag.add("rabbit",1);
bag.add("fox",1);
bag.add("rabbit",2);

//rabbit count
System.out.print(bag.getCount("rabbit"));
//how many animals
System.out.print(bag.uniqueSet().size());

除了HashBag,還有SynchronizedBag,TreeBag,可以通過源碼或者javadoc進一步了解使用。

BidiMap

BidiMap定義了一種map,不僅可以通過key得到value,還可以通過value得到key。Bidi意思是bidirectional,雙向使用的map。
除了傳統map的操作,特殊操作如下:

BidiMap bidi = new DualHashBidiMap();
bidi.put("k1","v1");
bidi.put("k2","v2");

bidi.get("k2"); // return v2
bidi.getKey("v2"); // return k2

bidi.inverseBidiMap(); //反轉bidi,原先的value作為key

作為代價,BidiMap必須要求k和v是一一對應的,在上述代碼之后,無法做到bidi.put("k2","v1");,因為這樣就無法實現響應操作。
現實中如學號和身份證號做對應就是這樣一種關系,可以視情況使用。

除了DualHashBidiMap,還有TreeBidiMap等,可以通過源碼或者javadoc進一步了解使用。

新的概念

Predicate 預言

這個類主要結合CollectionUtils.find,CollectionUtils.filter來使用。
他的作用類似于『斷言』,其中只有一個方法:

public boolean evaluate(Object object);

這個方法用于判斷一個對象是否滿足某種標準,你可以通過讓age>50來尋找列表中年齡大于50的元素,或是找到id為12306的那個對象,如

Predicate predicate = new Predicate{
  @override
  public boolean evaluate(Object object){
    return PropertyUtils.getSimpleProperty(object,"age") >50 ;
  }
}
Predicate predicate2 = new Predicate{
  @override
  public boolean evaluate(Object object){
    return PropertyUtils.getSimpleProperty(object,"id") == 12306 ;
  }
}
//刪除不滿足條件的結果
CollectionUtils.filter(list,predicate);
//返回第一個滿足的元素
Object obj = CollectionUtils.find(list,predicate2);

同時,Predicate可以進行謂詞連接,借助于:

  • AndPredicate
  • OrPredicate
  • AnyPredicate
  • NotPredicate
    我們可以創造出id為12306并且年齡大于50的預言
new AndPredicate(predicate1,predicate2);

代碼的復用程度被極大提升,這個類是apache-common包最精髓之處。

Closure和Transformer 代碼閉包

介紹這兩個接口主要目的是擴大思路,因為隨著jdk8的引入,作者認為這兩個接口的作用性就有待商榷了。但是在他們引入的時候,這個思路無疑是值得參考的。
我們從一段代碼來了解這兩個類。

//傳統代碼
..do something
for(obj in list){
  obj.sayHello();
}
..do otherthing

//使用closure
Closure closure = ClosureUtils.invokerClosure("sayHello");

..do something
CollectionUtils.forAllDo(list,closure);
..do otherthing

沒錯,這就是jdk8中大力引入的函數式編程思想。在其他語言(javascript, scala, python等)高度動態性的影響下,開發者們意識到函數式編程的好處。
最大好處在于減少了代碼塊的層次,把for,if,swith,while等等的嵌套代碼結構更改為了順序型的結構,讓代碼可讀性大大提升。實際上作者自己的感觸也是這樣的,一段長代碼的難懂程度跟嵌套代碼的深度成正比。

想像一下:

for(condition){
  if(cond2){
    if(cond4){
      ..do trunk logic
    }else{
      ..do branch logic
    }
  }else{
    if(cond3){
      ..do fast fail logic
    }
    ..do error fixing logic
  }
}

講到這里,可以正式介紹一下Closure和Transformer。他們的含義是封裝一個代碼結構塊,前者是void函數,后者是有返回值的函數,可以理解為對一個對象的transform
jdk8中正式引入了lambda功能,我們的解決方案可以是:

//forEach函數實現內部迭代
list.forEach(o->{System.out.println(o);});

這里想說的是,技術多種多樣,思想不變

老集合的新面孔

這一節我會走馬觀花的列舉一系列類,具體使用可以自行發掘。

  • GrowthList
    為了避免大部分IndexOutOfBoundsException而使用的list容器。對于index越界會安靜的處理:超過length的set和add操作會自動增大list以適應。

  • LazyList
    通過傳入一個factory對象來實現懶加載。同時也具有Growth的特點,比如get一個不存在的index時,會自動create一個對象并返回。

[1,2,3].get(5)會導致:[1,2,3,null,factory.create()]
  • SetUniqueList
    內部是一個set的list,我們經常需要用到沒有重復的列表,會新建一個ArrayList,并最后調用
    new ArrayList(new HashSet(list))來去重。而這個類會自動幫你做好這些。

  • SynchronizedList
    一個線程安全的列表

  • LazyMap
    類似LazyList

  • LRUMap
    一個有限個數的Map,并根據LRU算法來決定哪些元素被丟棄,適合用來做簡易緩存。

  • MultiKeyMap
    多個鍵的Map,譬如操作班級,學號時:

map.put("class1",1001,john);

但看過源碼之后發現是通過合并多個key的hashcode來做到這一點,那么和我們使用普通map時直接put("class1"+1001,john)效果類似。只能理解為提升代碼可讀性了。

  • MultiValueMap
    這個類可以方便的為一個key對應多個value的情況做適應。
    然而如果了解Google的Guava包,實際上里面有一個更好用的類:Multimap這里就不過多介紹了。

  • CompositeCollection
    包括CompositeSet,CompositeMap,用于保持其他容器的同時提供各容器混合的功能。

以CompositeSet為例,
compositeSet.add(set1,set2);
形成了一個二級的set,這一點和set1.addAll(set2)是不一樣的,使用時我們可以同時存在set1,set2和compositeSet。
而對二級set的修改操作會直接影響到子容器。
compositeSet.remove(obj);
如果set1和set2都包含了obj,會同時刪去obj。

有用的工具類

這是collections包中最有價值的一個部分,我準備介紹ListUtilsCollectionUtils

ListUtils 列表工具類
  • ListUtils.intersection(list1,list2)
    取交集
  • ListUtils.subtract(list1,list2)
    返回list1和list2的差。這里和list1.removeAll(list2)的差別在于:
    • 前者不改變任何一個集合
    • 如果list1中有2個a,list2中有一個a:removeAll會將list1中所有的a都抹去,而subtract結果list1仍然會剩下一個a
  • ListUtils.union(list1,list2)
    取并集
  • ListUtils.removeAll(list1,list2)
    不改變list的情況下做removeAll
CollectionUtils 通用的集合工具類
  • CollectionUtils.union(c1,c2),CollectionUtils.intersection(c1,c2)
    不再解釋
  • CollectionUtils.disjunction(c1,c2)
    返回兩者的不相交部分的并集,沒想到一個現實場景。。
  • CollectionUtils.containsAny(c1,c2)
    只要c1包含任何一個c2的元素,返回true

前方高能

  • CollectionUtils.find(c,predicate)
    重要方法:借助Predicate達到神一般的效果,從此減少一半for循環。返回第一個找到的元素

  • CollectionUtils.filter(c,predicate)
    重要方法:同上,直接改變容器c。

  • CollectionUtils.transform(c,transformer)
    重要方法:還是神器,但是在jdk8中等同于foreach方法效果。如果jdk<8,可以用這個方法代替

  • CollectionUtils.countMatches(c,predicate)
    根據predicate返回有多少元素滿足預言,返回值int。

  • CollectionUtils.select(c,predicate)
    根據predicate找出滿足的元素,組成新的collection并返回

  • CollectionUtils.select(c,predicate,outputCollection)
    根據predicate找出滿足的元素,加入到outputCollection中。

  • CollectionUtils.isEmpty(c)
    簡單實用,是否為null或者空集合

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,048評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,414評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,169評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,722評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,465評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,823評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,813評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,000評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,554評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,295評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,513評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,722評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,125評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,430評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,237評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,482評論 2 379

推薦閱讀更多精彩內容