如果你需要在不可变集合中使用 null,请使用 JDK 中的 Collections.unmodifiableXXX 方法
-
ImmutableSet
http://wiki.jikexueyuan.com/project/google-guava-official-tutorial/immutable-collections.html
https://www.yiibai.com/guava/guava_collections_utilities.html
package com.byedbl.common.collect;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.junit.Test;
import java.util.Collections;
import java.util.Set;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
public class ImmutableSetTest {
@Test
public void testCreation_oneDuplicate() {
// now we'll get the varargs overload
ImmutableSet<String> set =
ImmutableSet.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "a");
assertEquals(
Lists.newArrayList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m"),
Lists.newArrayList(set));
}
@Test
public void testCreation_manyDuplicates() {
// now we'll get the varargs overload
ImmutableSet<String> set =
ImmutableSet.of("a", "b", "c", "c", "c", "c", "b", "b", "a", "a", "c", "c", "c", "a");
assertThat(set).containsExactly("a", "b", "c").inOrder();
}
@Test
public void testCreation_arrayOfArray() {
String[] array = new String[] {"a","b","b"};
Set<String[]> set = ImmutableSet.<String[]>of(array);
assertEquals(Collections.singleton(array), set);
}
@Test
public void testCreation_Builder() {
Set<String> set = ImmutableSet.<String>builder().add("a").add("b").build();
assertEquals(Sets.newHashSet("a","b"),set);
}
@Test
public void testCreation_SortedSet() {
ImmutableSortedSet<String> set = ImmutableSortedSet.of("a", "b", "c", "a", "d", "b");
set.asList().forEach(System.out::println);
}
}
Multiset
继承自Collection
而非 Set
接口,所以其可以包含重复元素
可以用两种方式看待 Multiset:
- 没有元素顺序限制的 ArrayList
- Map<E, Integer>,键为元素,值为计数
package com.byedbl.common.collect;
import com.google.common.collect.Multiset;
import com.google.common.collect.Sets;
import com.google.common.collect.TreeMultiset;
import org.junit.Test;
import java.util.Arrays;
import java.util.Set;
import static org.junit.Assert.assertEquals;
public class MultisetsTest {
// @Test
// public void testNewTreeMultisetDerived() {
// TreeMultiset<DerivedComparable> set = TreeMultiset.create();
// assertTrue(set.isEmpty());
// set.add(new DerivedComparable("foo"), 2);
// set.add(new DerivedComparable("bar"), 3);
// assertThat(set)
// .containsExactly(
// new DerivedComparable("bar"),
// new DerivedComparable("bar"),
// new DerivedComparable("bar"),
// new DerivedComparable("foo"),
// new DerivedComparable("foo"))
// .inOrder();
// }
/**
* 初始化Set值
* @return :
*/
private TreeMultiset<String> getSet() {
TreeMultiset<String> set = TreeMultiset.create();
set.add("foo", 2);
set.add("bar", 3);
set.addAll(Arrays.asList("a", "a", "b", "b"));
return set;
}
@Test
public void testTreeMultiSetSize() {
TreeMultiset<String> set = getSet();
// 统计Set中总个数9个,包含重复的个数
assertEquals(9,set.size());
}
@Test
public void testTreeMultiSetCount() {
TreeMultiset<String> set = getSet();
//取出指定元素的个数
assertEquals(2,set.count("foo"));
}
@Test
public void testTreeMultiSetElementSet() {
Set<String> treeSet = Sets.newTreeSet();
treeSet.addAll(Arrays.asList("a", "bar", "foo","b"));
TreeMultiset<String> set = getSet();
// set.elementSet().forEach(System.out::println);
//返回不重复的两个元素
assertEquals(treeSet,set.elementSet());
}
@Test
public void testTreeMultiSetElementSetSize() {
TreeMultiset<String> set = getSet();
//不重复元素的个数
assertEquals(4,set.elementSet().size());
}
@Test
public void testTreeMultiSetEntrySet() {
TreeMultiset<String> set = getSet();
Set<Multiset.Entry<String>> entries = set.entrySet();
//遍历取出集合中的元素
entries.forEach(entry-> System.out.println(entry.getElement()+" : "+ entry.getCount()));
}
@Test
public void testTreeMultiSetAdd() {
//增加给定元素在 Multiset 中的计数
TreeMultiset<String> set = getSet();
set.add("foo", 3);
assertEquals(5,set.count("foo"));
}
@Test
public void testTreeMultiSetRemove() {
TreeMultiset<String> set = getSet();
set.remove("foo", 1);
assertEquals(1,set.count("foo"));
}
@Test
public void testTreeMultiSetSetCount() {
TreeMultiset<String> set = getSet();
set.setCount("foo", 5);
assertEquals(5,set.count("foo"));
}
}
Map<K, List>或 Map<K, Set>
Multimap 是把键映射到任意多个值的一般方式
ListMultimap.get(key)返回 List,SetMultimap.get(key)返回 Set。
*LinkedListMultimap.entries()保留了所有键和值的迭代顺序。详情见 doc 链接。
**LinkedHashMultimap 保留了映射项的插入顺序,包括键插入的顺序,以及键映射的所有值的插入顺序。
package com.byedbl.common.collect;
import com.google.common.collect.*;
import org.junit.Test;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import static org.junit.Assert.*;
public class MultimapTest {
private Multimap<String, String> getMultimap() {
Multimap<String,String> multimap = HashMultimap.create();
multimap.putAll("foo", Sets.newHashSet("foo1", "foo2"));
multimap.putAll("bar", Sets.newHashSet("bar1", "bar2"));
multimap.put("test", "a");
multimap.put("test", "b");
return multimap;
}
@Test
public void testMultimap() {
Multimap<String, String> multimap = getMultimap();
//遍历打印结果
// test : [a, b]
// foo : [foo2, foo1]
// bar : [bar1, bar2]
printMap(multimap);
}
private void printMap(Multimap<String, String> multimap) {
multimap.asMap().forEach((k,v)-> System.out.println(k + " : "+ v));
}
@Test
public void testMultimapSize() {
Multimap<String, String> multimap = getMultimap();
assertEquals(6, multimap.size());
//Set<K>
assertEquals(3,multimap.keySet().size());
//Multiset<K>
assertEquals(6,multimap.keys().size());
//Collection<V>
assertEquals(6,multimap.values().size());
//Collection<Entry<K, V>>
assertEquals(6,multimap.entries().size());
//Map<K, Collection<V>>
assertEquals(3,multimap.asMap().size());
}
@Test
public void testMultimapRemove(){
Multimap<String, String> multimap = getMultimap();
//删除指定元素
assertTrue(multimap.get("foo").remove("foo2"));
assertTrue(multimap.remove("test","a"));
printMap(multimap);
}
@Test
public void testMultimapRemoveAll() {
Multimap<String, String> multimap = getMultimap();
//清除键对应的所有值,返回的集合包含所有之前映射到 K 的值,
// 但修改这个集合就不会影响 Multimap 了。
//multimap.get(key).clear()
assertArrayEquals(multimap.removeAll("foo").toArray(),new String[]{"foo2","foo1"});
}
@Test
public void testMultimapReplaceValues() {
Multimap<String, String> multimap = getMultimap();
//清除键对应的所有值,并重新把 key 关联到 Iterable 中的每个元素。
// 返回的集合包含所有之前映射到 K 的值。
multimap.replaceValues("foo", Lists.newArrayList("foo3", "foo4"));
assertArrayEquals(multimap.get("foo").toArray(),new String[]{"foo3", "foo4"});
printMap(multimap);
}
@Test
public void testMultimapEntries() {
Multimap<String, String> multimap = getMultimap();
Collection<Map.Entry<String, String>> entries = multimap.entries();
//遍历key和values值
entries.forEach(entry-> System.out.println(entry.getKey()+" : " + entry.getValue()));
}
@Test
public void testMultimapKeySet() {
Multimap<String, String> multimap = getMultimap();
Set<String> set = multimap.keySet();
assertEquals(set,Sets.newHashSet("test","bar","foo"));
set.forEach(System.out::println);
}
@Test
public void testMultimapValues() {
Multimap<String, String> multimap = getMultimap();
Collection<String> collection = multimap.values();
// multimap.values() 就是一个 集合
collection.forEach(System.out::println);
System.out.println("====================");
//asMap().values() 是按照键值分开的集合
// [a, b]
// [foo2, foo1]
// [bar1, bar2]
Collection<Collection<String>> values = multimap.asMap().values();
values.forEach(System.out::println);
}
}
实现键值对的双向映射,解决传统的维护两个HashMap
经常出现的问题
BiMap<K, V>是特殊的 Map:
- 可以用 inverse()反转 BiMap<K, V>的键值映射
- 保证值是唯一的,因此 values()返回 Set 而不是普通的 Collection
BiMap在键和值是唯一的时候很有用,可以随意反转
package com.byedbl.common.collect;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import org.junit.Test;
public class BiMapTest {
private BiMap<String,Integer> getBiMap() {
BiMap<String,Integer> biMap = HashBiMap.create();
biMap.put("语文", 60);
// biMap.put("语文", 70);
biMap.put("数学", 70);
biMap.put("数学", 80);
return biMap;
}
@Test
public void testPutWithSameValueFails() {
BiMap<String,Integer> biMap = getBiMap();
try {
//在 BiMap 中,如果你想把键映射到已经存在的值,会抛出 IllegalArgumentException 异常
biMap.put("语文", 80);
// fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException expected) {
System.out.println("expected error");
}
}
@Test
public void testReverse() {
BiMap<String,Integer> biMap = getBiMap();
biMap.inverse().forEach((k,v)-> {
System.out.println(k + " " + v);
});
}
@Test
public void testForcePutWithSameValueFails() {
BiMap<String,Integer> biMap = getBiMap();
// try {
//在 BiMap 中,如果你想把键映射到已经存在的值,会抛出 IllegalArgumentException 异常
//可以用forcePut替代
biMap.forcePut("语文", 80);
// fail("Expected IllegalArgumentException");
// } catch (IllegalArgumentException expected) {
// }
}
}
解决多个键做索引的问题 Map<FirstName, Map<LastName, Person>>
Table 有如下几种实现:
- HashBasedTable:本质上用 HashMap<R, HashMap<C, V>>实现;
- TreeBasedTable:本质上用 TreeMap<R, TreeMap<C,V>>实现;
- ImmutableTable:本质上用 ImmutableMap<R, ImmutableMap<C, V>>实现;注:ImmutableTable 对稀疏或密集的数据集都有优化。
- ArrayTable:要求在构造时就指定行和列的大小,本质上由一个二维数组实现,以提升访问速度和密集 Table 的内存利用率。ArrayTable 与其他 Table 的工作原理有点不同,请参见 Javadoc 了解详情。
package com.byedbl.common.collect;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import org.junit.Test;
import java.util.Map;
import java.util.Set;
public class TableTest {
private Table<String, String, String> getTable() {
Table<String, String, String> employeeTable = HashBasedTable.create();
//initialize the table with employee details
employeeTable.put("IBM", "101","Mahesh");
employeeTable.put("IBM", "102","Ramesh");
employeeTable.put("IBM", "103","Suresh");
employeeTable.put("Microsoft", "102","Sohan");
employeeTable.put("Microsoft", "112","Mohan");
employeeTable.put("Microsoft", "113","Ram");
employeeTable.put("TCS", "121","Ram");
employeeTable.put("TCS", "122","Shyam");
employeeTable.put("TCS", "123","Sunil");
return employeeTable;
}
@Test
public void testRow() {
Table<String, String, String> employeeTable = getTable();
Map<String,String> ibmEmployees = employeeTable.row("IBM");
System.out.println(ibmEmployees);
ibmEmployees = employeeTable.row("102");
System.out.println(ibmEmployees);
ibmEmployees = employeeTable.column("102");
System.out.println(ibmEmployees);
//get all the unique keys of the table
Set<String> employers = employeeTable.rowKeySet();
System.out.print("Employers: ");
for(String employer: employers){
System.out.print(employer + " ");
}
System.out.println();
System.out.println("-----------------------------");
//get a Map corresponding to 102
Map<String,String> EmployerMap = employeeTable.column("102");
for(Map.Entry<String, String> entry : EmployerMap.entrySet()){
System.out.println("Employer: " + entry.getKey() + ", Name: " + entry.getValue());
}
System.out.println("-----------------------------");
//get a Map corresponding to 102
EmployerMap = employeeTable.column("Ram");
for(Map.Entry<String, String> entry : EmployerMap.entrySet()){
System.out.println("Employer: " + entry.getKey() + ", Name: " + entry.getValue());
}
}
@Test
public void testRowMap() {
// IBM {101=Mahesh, 102=Ramesh, 103=Suresh}
// Microsoft {102=Sohan, 112=Mohan, 113=Ram}
// TCS {121=Ram, 122=Shyam, 123=Sunil}
Table<String, String, String> employeeTable = getTable();
Map<String, Map<String, String>> rowMap = employeeTable.rowMap();
rowMap.forEach((k,v)-> System.out.println(k + " " + v));
}
@Test
public void testColumnMap() {
// 101 {IBM=Mahesh}
// 102 {IBM=Ramesh, Microsoft=Sohan}
// 103 {IBM=Suresh}
// 112 {Microsoft=Mohan}
// 113 {Microsoft=Ram}
// 121 {TCS=Ram}
// 122 {TCS=Shyam}
// 123 {TCS=Sunil}
Table<String, String, String> employeeTable = getTable();
Map<String, Map<String, String>> columnMap = employeeTable.columnMap();
columnMap.forEach((k,v)-> System.out.println(k + " " + v));
}
@Test
public void testCellSet() {
Table<String, String, String> employeeTable = getTable();
Set<Table.Cell<String, String, String>> cells = employeeTable.cellSet();
cells.forEach(c-> System.out.println(c.getRowKey() + " " + c.getColumnKey()+ " " + c.getValue()));
}
}
传入三个值,row,column,value
;既可以用row获取,也可以用columnKey获取值.遍历用cellSet
-
ClassToInstanceMap
特殊的Map
,它的键是类型,而值是符合键所指类型的对象
package com.byedbl.common.collect;
import com.google.common.collect.ClassToInstanceMap;
import com.google.common.collect.MutableClassToInstanceMap;
import org.junit.Test;
public class ClassToInstanceMapTest {
@Test
public void testClassToInstanceMap() {
ClassToInstanceMap<Number> numberDefaults= MutableClassToInstanceMap.create();
for(int i=0;i<10;i++) {
numberDefaults.putInstance(Integer.class, i);
numberDefaults.putInstance(Long.class, (long) i);
}
Integer instance = numberDefaults.getInstance(Integer.class);
//9
System.out.println(instance);
System.out.println(numberDefaults.getInstance(Long.class));
}
}
存放类型与其对应的值,后面的会覆盖前面的,
-
RangeSet
区间,描述了一组不相连的、非空的区间。当把一个区间添加到可变的RangeSet时,所有相连的区间会被合并,空区间会被忽略
package com.byedbl.common.collect;
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.TreeRangeSet;
import org.junit.Test;
public class RangeSetTest {
@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.add(Range.openClosed(0, 0)); //空区间; {[1,10], [11,20)}
rangeSet.remove(Range.open(5, 10)); //分割[1, 10]; 结果为{[1,5], [10,10], [11,20)}
System.out.println(rangeSet);
}
}