不可变对象集合的创建
- copyOf 方法,如 ImmutableSet.copyOf(set);
- of 方法,如 ImmutableSet.of(“a”, “b”, “c”)或 ImmutableMap.of(“a”,1,“b”, 2);
- Builder 工具,如
public static final ImmutableSet<Color> GOOGLE_COLORS =
ImmutableSet.<Color>builder()
.addAll(WEBSAFE_COLORS)
.add(new Color(0, 191, 255))
.build();
所有不可变集合都有一个 asList()方法提供 ImmutableList 视图,来帮助你用列表形式方便地读取集合元素。例如,你可以使用 sortedSet.asList().get(k)从 ImmutableSortedSet 中读取第 k 个最小元素。
可变集合接口 | 属于JDK还是Guava | 不可变版本 |
---|---|---|
Collection | JDK | ImmutableCollection |
List | JDK | ImmutableList |
Set | JDK | ImmutableSet |
SortedSet/NavigableSet | JDK | ImmutableSortedSet |
Map | JDK | ImmutableMap |
SortedMap | JDK | ImmutableSortedMap |
Multiset | Guava | ImmutableMultiset |
SortedMultiset | Guava | ImmutableSortedMultiset |
Multimap | Guava | ImmutableMultimap |
ListMultimap | Guava | ImmutableListMultimap |
SetMultimap | Guava | ImmutableSetMultimap |
BiMap | Guava | ImmutableBiMap |
ClassToInstanceMap | Guava | ImmutableClassToInstanceMap |
Table | Guava | ImmutableTable |
集合类型
Multiset接口(比较适合统计集合中数据出现几次)
该接口实现了 Collection 接口,并履行了 Collection 接口相关的契约,Guava 提供了一个新集合类型 Multiset,它可以多次添加相等的元素。维基百科从数学角度这样定义 Multiset:”集合[set]概念的延伸,它的元素可以重复出现…与集合[set]相同而与元组[tuple]相反的是,Multiset 元素的顺序是无关紧要的:Multiset {a, a, b}和{a, b, a}是相等的
可以用两种方式看待 Multiset:
- 没有元素顺序限制的 ArrayList
- Map
@Test
public void testMultiset1() {
String strWorld="wer|dfd|dd|dfd|dda|de|dr";
String[] words=strWorld.split("\\|");
List<String> wordList=new ArrayList<String>();
for (String word : words) {
wordList.add(word);
}
Multiset<String> wordsMultiset = HashMultiset.create();
wordsMultiset.addAll(wordList);
for(String key:wordsMultiset.elementSet()){
System.out.println(key+" count:"+wordsMultiset.count(key));
}
}
输出:
dd count:1
dda count:1
de count:1
dfd count:2
wer count:1
dr count:1
Multimap接口
特点:不会有任何键映射到空集合:一个键要么至少到一个值,要么根本就不在Multimap中。
可以用两种方式思考 Multimap 的概念:”键-单个值映射”的集合:
a -> 1 a -> 2 a ->4 b -> 3 c -> 5
或者”键-值集合映射”的映射:
a -> [1, 2, 4] b -> 3 c -> 5
一般来说,Multimap 接口应该用第一种方式看待,但 asMap()视图返回 Map
package com.unis.GuavaStudy;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Ordering;
class Person implements Comparable<Person> {
public String name;
public int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(Person.class).add("name", this.name).add("age", this.age).toString();
}
@Override
public int compareTo(Person that) {
// TODO Auto-generated method stub
return ComparisonChain.
start()
.compare(this.age, that.age)//对对象类型的字段进行排序修改
.compare(this.name, that.name,Ordering.natural().nullsFirst())
.result();
}
}
@Test
public void testMultiMap() {
Multimap<String , Person > multimap = ArrayListMultimap.create();//创建集合
for(int i=0;i<3;i++) {
Person p1 = new Person("steven"+i, i);
multimap.put("England", p1);
}
for(int i=6;i<8;i++) {
Person p1 = new Person("小米"+i, i);
multimap.put("Chinese", p1);
}
System.out.println("keyset="+multimap.keySet());
System.out.println("values="+multimap.values());
System.out.println("asMap()取key="+multimap.asMap().get("England"));
//所有键的个数
System.out.println(multimap.size());
//不同键的个数
System.out.print(multimap.keySet().size());
}
输出结果:
keyset=[England, Chinese]
values=[Person{name=steven0, age=0}, Person{name=steven1, age=1}, Person{name=steven2, age=2}, Person{name=小米6, age=6}, Person{name=小米7, age=7}]
asMap()取key=[Person{name=steven0, age=0}, Person{name=steven1, age=1}, Person{name=steven2, age=2}]
5
2
BiMap
传统上,实现键值对的双向映射需要维护两个单独的 map,并保持它们间的同步。但这种方式很容易出错,而且对于值已经在 map 中的情况,会变得非常混乱。例如:
Map<String, Integer> nameToId = Maps.newHashMap();
Map<Integer, String> idToName = Maps.newHashMap();
nameToId.put("Bob", 42);
idToName.put(42, "Bob");
//如果"Bob"和42已经在map中了,会发生什么?
//如果我们忘了同步两个map,会有诡异的bug发生...
BiMap
table(就和名字一样类似二维表的数据结构)
通常来说,当你想使用多个键做索引的时候,你可能会用类似 Map
@Test
public void testTable() {
Table<String, String, Integer> weightedGraph = HashBasedTable.create();
weightedGraph.put("v1", "v2", 4);
weightedGraph.put("v1", "v3", 20);
weightedGraph.put("v2", "v3", 5);
System.out.println(weightedGraph.row("v1")); // returns a Map mapping v2 to 4, v3 to 20
System.out.println(weightedGraph.column("v3")); // returns a Map mapping v1 to 20, v2 to 5
System.out.println(weightedGraph.columnMap());
System.out.println(weightedGraph.rowKeySet());
}
输出结果:
{v2=4, v3=20}
{v1=20, v2=5}
{v2={v1=4}, v3={v1=20, v2=5}}
[v1, v2]
主要的实现类:
- HashBasedTable:本质上用 HashMap
ClassToInstanceMap
ClassToInstanceMap 是一种特殊的 Map:它的键是类型,而值是符合键所指类型的对象。
为了扩展 Map 接口,ClassToInstanceMap 额外声明了两个方法:T getInstance(Class) 和 T putInstance(Class, T),从而避免强制类型转换,同时保证了类型安全。
ClassToInstanceMap 有唯一的泛型参数,通常称为 B,代表 Map 支持的所有类型的上界
实现上, ClassToInstanceMap实现了Map
MutableClassToInstanceMap<Number> map = MutableClassToInstanceMap.create(); map.putInstance(Integer.class, 100);
map.putInstance(Float.class, 10.01f);
System.out.println(map.getInstance(Integer.class));
System.out.println(map.getInstance(Float.class));
RangeSet
RangeSet描述了一组不相连的、非空的区间。当把一个区间添加到可变的RangeSet时,所有相连的区间会被合并,空区间会被忽略。例如:
@Test
public void testRangeSet() {
RangeSet<Integer> rangeSet = TreeRangeSet.create();
rangeSet.add(Range.closed(1, 10)); // {[1,10]}
rangeSet.add(Range.closedOpen(11, 15));//不相连区间:{[1,10], [11,15)}
rangeSet.add(Range.closedOpen(15, 20)); //相连区间; {[1,10], [11,20)}
rangeSet.remove(Range.open(5, 10)); //分割[1, 10]; {[1,5], [10,10], [11,20)}
Set<Range<Integer>> ranges = rangeSet.asRanges();
System.out.println(ranges);//[[1..5], [10..10], [11..20)]
Preconditions.checkArgument(rangeSet.contains(7), "7 not in the rangeset");//rangeSet 不包含7
}
RangeMap (貌似和前面的比,就是不会把相交集合合并喽?)
描述了”不相交的、非空的区间”到特定值的映射。和 RangeSet 不同,RangeMap 不会合并相邻的映射,即便相邻的区间映射到相同的值。例如:
@Test
public void testRangeMap() {
RangeMap<Integer, String> rangeMap = TreeRangeMap.create();
rangeMap.put(Range.closed(1, 10), "foo"); //{[1,10] => "foo"}
rangeMap.put(Range.open(3, 6), "bar"); //{[1,3] => "foo", (3,6) => "bar", [6,10] => "foo"}
rangeMap.put(Range.open(10, 20), "foo"); //{[1,3] => "foo", (3,6) => "bar", [6,10] => "foo", (10,20) => "foo"}
rangeMap.remove(Range.closed(5, 11)); //{[1,3] => "foo", (3,5) => "bar", (11,20) => "foo"}
Map<Range<Integer>, String> asMapOfRanges = rangeMap.asMapOfRanges();//返回一个不可变的集合用于遍历
System.out.println(asMapOfRanges);
RangeMap<Integer, String> subRangeMap = rangeMap.subRangeMap(Range.closed(8,14));//求子集映射
System.out.println(subRangeMap);
}
输出结果:
{[1..3]=foo, (3..5)=bar, (11..20)=foo}
{(11..14]=foo}