0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Guava Collect常見的集合類

科技綠洲 ? 來源:Java技術(shù)指北 ? 作者:Java技術(shù)指北 ? 2023-10-08 11:35 ? 次閱讀

集合操作是編程中使用頻率非常高的,所有有一款針對集合的操作工具是非常有必要的。通過框架提供的工具一方面可以減少開發(fā)相似功能的耗時(shí);同時(shí)框架在安全與穩(wěn)定性上更被推薦

Guava Collect是Guava工具包中的一個(gè)子模塊,主要對jdk中的集合操作添加了一些簡易的API,同時(shí)也是對Collections工具類的擴(kuò)展。當(dāng)然Guava還定義了一些特定場景的數(shù)據(jù)結(jié)構(gòu)以及一些針對jdk集合的優(yōu)化,最典型的就是Immutable Collections(不可變集合),你會發(fā)現(xiàn)調(diào)用Guava API很多都是不可變的

意義

我們常見的集合類有:

  • List
  • Set
  • Vector
  • Stack
  • Map
  • Queue

集合是一種非常常見的數(shù)據(jù)結(jié)構(gòu),JDK在處理各種數(shù)據(jù)集時(shí),提供了以上集合類型的數(shù)據(jù)結(jié)構(gòu)以及其對應(yīng)API方便開發(fā)者高效簡易地對數(shù)據(jù)對象操作

特色

guava主要提供了以下幾個(gè)方面的支持:

  • 增加了不可變集合
    • 不受信任的庫可以安全使用。
    • 線程安全:可以被許多線程使用,沒有競爭條件的風(fēng)險(xiǎn)。
    • 不需要支持突變,并且可以通過該假設(shè)節(jié)省時(shí)間和空間。所有不可變集合實(shí)現(xiàn)都比它們的可變兄弟更節(jié)省內(nèi)存。(分析)
    • 可以用作常數(shù),期望它保持不變。
  • 增加了新的集合類型
    • Multiset 與普通的Set相比,提供了元素出現(xiàn)頻率的記錄??捎糜谠爻霈F(xiàn)次數(shù)的記錄
    • Multimap 一個(gè)與Map相比,一個(gè)建可以對應(yīng)對應(yīng)多個(gè)值。與Spring中MultiValueMap一樣
    • BiMap 鍵值都是唯一的Map
    • Table 具有行、列的表格,數(shù)據(jù)視圖中可能更直觀。
    • ClassToInstanceMap 鍵為Class,值為Class實(shí)例的特殊Map
    • RangeSet 代表一組數(shù)據(jù)區(qū)間,類似數(shù)學(xué)中的 [1,9)
    • RangeMap 與RangeSet類似,不過將其區(qū)間作為建,可以有自己的值。[1,9) -> 'VAL'
  • 優(yōu)化了常用的操作
    • 集合的創(chuàng)建
      ImmutableSet.of(elem ...)
      Lists.newArrayList(elem ...)
      Sets.newHashSet(elem ...)
      Maps.newHashMap()
      ...
    • 常用的操作 判斷兩個(gè)集合是否相等:Iterables.elementsEqual()
      集合分段處理:Lists.partition()
      取集合的交集:Sets.intersection()
      取集合的差集:Sets.difference()
      ...

使用

Guava Collect作為集合操作工具,我們主要從實(shí)際業(yè)務(wù)中了解其能夠幫助我們實(shí)現(xiàn)怎樣的需求,下面看下其API的使用情況:

假設(shè)我們有10000名學(xué)生,通過Faker生成這些模擬的學(xué)生數(shù)據(jù)數(shù)據(jù):

List< Student > students = new ArrayList<  >();
Faker faker = new Faker(Locale.CHINA);
@Before
public void init(){
    Faker enFaker = new Faker();
    Name name = faker.name();
    IntStream.range(0,10000).forEach(index- >{
        students.add(
        Student.of()
        .setId(String.valueOf(index+1))
        .setName(name.name())
        .setAge(faker.number().numberBetween(18,22))
        .setGender(new String[]{"男","女"}[faker.number().numberBetween(0,2)])
        .setAddress(faker.address().streetAddress())
        .setScore(faker.number().randomDouble(3,50,100))
        .setEmail( faker.internet().emailAddress(enFaker.name().username()))
        .setTelephone(faker.phoneNumber().cellPhone())
        );
    });
}

Multiset
獲取元素出現(xiàn)頻次。比如獲取男生與女生的學(xué)生數(shù)量分別為多少

@Test
    public void multiset(){
        Multiset multiset = HashMultiset.create();
        students.forEach(student - > {
            if(Objects.equals(student.getGender(),"男")){
                multiset.add("男");
            }else{
                multiset.add("女");
            }
        });

        System.out.println("學(xué)生中男生數(shù)量:"+ multiset.count("男"));
        System.out.println("學(xué)生中女生數(shù)量:"+ multiset.count("女"));
    }

Multimap
一個(gè)鍵對應(yīng)多個(gè)值時(shí)。比如查看各個(gè)年齡的學(xué)生是哪些

@Test
    public void multimap(){
        ListMultimap< Integer, Student > multimap =
                MultimapBuilder.hashKeys().arrayListValues().build();

        students.forEach(student - > {
            multimap.put(student.getAge(),student);
        });

        System.out.println( multimap.get(20) );
    }

BiMap
鍵和值都是唯一時(shí)。比如處理學(xué)生的郵箱和手機(jī)號,客戶互換鍵值位置

@Test
    public void biMap(){
        BiMap biMap = HashBiMap.create();

        students.forEach(student - > {
            biMap.put(student.getEmail(),student.getTelephone());
        });

        BiMap inverse = biMap.inverse();// 鍵值更換

        System.out.println( biMap );
        System.out.println( inverse );
    }

Table
二維表,通過行(鍵)、列(鍵)取值 比如可以以學(xué)生為行數(shù)據(jù),其中id為行鍵,列名分別為學(xué)生屬性名稱

ID姓名年齡性別
1TOM22
@Test
    public void table(){
        Table< String, String, Object > weightedGraph = HashBasedTable.create();
        students.forEach(student - > {
            weightedGraph.put(student.getId(), "姓名", student.getName());
            weightedGraph.put(student.getId(), "年齡", student.getAge());
            weightedGraph.put(student.getId(), "性別", student.getGender());
            weightedGraph.put(student.getId(), "郵箱", student.getEmail());
            weightedGraph.put(student.getId(), "電話", student.getTelephone());
            weightedGraph.put(student.getId(), "地址", student.getAddress());
            weightedGraph.put(student.getId(), "分?jǐn)?shù)", student.getScore());
        });

        Map< String, Object > row = weightedGraph.row("1");
        Map< String, Object > column = weightedGraph.column("姓名");
        Set< Table.Cell< String, String, Object >> cells = weightedGraph.cellSet();

        System.out.println( row );
        System.out.println( column );
        System.out.println( cells );
    }

ClassToInstanceMap
當(dāng)值是鍵的類型實(shí)例時(shí),通過該Map現(xiàn)在鍵值關(guān)系

@Test
    public void classToInstanceMap(){
        ClassToInstanceMap< Number > numberDefaults = MutableClassToInstanceMap.create();
    
        numberDefaults.put(Number.class,1);
    
        Map< Class,Object > objectMap = new HashMap<  >();
        objectMap.put(Number.class,2);
    }

RangeSet
區(qū)間Set。比如通過學(xué)生分?jǐn)?shù)確定學(xué)生等級

@Test
    public void rangeSet(){
        RangeSet< Double > ArangeSet = TreeRangeSet.create();
        ArangeSet.add(Range.closed(90d,100d)); // [90,100]
        RangeSet< Double > BrangeSet = TreeRangeSet.create();
        BrangeSet.add(Range.closedOpen(80d,90d)); // [80,90)
        RangeSet< Double > CrangeSet = TreeRangeSet.create();
        CrangeSet.add(Range.closedOpen(70d,80d)); // [70,80)
        RangeSet< Double > DrangeSet = TreeRangeSet.create();
        DrangeSet.add(Range.closedOpen(60d,70d)); // [60,70)
        RangeSet< Double > ErangeSet = TreeRangeSet.create();
        ErangeSet.add(Range.lessThan(60d)); // [...,60)

        students.forEach(student - > {
            System.out.print( " 學(xué)生:"+ student.getName() );
            System.out.print( ",分?jǐn)?shù)為:"+ student.getScore() );
            String rank = "";
            if(ArangeSet.contains(student.getScore())){
                rank = "A";
            }else if(BrangeSet.contains(student.getScore())){
                rank = "B";
            }else if(CrangeSet.contains(student.getScore())){
                rank = "C";
            }else if(DrangeSet.contains(student.getScore())){
                rank = "D";
            }else if(ErangeSet.contains(student.getScore())){
                rank = "E";
            }
            System.out.print( ",等級為:"+ rank +"n");
        });
    }

RangeMap和RangeSet類似,區(qū)別是添加了區(qū)間命名。和上面一樣

@Test
    public void rangeMap(){
        RangeMap< Double, String > rangeMap = TreeRangeMap.create();
        rangeMap.put(Range.closed(90d,100d),"A"); // [90,100]
        rangeMap.put(Range.closedOpen(80d,90d),"B"); // [80,90)
        rangeMap.put(Range.closedOpen(70d,80d),"C"); // [70,80)
        rangeMap.put(Range.closedOpen(60d,70d),"D"); // [60,70)
        rangeMap.put(Range.lessThan(60d),"E"); // [...,60)

        students.forEach(student - > {
            System.out.print( " 學(xué)生:"+ student.getName() );
            System.out.print( ",分?jǐn)?shù)為:"+ student.getScore() );
            System.out.print( ",等級為:"+ rangeMap.get(student.getScore()) +"n");
        });
    }

下面看下對常用集合的一些操作

當(dāng)然我們首先需要將數(shù)據(jù)使用Guava Collect對應(yīng)的數(shù)據(jù)結(jié)構(gòu)來存儲數(shù)據(jù),這樣才能使用其對應(yīng)的API:

  • 集合創(chuàng)建 FluentIterable.of(elem ...)
    Lists.newArrayList(elem ...)
    Sets.newHashSet(elem ...)
    Maps.newHashMap()
    HashMultiset.create()
    ArrayListMultimap.create()
    Tables.newCustomTable(Maps.newLinkedHashMap(), () -> Maps.newLinkedHashMap())
  • 條件過濾
    FluentIterable.filter(predicate); FluentIterable.anyMatch(predicate); FluentIterable.allMatch(predicate); FluentIterable.firstMatch(predicate);
  • 拆分 Iterables.partition(list, pageSize); // 拆解集合
  • 計(jì)算 Iterables.frequency(list, elem); //元素出現(xiàn)的次數(shù)
  • 集合的并集、交集、差集 // 并集 Sets.union(set1, set2); // 交集 Sets.intersection(set1, set2); // 差集 set1為參考 Sets.difference(set1, set2); // 并集-交集 Sets.symmetricDifference(set1, set2); // 同上 Sets.difference(Sets.union(set1, set2),Sets.intersection(set1, set2) ); // 笛卡爾積 Sets.cartesianProduct(Arrays.asList(Sets.newHashSet(1, 2, 3), Sets.newHashSet(3, 4, 5, 6)); // Map,KV相同的部分 difference.entriesInCommon(); // 同K不同V difference.entriesDiffering(); // 左邊存在的右邊不存的K difference.entriesOnlyOnLeft(); // 右邊存在的左邊不存的K difference.entriesOnlyOnRight();
  • 索引 // 將元素中的子項(xiàng)作為索引,由于元素檢索 Maps.uniqueIndex() Multimaps.index()

Jdk中的集合操作

自從Jdk中引入了集合Stream的操作后,從很大程度上簡化了對集合的操作,以前大量代碼現(xiàn)在可能簡單幾行就能夠達(dá)到相同的效果,同時(shí)支持并發(fā)處理,一并提升了效率。

下面看下常見的集合基于stream操作,同樣以上面的學(xué)生為例:

遍歷 forEach

@Test
    public void forEach(){
        students.stream().forEach(System.out::println);
    }

轉(zhuǎn)換 map
將元素轉(zhuǎn)換成其他類型。比如根據(jù)學(xué)生名稱、性別組成新的List;以id為鍵元素為值的Map或者學(xué)生姓名拼接的字符串等等

@Test
    public void transform(){
        // 轉(zhuǎn)換為數(shù)組
        List< String > listResult = students.stream()
        .map((val)- > val.getName() + ":" + val.getGender()).collect(Collectors.toList());
        System.out.println( listResult );
    
        // 轉(zhuǎn)換成String
        String stringResult = students.stream().map(Student::getName).collect(Collectors.joining());
        System.out.println( stringResult );
    
        // 轉(zhuǎn)換成Map
        Map< String, Student > mapResult = students.stream().collect(
        // key ,value ,mergerOperation, initialization
        Collectors.toMap(Student::getName,Student::self,(v1,v2)- >{
            // 出現(xiàn)相同key時(shí)的合并規(guī)則
            return null;
            },HashMap::new)
        );
        System.out.println( mapResult );
    }

過濾 filter
根據(jù)條件匹配滿足要求的元素。如找出分?jǐn)?shù)大于80分的學(xué)生

@Test
    public void filter(){
        List< Student > filterResult = students.stream().filter((val)- >{
            return val.getScore() >80;
        }).collect(Collectors.toList());
        System.out.println(filterResult);
    }

拆解 flatMap
將二層級集合進(jìn)行拆解,并成一級集合。如[[1,2,3],[4,5,6]] -> [1,2,3,4,5,6]

@Test
    public void flatMap(){
        //復(fù)合拆解
        List< Integer > result = Stream.of(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6))
                .flatMap(subList - > subList.stream())
                .collect(Collectors.toList());
        System.out.println(result);// 1 2 3 4 5 6
    }

計(jì)算實(shí)現(xiàn)數(shù)據(jù)的匯總、求平均值、最大值...,當(dāng)然主要針對數(shù)字(Number)類型

@Test
    public void calculate(){
        // 求和
        double sum = students.stream().mapToDouble(Student::getScore).sum();
        // 最大值
        double max = students.stream().mapToDouble(Student::getScore).max().getAsDouble();
        // 最小值
        double min = students.stream().mapToDouble(Student::getScore).min().getAsDouble();
        // 平均值
        double avg = students.stream().mapToDouble(Student::getScore).average().getAsDouble();
        // 歸約運(yùn)算 fold . count、sum、min、max、average
        DoubleSummaryStatistics doubleSummaryStatistics = students.stream().mapToDouble(Student::getScore).summaryStatistics();
    }

歸納計(jì)算 reduce
在很多語言中都存在的函數(shù),如pythonjavascript。數(shù)據(jù)的累加、map的功能

@Test
    public void reduce(){
        // 結(jié)果和identity(初始值)類型相同
        // identity accumulator combiner
        Map result = students.stream().reduce(
                new HashMap< String,Student >(), //初始值
                (map, student) - > {
                    map.put(student.getId(),student);
                    return map;
                },
                (map1, map2) - > {
                    // 并發(fā)執(zhí)行時(shí)的map合并
                    return null;
                }
        );
    }

并發(fā) parallel
上面的操作我們還可以使用parallel對stream并發(fā)處理

Arrays.asList().stream().parallel()...;
    Arrays.asList().parallelStream()...;

分段處理對集合按固定規(guī)格分段處理,處理大批量數(shù)據(jù)時(shí),結(jié)合parallel實(shí)現(xiàn)分段并發(fā)處理來提示效率

@Test
    public void partition(){
        List< String > list = new ArrayList<  >();
        int partition = 100; //每段100個(gè)元素
    
        int part = list.size() / partition  + (list.size() % partition==0? 0:1);
        Stream.iterate(0, n - > n+1)
                .limit(part)
                .parallel() //并發(fā)
                .map(index - > list.stream().skip(index * partition).limit(partition).parallel().collect(Collectors.toList()))
                .forEach(System.out::println);
    }

總結(jié)

本章主要介紹了Guava Collect部分,以及對集合操作的常用API,通過示例可以看到有其對JDK集合的擴(kuò)展有了更廣泛與簡易的操作。同時(shí)在JDK引入 了Stream操作后,Guava Collect中的很多功能通過Stream也可以比較容易的實(shí)現(xiàn)了,當(dāng)然具體如何選擇根據(jù)實(shí)際情況。需要注意的是Guava Collect中 返回的基本都是不可變的集合,這樣在對數(shù)據(jù)的操作會更加的安全。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 模塊
    +關(guān)注

    關(guān)注

    7

    文章

    2655

    瀏覽量

    47292
  • 編程
    +關(guān)注

    關(guān)注

    88

    文章

    3565

    瀏覽量

    93536
  • Collector
    +關(guān)注

    關(guān)注

    0

    文章

    3

    瀏覽量

    5978
  • JDK
    JDK
    +關(guān)注

    關(guān)注

    0

    文章

    81

    瀏覽量

    16567
收藏 人收藏

    評論

    相關(guān)推薦

    java中的IO流與Guava工具

    Guava IO 日常系統(tǒng)交互中,文件的上傳下載都是常見的,一般我們會通過jdk提供的IO操作庫幫助我們實(shí)現(xiàn)。IO指的是數(shù)據(jù)相對當(dāng)前操作程序的入與出,將數(shù)據(jù)通過 輸出流從程序輸出,或者通過輸入流將
    的頭像 發(fā)表于 09-25 16:24 ?690次閱讀

    java集合干貨系列

    :List列表、Set集合、Map映射、工具(Iterator迭代器、Enumeration枚舉、Arrays和Collections)?! ollection接口、子接口以及實(shí)現(xiàn)
    發(fā)表于 12-14 15:11

    Python 集合set添加刪除操作

    、創(chuàng)建集合setpython set是在python的sets模塊中,新的python版本可以直接創(chuàng)建集合,不需要導(dǎo)入sets模塊。具體用法:1. set('old')2.set(‘o’,’l’,’d
    發(fā)表于 03-05 15:29

    python入門知識:什么是set集合

    進(jìn)行交并差等,既可以使用union一的英文方法名,也可以更方便的使用減號表示差集,“&”表示交集,“|”表示并集 。 集合數(shù)據(jù)類型屬于Python內(nèi)置的數(shù)據(jù)類型,但不被重視,在很多書籍中甚至都看不到
    發(fā)表于 09-24 16:29

    Java 那些最常用的工具

    ());Google Guava集合的創(chuàng)建// 普通集合的創(chuàng)建List list = Lists.newArrayList();Set set = Sets.newHashSet();// 不可變
    發(fā)表于 06-15 17:18

    在Labview中使用GC.Collect

    請教各位一下,GC.Collect在Labview中使用,可以釋放掉內(nèi)存么?怎樣調(diào)用GC.Collect?感謝
    發(fā)表于 06-07 08:35

    D音頻功放IC常見問答

    常見問題解答的第1部分提供了關(guān)于D放大器的概論,并回答了如何進(jìn)行選擇放大器以及D放大器濾波器的設(shè)計(jì)問題。 什么是D放大器 D放大器使
    發(fā)表于 03-28 10:47 ?6945次閱讀

    JAVA集合匯總

    多數(shù)情況下使用。 二、層次關(guān)系 如圖所示:圖中,實(shí)線邊框的是實(shí)現(xiàn),折線邊框的是抽象,而點(diǎn)線邊框的是接口 Collection 接口是集合的根接口,Java中沒有提供這個(gè)接口的直接
    的頭像 發(fā)表于 01-16 11:50 ?3747次閱讀
    JAVA<b class='flag-5'>集合</b><b class='flag-5'>類</b>匯總

    數(shù)組與集合說明

    對數(shù)組與集合進(jìn)行詳細(xì)介紹。
    發(fā)表于 03-17 14:28 ?6次下載
    數(shù)組與<b class='flag-5'>集合</b><b class='flag-5'>類</b>說明

    python集合是什么

    python集合 集合(英文名 set),它是一個(gè)無序的不重復(fù)元素序列。 這里面有兩個(gè)重點(diǎn): 無序, 不重復(fù) 1. 創(chuàng)建集合 集合的創(chuàng)建有兩種方法 第一種方法 :使用 花括號 {} 直
    的頭像 發(fā)表于 02-23 17:01 ?2213次閱讀

    Visual C++ 2010 CLR集合

    在實(shí)際應(yīng)用中,經(jīng)常需要處理集合:一些相同類型或不同類型的對象的集合。其中最常用的集合就是數(shù)組,在前面的章節(jié)中已經(jīng)學(xué)習(xí)了利用System::array對數(shù)組進(jìn)行操作。但是這里的數(shù)組要求
    發(fā)表于 07-14 16:24 ?0次下載

    Map集合基本元素的實(shí)現(xiàn)演變

    說到集合,之前介紹的ArrayList,HashMap可能是大家日常用的最多的,但是對于另一個(gè)集合
    的頭像 發(fā)表于 10-10 16:14 ?379次閱讀
    Map<b class='flag-5'>類</b><b class='flag-5'>集合</b>基本元素的實(shí)現(xiàn)演變

    Guava中這些Map的操作,讓我的代碼量減少了50%

    Guava是google公司開發(fā)的一款Java庫擴(kuò)展工具包,內(nèi)含了豐富的API,涵蓋了集合、緩存、并發(fā)、I/O等多個(gè)方面。
    的頭像 發(fā)表于 10-20 11:30 ?349次閱讀
    <b class='flag-5'>Guava</b>中這些Map的操作,讓我的代碼量減少了50%

    python怎么定義空集合

    在Python中,可以通過兩種方式來定義一個(gè)空集合: 使用大括號 {} 創(chuàng)建空集合 使用 set() 函數(shù)創(chuàng)建空集合 第一種方式是最常見的,直接使用大括號 {} 來創(chuàng)建一個(gè)沒有任何元素
    的頭像 發(fā)表于 11-21 16:20 ?5022次閱讀

    java的util包下有哪些

    在Java的util包下,包含了許多,用于提供各種常見的實(shí)用工具和數(shù)據(jù)結(jié)構(gòu)。以下是一些常見: ArrayList:動態(tài)數(shù)組,可以根據(jù)需要自動調(diào)整大小。 LinkedList:雙向
    的頭像 發(fā)表于 11-22 15:04 ?1056次閱讀