前言
日常开发中,使用最多的就是集合了,所以避免不了对集合的各种操作,本篇文章来看一下,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);
}