Skip to content

Latest commit

 

History

History
160 lines (132 loc) · 6.67 KB

集合.md

File metadata and controls

160 lines (132 loc) · 6.67 KB

不可变集合

为什么要使用不可变集合

不可变对象有很多优点,包括:

  • 当对象被不可信的库调用时,不可变形式是安全的;
  • 不可变对象被多个线程调用时,不存在竞态条件问题
  • 不可变集合不需要考虑变化,因此可以节省时间和空间。所有不可变的集合都比它们的可变形式有更好的内存利用率(分析和测试细节);
  • 不可变对象因为有固定不变,可以作为常量来安全使用。

使用不可变集合

  //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

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

Multimap(一键多值的Map)

每个有经验的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

具体实现很简单,此处忽略.


BiMap(双向Map)

我们知道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

Table(二维表结构)

通常来说,当你想使用多个键做索引的时候,你可能会用类似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));
    }
}