不可变对象有很多优点,包括:
- 当对象被不可信的库调用时,不可变形式是安全的;
- 不可变对象被多个线程调用时,不存在竞态条件问题
- 不可变集合不需要考虑变化,因此可以节省时间和空间。所有不可变的集合都比它们的可变形式有更好的内存利用率(分析和测试细节);
- 不可变对象因为有固定不变,可以作为常量来安全使用。
//of方法,有多种参数的变法
public static <E> ImmutableSet<E> of(E element) {
return new SingletonImmutableSet<E>(element);
}
//copyOf方法,有多种参数的变法
public static <E> ImmutableSet<E> copyOf(Collection<? extends E> elements) {
/*
* TODO(lowasser): consider checking for ImmutableAsList here
* TODO(lowasser): consider checking for Multiset here
*/
if (elements instanceof ImmutableSet && !(elements instanceof ImmutableSortedSet)) {
@SuppressWarnings("unchecked") // all supported methods are covariant
ImmutableSet<E> set = (ImmutableSet<E>) elements;
if (!set.isPartialView()) {
return set;
}
} else if (elements instanceof EnumSet) {
return copyOfEnumSet((EnumSet) elements);
}
Object[] array = elements.toArray();
return construct(array.length, array);
}
//使用builder
final ImmutableSet<Color> GOOGLE_COLORS =
ImmutableSet.<Color>builder()
.addAll(WEBSAFE_COLORS)
.add(new Color(0, 191, 255))
.build();
不可变集合可以将map变为常量使用,但是传统的map没有建造模式
public static final ImmutableMap<String, String> PALTFORM_MAP = new ImmutableMap.Builder<String, String>()
.put("100000", "猪八戒主站").put("100001", "猪八戒主站")
.put("100002", "八戒知识产权").put("100005", "天蓬网")
.put("100009", "八戒财税").build();
Multiset是JDK中不存在的一种类型,继承自Collection,与List,Set同级的一种集合类型.
public interface Multiset extends Collection
Multiset multiset = HashMultiset.create();
String sentences = "this is a story, there is a good girl in the story.";
Iterable<String> words = Splitter.onPattern("[^a-z]{1,}").omitEmptyStrings().trimResults().split(sentences);
for (String word : words) {
multiset.add(word);
}
for (Object element : multiset.elementSet()) {
System.out.println((String)element + ":" + multiset.count(element));
}
对应的JDK中的实现:
- HashMultiset: 元素存放于 HashMap
- LinkedHashMultiset: 元素存放于 LinkedHashMap,即元素的排列顺序由第一次放入的顺序决定
- TreeMultiset:元素被排序存放于TreeMap
- EnumMultiset: 元素必须是 enum 类型
- ImmutableMultiset: 不可修改的 Mutiset
每个有经验的Java程序员都在某处实现过Map<K, List>或Map<K, Set>,并且要忍受这个结构的笨拙。 例如,Map<K, Set>通常用来表示非标定有向图。Guava的 Multimap可以很容易地把一个键映射到多个值。 换句话说,Multimap是把键映射到任意多个值的一般方式。
实现 | Key实现 | Value实现 |
---|---|---|
ArrayListMultimap | HashMap | ArrayList |
HashMultimap | HashMap | HashSet |
LinkedListMultimap | LinkedHashMap | LinkedList |
LinkedHashMultimap | LinkedHashMap | LinkedHashSet |
TreeMultimap | TreeMap | TreeSet |
ImmutableListMultimap | ImmutableMap | ImmutableList |
ImmutableSetMultimap | ImmutableMap | ImmutableSet |
具体实现很简单,此处忽略.
我们知道Map是一种键值对映射,这个映射是键到值的映射,而BiMap首先也是一种Map,他的特别之处在于,既提供键到值的映射,也提供值到键的映射,所以它是双向Map.
想象这么一个场景,我们需要做一个星期几的中英文表示的相互映射,例如Monday对应
BiMap<String,String> weekNameMap = HashBiMap.create();
weekNameMap.put("星期一","Monday");
weekNameMap.put("星期二","Tuesday");
weekNameMap.put("星期三","Wednesday");
weekNameMap.put("星期四","Thursday");
weekNameMap.put("星期五","Friday");
weekNameMap.put("星期六","Saturday");
weekNameMap.put("星期日","Sunday");
System.out.println("星期日的英文名是" + weekNameMap.get("星期日"));
System.out.println("Sunday的中文是" + weekNameMap.inverse().get("Sunday"));
键–值实现 | 值–键实现 | 对应的BiMap实现 |
---|---|---|
HashMap | HashMap | HashBiMap |
ImmutableMap | ImmutableMap | ImmutableBiMap |
EnumMap | EnumMap | EnumBiMap |
EnumMap | HashMap | EnumHashBiMap |
通常来说,当你想使用多个键做索引的时候,你可能会用类似Map<FirstName, Map<LastName, Person>>的实现, 这种方式很丑陋,使用上也不友好。Guava为此提供了新集合类型Table,它有两个支持所有类型的键:”行”和”列”。 Table提供多种视图,以便你从各种角度使用它:
- rowMap():用Map<R, Map<C, V>>表现Table<R, C, V>。同样的, rowKeySet()返回”行”的集合Set。
- row(r) :用Map<C, V>返回给定”行”的所有列,对这个map进行的写操作也将写入Table中。
- 类似的列访问方法:columnMap()、columnKeySet()、column(c)。(基于列的访问会比基于的行访问稍微低效点)
- cellSet():用元素类型为Table.Cell<R, C, V>的Set表现Table<R, C, V>。Cell类似于Map.Entry,但它是用行和列两个键区分的。
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了解详情。
实例:
Table<Integer, Integer, String> table = HashBasedTable.create();
for (int row = 0; row < 10; row++) {
for (int column = 0; column < 5; column++) {
table.put(row, column, "value of cell (" + row + "," + column + ")");
}
}
for (int row=0;row<table.rowMap().size();row++) {
Map<Integer,String> rowData = table.row(row);
for (int column =0;column < rowData.size(); column ++) {
System.out.println("cell(" + row + "," + column + ") value is:" + rowData.get(column));
}
}