0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Guava Collect常见的集合类

科技绿洲 来源:Java技术指北 作者:Java技术指北 2023-10-08 11:35 次阅读

集合操作是编程中使用频率非常高的,所有有一款针对集合的操作工具是非常有必要的。通过框架提供的工具一方面可以减少开发相似功能的耗时;同时框架在安全与稳定性上更被推荐

Guava Collect是Guava工具包中的一个子模块,主要对jdk中的集合操作添加了一些简易的API,同时也是对Collections工具类的扩展。当然Guava还定义了一些特定场景的数据结构以及一些针对jdk集合的优化,最典型的就是Immutable Collections(不可变集合),你会发现调用Guava API很多都是不可变的

意义

我们常见的集合类有:

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

集合是一种非常常见的数据结构,JDK在处理各种数据集时,提供了以上集合类型的数据结构以及其对应API方便开发者高效简易地对数据对象操作

特色

guava主要提供了以下几个方面的支持:

  • 增加了不可变集合
    • 不受信任的库可以安全使用。
    • 线程安全:可以被许多线程使用,没有竞争条件的风险。
    • 不需要支持突变,并且可以通过该假设节省时间和空间。所有不可变集合实现都比它们的可变兄弟更节省内存。(分析)
    • 可以用作常数,期望它保持不变。
  • 增加了新的集合类型
    • Multiset 与普通的Set相比,提供了元素出现频率的记录。可用于元素出现次数的记录
    • Multimap 一个与Map相比,一个建可以对应对应多个值。与Spring中MultiValueMap一样
    • BiMap 键值都是唯一的Map
    • Table 具有行、列的表格,数据视图中可能更直观。
    • ClassToInstanceMap 键为Class,值为Class实例的特殊Map
    • RangeSet 代表一组数据区间,类似数学中的 [1,9)
    • RangeMap 与RangeSet类似,不过将其区间作为建,可以有自己的值。[1,9) -> 'VAL'
  • 优化了常用的操作
    • 集合的创建
      ImmutableSet.of(elem ...)
      Lists.newArrayList(elem ...)
      Sets.newHashSet(elem ...)
      Maps.newHashMap()
      ...
    • 常用的操作 判断两个集合是否相等:Iterables.elementsEqual()
      集合分段处理:Lists.partition()
      取集合的交集:Sets.intersection()
      取集合的差集:Sets.difference()
      ...

使用

Guava Collect作为集合操作工具,我们主要从实际业务中了解其能够帮助我们实现怎样的需求,下面看下其API的使用情况:

假设我们有10000名学生,通过Faker生成这些模拟的学生数据数据:

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
获取元素出现频次。比如获取男生与女生的学生数量分别为多少

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

        System.out.println("学生中男生数量:"+ multiset.count("男"));
        System.out.println("学生中女生数量:"+ multiset.count("女"));
    }

Multimap
一个键对应多个值时。比如查看各个年龄的学生是哪些

@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
键和值都是唯一时。比如处理学生的邮箱和手机号,客户互换键值位置

@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
二维表,通过行(键)、列(键)取值 比如可以以学生为行数据,其中id为行键,列名分别为学生属性名称

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(), "分数", 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
当值是键的类型实例时,通过该Map现在键值关系

@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
区间Set。比如通过学生分数确定学生等级

@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( " 学生:"+ student.getName() );
            System.out.print( ",分数为:"+ 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类似,区别是添加了区间命名。和上面一样

@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( " 学生:"+ student.getName() );
            System.out.print( ",分数为:"+ student.getScore() );
            System.out.print( ",等级为:"+ rangeMap.get(student.getScore()) +"n");
        });
    }

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

当然我们首先需要将数据使用Guava Collect对应的数据结构来存储数据,这样才能使用其对应的API:

  • 集合创建 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); // 拆解集合
  • 计算 Iterables.frequency(list, elem); //元素出现的次数
  • 集合的并集、交集、差集 // 并集 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();
  • 索引 // 将元素中的子项作为索引,由于元素检索 Maps.uniqueIndex() Multimaps.index()

Jdk中的集合操作

自从Jdk中引入了集合Stream的操作后,从很大程度上简化了对集合的操作,以前大量代码现在可能简单几行就能够达到相同的效果,同时支持并发处理,一并提升了效率。

下面看下常见的集合基于stream操作,同样以上面的学生为例:

遍历 forEach

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

转换 map
将元素转换成其他类型。比如根据学生名称、性别组成新的List;以id为键元素为值的Map或者学生姓名拼接的字符串等等

@Test
    public void transform(){
        // 转换为数组
        List< String > listResult = students.stream()
        .map((val)- > val.getName() + ":" + val.getGender()).collect(Collectors.toList());
        System.out.println( listResult );
    
        // 转换成String
        String stringResult = students.stream().map(Student::getName).collect(Collectors.joining());
        System.out.println( stringResult );
    
        // 转换成Map
        Map< String, Student > mapResult = students.stream().collect(
        // key ,value ,mergerOperation, initialization
        Collectors.toMap(Student::getName,Student::self,(v1,v2)- >{
            // 出现相同key时的合并规则
            return null;
            },HashMap::new)
        );
        System.out.println( mapResult );
    }

过滤 filter
根据条件匹配满足要求的元素。如找出分数大于80分的学生

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

拆解 flatMap
将二层级集合进行拆解,并成一级集合。如[[1,2,3],[4,5,6]] -> [1,2,3,4,5,6]

@Test
    public void flatMap(){
        //复合拆解
        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
    }

计算实现数据的汇总、求平均值、最大值...,当然主要针对数字(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();
        // 归约运算 fold . count、sum、min、max、average
        DoubleSummaryStatistics doubleSummaryStatistics = students.stream().mapToDouble(Student::getScore).summaryStatistics();
    }

归纳计算 reduce
在很多语言中都存在的函数,如pythonjavascript。数据的累加、map的功能

@Test
    public void reduce(){
        // 结果和identity(初始值)类型相同
        // identity accumulator combiner
        Map result = students.stream().reduce(
                new HashMap< String,Student >(), //初始值
                (map, student) - > {
                    map.put(student.getId(),student);
                    return map;
                },
                (map1, map2) - > {
                    // 并发执行时的map合并
                    return null;
                }
        );
    }

并发 parallel
上面的操作我们还可以使用parallel对stream并发处理

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

分段处理对集合按固定规格分段处理,处理大批量数据时,结合parallel实现分段并发处理来提示效率

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

总结

本章主要介绍了Guava Collect部分,以及对集合操作的常用API,通过示例可以看到有其对JDK集合的扩展有了更广泛与简易的操作。同时在JDK引入 了Stream操作后,Guava Collect中的很多功能通过Stream也可以比较容易的实现了,当然具体如何选择根据实际情况。需要注意的是Guava Collect中 返回的基本都是不可变的集合,这样在对数据的操作会更加的安全。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 模块
    +关注

    关注

    7

    文章

    2485

    浏览量

    46534
  • 编程
    +关注

    关注

    88

    文章

    3441

    浏览量

    92406
  • Collector
    +关注

    关注

    0

    文章

    3

    浏览量

    5970
  • JDK
    JDK
    +关注

    关注

    0

    文章

    77

    浏览量

    16489
收藏 人收藏

    评论

    相关推荐

    java中的IO流与Guava工具

    Guava IO 日常系统交互中,文件的上传下载都是常见的,一般我们会通过jdk提供的IO操作库帮助我们实现。IO指的是数据相对当前操作程序的入与出,将数据通过 输出流从程序输出,或者通过输入
    的头像 发表于 09-25 16:24 488次阅读

    C语言常见问题

    C语言常见问题汇总大集合
    发表于 05-10 21:37

    java集合干货系列

    :List列表、Set集合、Map映射、工具(Iterator迭代器、Enumeration枚举、Arrays和Collections)。  Collection接口、子接口以及实现
    发表于 12-14 15:11

    Python 集合set添加删除操作

    、创建集合setpython set是在python的sets模块中,新的python版本可以直接创建集合,不需要导入sets模块。具体用法:1. set('old')2.set(‘o’,’l’,’d
    发表于 03-05 15:29

    python入门知识:什么是set集合

    进行交并差等,既可以使用union一的英文方法名,也可以更方便的使用减号表示差集,“&”表示交集,“|”表示并集 。 集合数据类型属于Python内置的数据类型,但不被重视,在很多书籍中甚至都看不到
    发表于 09-24 16:29

    Java 那些最常用的工具

    ());Google Guava集合的创建// 普通集合的创建List list = Lists.newArrayList();Set set = Sets.newHashSet();// 不可变
    发表于 06-15 17:18

    在Labview中使用GC.Collect

    请教各位一下,GC.Collect在Labview中使用,可以释放掉内存么?怎样调用GC.Collect?感谢
    发表于 06-07 08:35

    常见的特征选择方法大致可以分为哪几类呢

    。  常见的特征选择方法大致可以分为三:过滤式、包裹式和嵌入式。2. 过滤式选择  过滤式方法先对数据集进行特征选择,然后再训练学习器。特征选择过程与后续学习...
    发表于 12-20 06:00

    python集合

    python集合集合(英文名 set),它是一个无序的不重复元素序列。这里面有两个重点:无序,不重复1. 创建集合集合的创建有两种方法第一种方法:使用 花括号 {} 直接创建,创建的时候,{} 可以
    发表于 02-23 17:04

    LabVIEW集合

    LabVIEW集合点什么是集合点?它在LabVIEW中有什么用途? 集合点可确保多个线程或VI在继续执行之前在某个执行点等待。每个到达集合点的任务将等待,直到
    发表于 04-09 21:26

    集合与搜索

    集合与搜索:1、集合基本概念•集合:成员的一个群集,成员可以是原子,也可以是集合。成员是无序的。为了处理的方便,在计算机中处理是,通常指定一个线性有序的关系。
    发表于 08-13 14:11 0次下载

    python集合是什么

    python集合 集合(英文名 set),它是一个无序的不重复元素序列。 这里面有两个重点: 无序, 不重复 1. 创建集合 集合的创建有两种方法 第一种方法 :使用 花括号 {} 直
    的头像 发表于 02-23 17:01 1964次阅读

    Guava的这些骚操作,让我的代码量减少了50%

    java中的Map只允许有一个key和一个value存在,但是guava中的Table允许一个value存在两个key。Table中的两个key分别被称为rowKey和columnKey,也就是行和列。(但是个人感觉将它们理解为行和列并不是很准确,看作两列的话可能会更加合适一些)
    的头像 发表于 05-09 10:13 4920次阅读
    <b class='flag-5'>Guava</b>的这些骚操作,让我的代码量减少了50%

    Guava中这些Map的操作,让我的代码量减少了50%

    Guava是google公司开发的一款Java类库扩展工具包,内含了丰富的API,涵盖了集合、缓存、并发、I/O等多个方面。
    的头像 发表于 10-20 11:30 203次阅读
    <b class='flag-5'>Guava</b>中这些Map的操作,让我的代码量减少了50%

    python怎么定义空集合

    在Python中,可以通过两种方式来定义一个空集合: 使用大括号 {} 创建空集合 使用 set() 函数创建空集合 第一种方式是最常见的,直接使用大括号 {} 来创建一个没有任何元素
    的头像 发表于 11-21 16:20 2260次阅读