0%

Guava库学习之Collections

前言

Collections应该是最最常用的了

MultiMap

MultiMap是一个接口,形象的用JDK中的形式解释就是Map<K, Collection<V>>

MultiMap有很多的实现类

HashMultiMap

需要注意的是,HashMultimap每个key对应的value的集合是一个Set
所以下面这个输出是

{World=[2], Hello=[1, 3]}

1
2
3
4
5
6
Multimap<String, Integer> multimap = HashMultimap.create();
multimap.put("Hello", 1);
multimap.put("World", 2);
multimap.put("Hello", 3);
multimap.put("Hello", 3);
System.out.println(multimap);

LinkedListMultimap

上面的value是个Set集合,但是如果我们需要是个List集合呢
就可以使用LinkedListMultimap这个方法
输出

{Hello=[1, 3, 3], World=[2]}

1
2
3
4
5
6
Multimap<String, Integer> multimap = LinkedListMultimap.create();
multimap.put("Hello", 1);
multimap.put("World", 2);
multimap.put("Hello", 3);
multimap.put("Hello", 3);
System.out.println(multimap);

TreeMultimap

HashMultiMap一样,也是value存的是个Set集合,只是这个使用的是红黑树为底层节点。

MultiSet

这个接口的作用大概和Apache CommonBag的概念类似。
虽然名字里带Set,但是其实没有实现Set接口。
比如

1
2
3
4
5
6
7
8
9
Multiset<String> multiset = LinkedHashMultiset.create();
multiset.add("1");
multiset.add("1");
multiset.add("1");
multiset.add("2");
multiset.add("2");
multiset.add("3");
System.out.println(multiset.count("1")); // 3
System.out.println(multiset.elementSet()); // 1 2 3

理解为Bag,我们往包里放了3个1,2个2,1个3。
调用Count我们可以得到里面究竟有多少1。

MultiSet的主要实现有三个类

  • HashMultiSet 底层使用HashMap
  • LinkedHashMultiSet 底层使用LinkedHashMap,就是元素顺序就依次添加进去的
  • TreeMultiSet 底层使用的TreeMap

BiMap

正常我们维护一个K,V的关系是选用一个Map,但是如果我们不仅仅需要K,V 还需要一个V,K的关系呢 也就是正向是个Map,全部反过来也是个Map。
很多人会New两个Map,然后正着放一遍,反着再放一遍

1
2
3
4
5
Map<String, Integer> nameToId = Maps.newHashMap();
Map<Integer, String> idToName = Maps.newHashMap();

nameToId.put("Bob", 42);
idToName.put(42, "Bob");

但是这样会有问题,因为Map中的value并不保证是个Set集合。

Guava提供了BiMap接口,使得我们不需要创建两个Map,而且也保证Value是个Set集合。

1
2
3
4
5
6
7
8
BiMap<String, Integer> biMap = HashBiMap.create();
biMap.put("Hello", 2);
biMap.put("World", 3);
biMap.put("Java", 4);
System.out.println(biMap);

BiMap<Integer, String> inverse = biMap.inverse();
System.out.println(inverse);

如果我们需要反过来的Map,只需要调用inverse方法就行。

Immutable Collections

在原生的JDK中,似乎并没有想严格的区分可变集合和不可变集合。
但是如果你阅读过Effective Java的话,里面在第十五条有提到不可变的对象的好处

  • 不可变的类比可变类更加易于设计、实现和使用
  • 不可变对象本质上是线程安全的,它们不要求同步
    • 首先作为Map的key等这种场景,肯定是适用的
    • 因为无法修改,所以在多线程下的迭代不会抛出异常

就像String推荐作为Mapkey一样,String天生是不可变类,易用且不会出现什么问题。

那么怎么理解不可变集合类的用法呢
个人觉得就像StringBuilderString的用法一样

ImmutableList

创建不可变的List
同时这个类的声明中还带有RandomAccess接口
所以底层是使用了Array来存储元素

如果不清楚我们需要多少元素,可以先创建一个builder
builder()

1
2
3
4
5
6
7
8
9
10
11
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("Hello");
arrayList.add("World");
arrayList.add("Java");

ImmutableList.Builder<String> stringBuilder = ImmutableList.<String>builder()
.add("Hello")
.add("world");
.addAll(arrayList);
stringBuilder.add("Java");
ImmutableList<String> strings = stringBuilder.build();

of()
或者我们可以直接调用of方法如果我们知道已经知道成员变量。

1
ImmutableList<String> list = ImmutableList.of("Hello", "World", "Java");

因为ImmutableList是实现了List接口,所以我们这样也行

1
List<String> list = ImmutableList.of("Hello", "World", "Java");

如果我们拿到的是List方法,那么他的企图修改List的方法已经被标记为@Deprecated
并且如果强行调用会抛出UnsupportedOperationException

copyOf()
如果我们已经有了一个List,想要根据他来创建一个不可变的List

1
2
3
4
5
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("Hello");
arrayList.add("World");
arrayList.add("Java");
ImmutableList<String> immutableList = ImmutableList.copyOf(arrayList);

sortedCopyOf()
之前的List是可以进行Collections.sort()方法进行修改的
但是ImmutableList是不可变的,所以不可以进行sort,或者说在build完成之前是不可以进行sort
那么我们可以调用sortedCopyOf()方法进行构建
那么如果我使用的是Builder方法进行构建,可不可以在build()之前对里面的元素进行排序呢?
似乎没看到方法。

ImmutableSet

其实和上面的ImmutableList差不是很多,就不讲了。

ImmutableBiMap

同时BiMap也提供了不可变的类型
我们可以使用Builder类方法或者of方法去进行创建

1
2
3
4
5
6
7
8
ImmutableBiMap.Builder<String, Integer> builder = ImmutableBiMap.builder();
builder.put("Hello", 2);
builder.put("World", 3);
builder.put("Java", 4);
ImmutableBiMap<String, Integer> biMap = builder.build();
System.out.println(biMap);
BiMap<Integer, String> inverse = biMap.inverse();
System.out.println(inverse);