Google Guava之Maps&Lists&Sets

简介: 日常开发中,使用最多的就是集合了,所以避免不了对集合的各种操作,本篇文章来看一下,Guava中都有哪些常用的集合操作的API可以简化我们的代码。

前言

iShot2022-12-06 02.24.29.png
日常开发中,使用最多的就是集合了,所以避免不了对集合的各种操作,本篇文章来看一下,Guava中都有哪些常用的集合操作的API可以简化我们的代码。

Lists

详见:https://guava.dev/releases/27.0.1-jre/api/docs/com/google/common/collect/Lists.html
在Lists类中支持构造 ArrayList、LinkedList、newCopyOnWriteArrayList对象的方法。

package com.google.common.collect;

import ...;

/**
 * Static utility methods pertaining to {@link List} instances. Also see this
 * class's counterparts {@link Sets}, {@link Maps} and {@link Queues}.
 *
 * <p>See the Guava User Guide article on <a href=
 * "https://github.com/google/guava/wiki/CollectionUtilitiesExplained#lists">
 * {@code Lists}</a>.
 *
 * @author Kevin Bourrillion
 * @author Mike Bostock
 * @author Louis Wasserman
 * @since 2.0
 */
@GwtCompatible(emulated = true)
public final class Lists {
  private Lists() {}
}

newArrayList

  • 构造ArrayList对象,如果你想设置初始化容量大小,你可以通过newArrayListWithCapacity构建。
    public static void main(String[] args) throws Exception {

        List<String> strs = Lists.newArrayListWithCapacity(10);
        List<Integer> ints = Lists.newArrayList(1, 2, 3);
    }

reverse

  • 将集合中的元素进行反转,如下:
    public static void main(String[] args) throws Exception {

        List<Integer> ints = Lists.newArrayList(1, 2, 3);

        List<Integer> reverse = Lists.reverse(ints);

        // [1, 2, 3]->[3, 2, 1]
        System.out.println(String.format("%s->%s", ints, reverse));
    }

transform

  • 它用于将list转化为另一种list,比如在请求响应中,DTO与VO的转换等。
/**
 * @author Duansg
 * @date 2022-11-29 10:50 下午
 */
public class Example {


    public static void main(String[] args) throws Exception {

        List<String> names = Lists.newArrayList("小明", "小王", "小刘");

        final List<User> transform = Lists.transform(names, new Function<String, User>() {
            @Nullable
            @Override
            public User apply(@Nullable String name) {
                return User.builder().name(name).build();
            }
        });
        // [Example.User(name=小明), Example.User(name=小王), Example.User(name=小刘)]
        System.out.println(transform);
    }

    @Data
    @Builder
    @ToString
    static class User {

        private String name;

    }
}

partition

  • 分割List,但需要注意的是,分割完的list还是指向原来的list,它底层其实是用下标控制的。
    public static void main(String[] args) throws Exception {

        List<String> names = Lists.newArrayList("小明", "小王", "小刘");

        // 分割为每个长度为2,最后一个长度小于2,返回一个list集合。
        List<List<String>> partitions = Lists.partition(names, 2);

        partitions.forEach(p -> {
            // [小明, 小王]
            // [小刘]
            System.out.println(p);
        });

    }

Maps

package com.google.common.collect;

import ...;

/**
 * Static utility methods pertaining to {@link Map} instances (including instances of
 * {@link SortedMap}, {@link BiMap}, etc.). Also see this class's counterparts
 * {@link Lists}, {@link Sets} and {@link Queues}.
 *
 * <p>See the Guava User Guide article on <a href=
 * "https://github.com/google/guava/wiki/CollectionUtilitiesExplained#maps">
 * {@code Maps}</a>.
 *
 * @author Kevin Bourrillion
 * @author Mike Bostock
 * @author Isaac Shum
 * @author Louis Wasserman
 * @since 2.0
 */
@GwtCompatible(emulated = true) // 表示该类型兼容Google Web Toolkit
public final class Maps {
  private Maps() {}
}

newHashMap

  • 构造HashMap对象,如果你想创建HashMap的时候设置其期望的大小,你可以通过newHashMapWithExpectedSize构建。
    public static void main(String[] args) throws Exception {

        HashMap<String, String> map1 = Maps.newHashMapWithExpectedSize(10);
        
        HashMap<String, String> map2 = Maps.newHashMap();

    }

newHashMapWithExpectedSize跟new HashMap(int initialCapacity)不同的是,newHashMapWithExpectedSize设置的是期望值,而不是固定值,我们可以通过它的源码来分析它是如何处理的,如下:

/**
   * Creates a {@code HashMap} instance, with a high enough "initial capacity"
   * that it <i>should</i> hold {@code expectedSize} elements without growth.
   * This behavior cannot be broadly guaranteed, but it is observed to be true
   * for OpenJDK 1.7. It also can't be guaranteed that the method isn't
   * inadvertently <i>oversizing</i> the returned map.
   *
   * @param expectedSize the number of entries you expect to add to the
   *        returned map
   * @return a new, empty {@code HashMap} with enough capacity to hold {@code
   *         expectedSize} entries without resizing
   * @throws IllegalArgumentException if {@code expectedSize} is negative
   */
  public static <K, V> HashMap<K, V> newHashMapWithExpectedSize(int expectedSize) {
    // 虽然底层还是new HashMap(int initialCapacity)
    // 但是会对initialCapacity做处理
    return new HashMap<K, V>(capacity(expectedSize));
  }

  /**
   * Returns a capacity that is sufficient to keep the map from being resized as
   * long as it grows no larger than expectedSize and the load factor is >= its
   * default (0.75).
   */
  static int capacity(int expectedSize) {
    if (expectedSize < 3) {
      checkNonnegative(expectedSize, "expectedSize");
      // 期望值小于3,就期望值+1
      return expectedSize + 1;
    }
    if (expectedSize < Ints.MAX_POWER_OF_TWO) {
      // This is the calculation used in JDK8 to resize when a putAll
      // happens; it seems to be the most conservative calculation we
      // can make.  0.75 is the default load factor.
      // 若期望值不小于3,则先增加1/4倍,为什么要这么做呢?
      // 是因为HashMap本身是会扩容的,扩容是先将原来的数组大小*2,再将原来数组上的值迁移到新的数组上,这个过程是有开销的。
      // 而hashMap本身的负载因子是0.75 若你初始化容器大小为16,则到了 16*0.75 = 12 的时候会进行扩容。
      // 而你在初始化的时候,是大概率知道这个Map的容器有多大,应该装多少,因此才去设置了初始值16。
      // 而又因为HashMap有提前阈值(即容器达到12的时候)就扩容的,因此设置初始值就可以有点变化。
      // 比如容器感觉只会用到10,你就初始化new HashMap(14) 即 14 = 10/0.75 + 1 (加1 是为了向上取整)
      // 那么这个HashMap到达 14*0.75 = 10.5 的时候才会扩容,即你容器存到10个时候,都不会扩容
      // 也就是所谓的用空间换时间。
      return (int) ((float) expectedSize / 0.75F + 1.0F);
    }
    return Integer.MAX_VALUE; // any large value
  }

uniqueIndex

  • Maps.uniqueIndex(Iterable,Function),通常针对的场景是一组对象,它们在某个属性上分别有独一无二的值,而我们希望能够按照这个属性值查找对象。
  • 这个方法返回一个Map,键为Function返回的属性值,值为Iterable中相应的元素,因此我们可以反复用这个Map进行查找操作。
     public static void main(String[] args) throws Exception {
        List<User> users = Lists.newArrayList(
                User.builder().name("小明").build(),
                User.builder().name("小王").build()
        );
        Map<String, User> userMap = Maps.uniqueIndex(users, User::getName);

        // Example.User(name=小明)
        System.out.println(userMap.get("小明"));


    }

    @Data
    @Builder
    @ToString
    static class User {

        private String name;

    }

transformValues

  • 根据Map的value进行转换。
    public static void main(String[] args) throws Exception {

        final Map<String, Integer> map = Maps.transformValues(Maps.newHashMap(ImmutableMap.of("小明", 22)), new Function<Integer, Integer>() {
            @Nullable
            @Override
            public Integer apply(@Nullable Integer input) {
                return input + 1;
            }
        });

        // {小明=23}
        System.out.println(map);

    }

transformEntries

  • 根据Map的key、value进行转换。
    public static void main(String[] args) throws Exception {

        final Map<String, Integer> map = Maps.transformEntries(Maps.newHashMap(ImmutableMap.of("小明", 22, "小王", 22)), new Maps.EntryTransformer<String, Integer, Integer>() {
            @Override
            public Integer transformEntry(@Nullable String key, @Nullable Integer value) {
                if ("小明".equals(key)) {
                    return value + 1;
                }
                return value;
            }
        });

        // {小明=23, 小王=22}
        System.out.println(map);

    }

filterKeys

  • 根据Map的key进行过滤。
public static void main(String[] args) throws Exception {

        final Map<String, Integer> map = Maps.filterKeys(Maps.newHashMap(ImmutableMap.of("小明", 23, "小王", 22)), new Predicate<String>() {
            @Override
            public boolean apply(@Nullable String input) {
                return "小明".equals(input);
            }
        });
    
        // {小明=23}
        System.out.println(map);

}

filterValues

  • 根据Map的value进行过滤
public static void main(String[] args) throws Exception {

        final Map<String, Integer> map = Maps.filterValues(Maps.newHashMap(ImmutableMap.of("小明", 23, "小王", 22)), new Predicate<Integer>() {
            @Override
            public boolean apply(@Nullable Integer input) {
                return Integer.valueOf(23).equals(input);
            }
        });
        // {小明=23}
        System.out.println(map);
}

filterEntries

  • 根据Map的Entry进行过滤
public static void main(String[] args) throws Exception {

        final Map<String, Integer> map = Maps.filterEntries(ImmutableMap.of("小明", 23, "小王", 22), new Predicate<Map.Entry<String, Integer>>() {
                    @Override
                    public boolean apply(@Nullable Map.Entry<String, Integer> input) {
                        if ("小明".equals(input.getKey()) && Integer.valueOf(23).equals(input.getValue())) {
                            return true;
                        }
                        return false;
                    }
                });
        // {小明=23}
        System.out.println(map);

    }

Multimaps

index

通常针对的场景是:有一组对象,它们有共同的特定属性,我们希望按照这个属性的值查询对象,但属性值不一定是独一无二的,如下所示:

public static void main(String[] args) throws Exception {
    final ImmutableListMultimap<Integer, String> index = Multimaps.index(ImmutableSet.of("one", "two", "three"), new Function<String, Integer>() {
        @Nullable
        @Override
        public Integer apply(@Nullable String input) {
            return input.length();
        }
    });

    // {3=[one, two], 5=[three]}
    System.out.println(index);

}

ImmutableMap

ImmutableMap是不可变的map,Guava其实提供了每个集合的的不可变版本,使用ImmutableMap映射,每当尝试修改它时,它都会抛出UnsupportedOperationException。

builder

  • 构造一个map;
    public static void main(String[] args) throws Exception {
        final ImmutableMap<String, Integer> map = ImmutableMap.<String, Integer>builder()
                .put("小明", 22)
                .put("小王", 22)
                .build();

        // {小明=22, 小王=22}
        System.out.println(map);
    }

of

  • of方法入参最多只能有5对,如果超过5对,还是通过builder构建吧。
    public static void main(String[] args) throws Exception {

        final ImmutableMap<String, Integer> map = ImmutableMap.of("小明", 22, "小王", 22);

        // {小明=22, 小王=22}
        System.out.println(map);
    }

Sets

/**
* Static utility methods pertaining to {@link Set} instances. Also see this
* class's counterparts {@link Lists}, {@link Maps} and {@link Queues}.
*
* <p>See the Guava User Guide article on <a href=
* "https://github.com/google/guava/wiki/CollectionUtilitiesExplained#sets">
* {@code Sets}</a>.
*
* @author Kevin Bourrillion
* @author Jared Levy
* @author Chris Povirk
* @since 2.0
*/
@GwtCompatible(emulated = true)
public final class Sets {
    private Sets() {}
}

newHashSet

  • 构建一个set
public static void main(String[] args) throws Exception {

    Set<String> names = Sets.newHashSet("小明", "小王");

    // [小明, 小王]
    System.out.println(names);
}

intersection

  • 也就是求交集,返回两个set中共同存在的元素
    public static void main(String[] args) throws Exception {

        Set<String> names1 = Sets.newHashSet("小明", "小王");
        Set<String> names2 = Sets.newHashSet("小明", "小张");
        Sets.SetView<String> intersection = Sets.intersection(names1, names2);

        // [小明]
        System.out.println(intersection);
    }

difference

  • 也就是求差集,difference(Set set1, Set<?> set2),返回在set1中但不在set2中的元素,只在set2中不在set1中的元素也会被忽略。
    public static void main(String[] args) throws Exception {

        Set<String> names1 = Sets.newHashSet("小明", "小王");
        Set<String> names2 = Sets.newHashSet("小明", "小张");
        Sets.SetView<String> difference = Sets.difference(names1, names2);

        // [小王]
        System.out.println(difference);
    }

union

  • 也就是求并集。
    public static void main(String[] args) throws Exception {

        Set<String> names1 = Sets.newHashSet("小明", "小王");
        Set<String> names2 = Sets.newHashSet("小明", "小张");
        Sets.SetView<String> union = Sets.union(names1, names2);

        // [小明, 小王, 小张]
        System.out.println(union);
    }

symmetricDifference

  • symmetricDifference(Set<? extends E> set1, Set<? extends E> set2),获取在set1或set2中,但不在两者交集中的元素。也就是set1和set2的并集减去交集。
    public static void main(String[] args) throws Exception {

        Set<String> names1 = Sets.newHashSet("小明", "小王");
        Set<String> names2 = Sets.newHashSet("小明", "小张");
        Sets.SetView<String> symmetricDifference = Sets.symmetricDifference(names1, names2);

        // [小王, 小张]
        System.out.println(symmetricDifference);
    }
目录
相关文章
|
4月前
Google Guava ListeningExecutorService
Google Guava ListeningExecutorService
28 0
|
6月前
|
Java 数据库连接
提升编程效率的利器: 解析Google Guava库之IO工具类(九)
提升编程效率的利器: 解析Google Guava库之IO工具类(九)
|
6月前
|
缓存 Java Maven
深入解析Google Guava库与Spring Retry重试框架
深入解析Google Guava库与Spring Retry重试框架
|
6月前
|
监控 安全 算法
提升编程效率的利器: 解析Google Guava库之RateLimiter优雅限流(十)
提升编程效率的利器: 解析Google Guava库之RateLimiter优雅限流(十)
|
6月前
|
缓存 安全 Java
提升编程效率的利器: 解析Google Guava库之集合工具类-50个示例(八)
提升编程效率的利器: 解析Google Guava库之集合工具类-50个示例(八)
|
6月前
|
缓存 算法 Java
提升编程效率的利器: 解析Google Guava库之常用工具类-40个示例(七)
提升编程效率的利器: 解析Google Guava库之常用工具类-40个示例(七)
|
6月前
|
存储
提升编程效率的利器: 解析Google Guava库之集合篇RangeMap范围映射(六)
提升编程效率的利器: 解析Google Guava库之集合篇RangeMap范围映射(六)
提升编程效率的利器: 解析Google Guava库之集合篇RangeSet范围集合(五)
提升编程效率的利器: 解析Google Guava库之集合篇RangeSet范围集合(五)
|
6月前
|
存储 安全 Java
提升编程效率的利器: 解析Google Guava库之集合篇Table二维映射(四)
提升编程效率的利器: 解析Google Guava库之集合篇Table二维映射(四)
|
6月前
|
安全 Java 测试技术
提升编程效率的利器: 解析Google Guava库之集合篇BitMap(三)
提升编程效率的利器: 解析Google Guava库之集合篇BitMap(三)