九陽神功-Guava使用技巧

九陽神功我覺得是一個(gè)比較基礎(chǔ)的武功间唉,能夠反彈傷害(避免垃圾代碼)栋盹,學(xué)了它施逾,張無忌才能駕馭其他更高級(jí)的武功。

介紹

Guava是Google開源的一個(gè)項(xiàng)目例获,github上面的描述為Google core libraries for Java汉额,其實(shí)就是Google內(nèi)部沉淀的一個(gè)java工具類包。它的一些工具類或思想也被JDK認(rèn)可以及引入了榨汤,比如Optional蠕搜,并且在很多其他開源框架也能看到guava的身影,所以學(xué)習(xí)這個(gè)工具類包對(duì)于我們?nèi)粘i_發(fā)是很有幫助的收壕。工具的作用就是提升效能妓灌。

下面我會(huì)大致通過demo介紹下使用,具體細(xì)節(jié)待各位自己深入了解啼器,總會(huì)有你驚喜的地方旬渠。

引入guava

            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>27.1-jre</version>
            </dependency>

字符串操作

Joiner

Joiner用來處理我們常出現(xiàn)的字符串拼接操作。

        List<String> words = Lists.newArrayList("123","456","789",null);

        //不使用guava
        StringBuilder sb = new StringBuilder();
        for(String word : words){
            if(word==null){
                sb.append("default");
            }else {
                sb.append(word);
            }
            sb.append(",");
        }
        if(sb.length()>1){
            sb.deleteCharAt(sb.length()-1);
        }
        System.out.println(sb.toString());

        //使用guava
        System.out.println(Joiner.on(",").useForNull("default").join(words));
        System.out.println(Joiner.on(",").skipNulls().join(words));

        Map<String, String> data = ImmutableMap.of("a", "1", "b", "2");
        System.out.println(Joiner.on(",").withKeyValueSeparator("-").join(data));
        //output:a-1,b-2
        Map<String, Integer> data2 = ImmutableMap.of("a", 1, "b", 2);
        System.out.println(Joiner.on(",").withKeyValueSeparator("-").join(data2));
        //output:a-1,b-2

使用了 guava后代碼是變得多么簡潔端壳,并且這個(gè)工具類是絕對(duì)沒有bug的告丢,我們自己寫這種代碼還保不定出錯(cuò)。

Joiner的使用方式分為三步损谦。

  1. on方法用來設(shè)置鏈接符
  2. 在on方法之后 join方法之前 岖免,我們可以做一些擴(kuò)展操作,比如我上面代碼的useForNull是為null值設(shè)置默認(rèn)值照捡。
  3. join方法用來設(shè)置被操作的集合

除了useForNull之外颅湘,Joiner的擴(kuò)展操作還有

  1. skipNulls 跳過null值
  2. withKeyValueSeparator 用來處理對(duì)Map的輸出

Splitter

Splitter思想和Joiner類似,我們直接看例子

        Splitter.on(",").omitEmptyStrings().splitToList("123,456,789,,23").forEach(a->{
            System.out.println(a);
        });
        Splitter.on(",").limit(2).splitToList("123,456,789,,23").forEach(a->{
            System.out.println(a);
        });
        Splitter.on(",").trimResults().splitToList("12 3, 456 ,789,,23").forEach(a->{
            System.out.println(a);
        });
        Map<String,String> map = Splitter.on(",").withKeyValueSeparator("-").split("1-2,3-5");
        System.out.println(map);

介紹下on后面的擴(kuò)展操作
omitEmptyStrings 用來省略空白
limit 用來限制結(jié)果個(gè)數(shù)栗精,也就是前幾個(gè)分隔符會(huì)生效
trimResults 去除結(jié)果頭尾空格
withKeyValueSeparator 將String轉(zhuǎn)換Map<String,String>

CharMatcher

CharMatcher用來從字符串匹配出自己想要的那部分闯参,操作也被抽象為兩步

  1. 選擇匹配模式
  2. 選擇如何處理這些匹配到的字符

不理解?看下demo就清楚了

        System.out.println(CharMatcher.inRange('0','9').retainFrom("asfds12312fds444"));
        //12312444
        System.out.println(CharMatcher.inRange('0','9').removeFrom("asfds12312fds444"));
        //asfdsfds
        System.out.println(CharMatcher.inRange('0','9').or(CharMatcher.whitespace()).retainFrom("as fds123 12 fds444"));
        // 123 12 444

CharMatcher是相當(dāng)?shù)撵`活悲立,讀者有什么匹配需求看對(duì)應(yīng)API即可鹿寨,我覺得應(yīng)該都能滿足。

集合相關(guān)

新集合

Guava提供了一些自定義的新集合類薪夕,用來解決業(yè)務(wù)開發(fā)中JDK自帶集合滿足不了我們需求的問題脚草。意思就是說,以前你做一個(gè)功能要在老集合上面進(jìn)行復(fù)雜操作原献,但是使用新集合之后馏慨,它直接能滿足你的需求埂淮。

MultiSet

MultiSet的特性是可以用來統(tǒng)計(jì)集合內(nèi)元素出現(xiàn)的次數(shù),在JDK自帶集合類中写隶,我們會(huì)使用以下代碼實(shí)現(xiàn)這個(gè)功能

        List<String> words = Lists.newArrayList("a","b","c","b","b","c");
        Map<String, Integer> counts = new HashMap<String, Integer>();
        for (String word : words) {
            Integer count = counts.get(word);
            if (count == null) {
                counts.put(word, 1);
            } else {
                counts.put(word, count + 1);
            }
        }
        //output 
        // {a=1, b=3, c=2}

但是是用了MultiSet后

        List<String> words = Lists.newArrayList("a","b","c","b","b","c");

        Multiset<String> multiset1 = HashMultiset.create();
        for(String word : words){
            multiset1.add(word);
        }
        System.out.println(multiset1);

        Multiset<String> multiset2 = HashMultiset.create(words);
        multiset2.add("d",4);
        System.out.println(multiset2);
        //output
        //[a, b x 3, c x 2]
        //[a, b x 3, c x 2, d x 4]
        //1

Multiset的實(shí)現(xiàn)類有很多個(gè)倔撞,這邊我使用了HashMultiset。
具體使用上我們通過create方法初始化Multiset實(shí)例樟澜,通過add增加元素误窖,然后通過count可以得到這個(gè)元素出現(xiàn)的次數(shù)。除了通過add增加元素秩贰,在create初始化的時(shí)候霹俺,我們也能傳入數(shù)組進(jìn)行初始化。

SortedMultiset

SortedMultiset是Multiset的變體毒费,增加了針對(duì)元素次數(shù)的排序功能丙唧,接口實(shí)現(xiàn)類為TreeMultiset

使用方式如下

        SortedMultiset<Integer> sortedMultiset = TreeMultiset.create();
        sortedMultiset.add(2,3);
        sortedMultiset.add(3,5);
        sortedMultiset.add(4,4);
        System.out.println(sortedMultiset);
        sortedMultiset = sortedMultiset.descendingMultiset();
        System.out.println(sortedMultiset);
        System.out.println(sortedMultiset.firstEntry().getElement());

        sortedMultiset = sortedMultiset.subMultiset(3,BoundType.OPEN,2,BoundType.CLOSED);
        System.out.println(sortedMultiset);

        //output
        //[2 x 3, 3 x 5, 4 x 4]
        //[4 x 4, 3 x 5, 2 x 3]
        //4
        //[2 x 3]

不過這個(gè)SortedMultiset是針對(duì)元素進(jìn)行排序的,而不是元素次數(shù)觅玻,所以使用這個(gè)集合類的時(shí)候想际,最好保存數(shù)字類型的元素。并且它的subMultiset是針對(duì)這個(gè)排序規(guī)則來的溪厘,比如我上面是倒序的胡本,使用subMultiset是3到2,而不是2到3畸悬。

guava文檔中SortedMultiset的使用案例是用來統(tǒng)計(jì)接口時(shí)延的分布侧甫,所以key為Long類型。

MultiMap

MultiMap可以理解為對(duì)Map<K, List<V>>或Map<K, Set<V>> 的抽象蹋宦,我們在開發(fā)中也肯定經(jīng)常有統(tǒng)計(jì)一個(gè)key下有哪些value之類場景披粟。

        ListMultimap<String,Integer> listMultimap = MultimapBuilder
                .treeKeys()
                .arrayListValues()
                .build();
        listMultimap.put("1",1);
        listMultimap.put("1",2);
        listMultimap.put("2",1);
        System.out.println(listMultimap);

        List<Integer> value = listMultimap.get("1");
        value.add(3);
        System.out.println(listMultimap);

        listMultimap.removeAll("2");
        listMultimap.remove("1",1);
        System.out.println(listMultimap);

        Map<String, Collection<Integer>> mapView = listMultimap.asMap();
        System.out.println(mapView);

        SetMultimap<String,Integer> setMultimap = MultimapBuilder
                .treeKeys()
                .hashSetValues()
                .build();
        //output
        //{1=[1, 2], 2=[1]}
        //{1=[1, 2, 3], 2=[1]}
        //{1=[2, 3]}
        //{1=[2, 3]}

首先我們可以看到MultiMap的初始化采用建造者模式,key和value 的實(shí)現(xiàn)是定制化的冷冗,可以根據(jù)自己具體需求選擇對(duì)應(yīng)實(shí)現(xiàn)守屉。選擇treeKeys就代表key是有序的。
其次通過get方法拿到的value List是淺拷貝蒿辙。
SetMultimap是另外一種MultiMap的實(shí)現(xiàn)拇泛,不同之處么,Set去重思灌。

BiMap

BiMap提供的功能是反轉(zhuǎn)碰镜,就是說Map<K,V>轉(zhuǎn)換為Map<V,K>。通過這個(gè)數(shù)據(jù)結(jié)構(gòu)能夠滿足你需要通過value去查key的需求习瑰,而不是同時(shí)維護(hù)兩個(gè)map。

        BiMap<String,String> biMap = HashBiMap.create();
        biMap.put("scj","programmer");
        //biMap.put("scj2","programmer");

        System.out.println(biMap.get("scj"));
        System.out.println(biMap.inverse().get("programmer"));
        //output
        //programmer
        //scj

通過inverse能夠進(jìn)行反轉(zhuǎn)秽荤。
需要注意的是 value不能重復(fù)甜奄,不然會(huì)報(bào)錯(cuò)柠横。畢竟反轉(zhuǎn)后也是Map,所以value肯定不能重復(fù)课兄。

Table

通過Map這個(gè)結(jié)構(gòu)牍氛,我們可以通過key去找到我們的數(shù)據(jù)。Table的不同之處是烟阐,他提供了兩個(gè)維度去找到我們的數(shù)據(jù)搬俊。

        Table<String,String,String> table = HashBasedTable.create();
        table.put("male","programmer","scj");
        table.put("female","beauty","ss");
        table.put("female","programmer","s2");

        System.out.println(table.get("male","programmer"));
        System.out.println(table.row("male").get("programmer"));
        System.out.println(table.column("programmer").get("female"));

三個(gè)泛型分別為Row,Column,Value,所以這個(gè)數(shù)據(jù)類型叫Table。那么問題來了蜒茄,三維唉擂,四維,五維的叫什么檀葛。玩祟。
get方法通過row和column定位value
row/column方法通過Row/Column的維度得到對(duì)應(yīng)的Map

集合工具類

以下集合工具類的好處是

  1. 提供了一些工廠方法,讓我們創(chuàng)建集合更加方便

我們上面創(chuàng)建新集合屿聋,全部都是通過工廠方法的模式來的空扎,并且guava也提供了JDK原生集合的工廠創(chuàng)建方法,見Lists润讥,Sets转锈,Maps。為什么推崇用工廠方法呢楚殿,因?yàn)樵贘DK8以前泛型不能省略撮慨,代碼冗余。并且工廠方法API除了普通的創(chuàng)建之外也有很多變體勒魔。

        List<String> test = new ArrayList<String>();
        List<String> test2 = Lists.newArrayList();
        List<String> test3 = Lists.newArrayList("1","2");
        List<String> test4 = Lists.newArrayList(test);
  1. 封裝了一些其他方法甫煞,讓我們操縱集合更加方便
    這邊我選取一些guava中我覺得好用的集合工具
工具類 方法 作用
Sets union 求兩個(gè)的set并集
Sets intersection 求兩個(gè)set的交集
Sets difference 求兩個(gè)set的差集
Maps difference 返回MapDifference用于比較兩個(gè)Map的并/交/左差/右差集

緩存

這邊推薦一個(gè)guava cache的升級(jí)版框架(性能提升),Caffeine冠绢,兼容guava cache api

Guava提供了一個(gè)基于本地緩存的工具類抚吠,很好的封裝了緩存的一些特性,使用方式如下弟胀。

        LoadingCache<String,String> cache = CacheBuilder.newBuilder()
                .maximumSize(100)
                .maximumWeight(1000)
                .weigher(new Weigher<String, String>() {
                    @Override
                    public int weigh(String key, String value) {
                        return key.length();
                    }
                })
                .expireAfterAccess(10, TimeUnit.MINUTES)
                .expireAfterAccess(10, TimeUnit.MINUTES)
                .build(new CacheLoader<String, String>() {
                    @Override
                    public String load(String key) throws Exception {
                        return key+"cache";
                    }
                });
        cache.put("test","23333");
        System.out.println(cache.get("test"));
        System.out.println(cache.get("scj"));
        //output
        //2333
        //scjcache

同樣的楷力,使用建造者模式進(jìn)行初始化。針對(duì)初始化孵户,我總結(jié)了以下幾點(diǎn)萧朝。

  1. 設(shè)置緩存容量
  2. 設(shè)置緩存過期策略
  3. 設(shè)置緩存生成策略
    緩存大小和過期策略都是為了解決就是應(yīng)用內(nèi)存有限以及緩存有效性的問題。
    對(duì)于緩存大小有Size和Weight兩種模式夏哭。
    Size針對(duì)緩存的個(gè)數(shù)來設(shè)置上限检柬。

上面代碼只是為了說明使用方式,兩種模式只能設(shè)置一種

Weight可以通過Weigher函數(shù)針對(duì)不同的緩存來返回不同Weight,所有緩存累加值不能超過maximumWeight竖配。
當(dāng)緩存容量超過限制值后何址,我們就需要根據(jù)緩存過期策略淘汰一些緩存里逆。
expireAfterAccess會(huì)在緩存read或write后指定時(shí)間后失效。
expireAfterWrite會(huì)在緩存write后指定時(shí)間后失效用爪。

上面代碼只是為了說明使用方式,兩種模式只能設(shè)置一種

緩存生成策略通過CacheLoader來封裝我們緩存的生成邏輯原押。我們可以預(yù)先初始化緩存,當(dāng)get的時(shí)候偎血,如果key不在緩存中诸衔,就會(huì)通過CacheLoader來生成我們的緩存。

最后

上面只介紹了guava中一小部分的常用工具類颇玷,還是很建議讀者全面了解一下笨农,等遇到需求時(shí),也算是一種解決方案亚隙。下面我會(huì)貼上guava的wiki鏈接磁餐,基本常用的都有介紹,有能力的同學(xué)也可以通過看源碼來深入學(xué)習(xí)guava阿弃。

參考

guava wiki

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末诊霹,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子渣淳,更是在濱河造成了極大的恐慌脾还,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件入愧,死亡現(xiàn)場離奇詭異鄙漏,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)棺蛛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門怔蚌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人旁赊,你說我怎么就攤上這事桦踊。” “怎么了终畅?”我有些...
    開封第一講書人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵籍胯,是天一觀的道長。 經(jīng)常有香客問我离福,道長杖狼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任妖爷,我火速辦了婚禮蝶涩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己子寓,他們只是感情好暗挑,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著斜友,像睡著了一般。 火紅的嫁衣襯著肌膚如雪垃它。 梳的紋絲不亂的頭發(fā)上鲜屏,一...
    開封第一講書人閱讀 49,760評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音国拇,去河邊找鬼洛史。 笑死,一個(gè)胖子當(dāng)著我的面吹牛酱吝,可吹牛的內(nèi)容都是我干的也殖。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼务热,長吁一口氣:“原來是場噩夢啊……” “哼忆嗜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起崎岂,我...
    開封第一講書人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤捆毫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后冲甘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绩卤,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年江醇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了濒憋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡陶夜,死狀恐怖凛驮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情律适,我是刑警寧澤辐烂,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站捂贿,受9級(jí)特大地震影響纠修,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜厂僧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一扣草、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦辰妙、人聲如沸鹰祸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蛙婴。三九已至,卻和暖如春尔破,著一層夾襖步出監(jiān)牢的瞬間街图,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來泰國打工懒构, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留餐济,地道東北人焕窝。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓临梗,卻偏偏與公主長得像节猿,于是被迫代替她去往敵國和親凉蜂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子用押,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348