java基础-guava集合(二)

如果你需要在不可变集合中使用 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。

image.png

*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);
    }
}

推荐阅读更多精彩内容

  • com.google.common.collect 1、不可变集合 何为“不可变” 无法修改返回容器的内容,注意,...
    拾壹北阅读 1,361评论 0 4
  • 不可变集合类 为什么要使用不可变集合不可变对象有很多优点,包括: 当对象被不可信的库调用时,不可变形式是安全的;不...
    icecrea阅读 1,090评论 1 0
  • Guava简单介绍 1 资料链接 极客学院http://wiki.jikexueyuan.com/project/...
    田园小丁阅读 1,188评论 0 3
  • 作者:李硕 秋天,一片片黄叶从树梢落下,被风吹着,在空中悠悠地飘, 黄昏吹着风的软,细雨点洒在花前。 这一路走来,...
    潍科WK阅读 53评论 0 0
  • 遇见你 年轮已裂成两半 造物主的心事 流落在掌间 据说你是印度的主 虚构一场历劫 抵达神灵的眉心 高高在上,日夜诵...
    客尘人阅读 55评论 3 4