网站 建设 步骤是,免费的行情网站app网页推荐,荣耀手机全部型号,网站建设行业衰落深入Guava集合操作
在Java开发中#xff0c;Google Guava库是处理集合的强大工具。起源于Google内部需求#xff0c;Guava以简洁性、性能优化为理念#xff0c;提供高效不可变集合和实用工具类。本文深入剖析Guava的核心功能#xff0c;为开发者呈现集合操作的全新视角Google Guava库是处理集合的强大工具。起源于Google内部需求Guava以简洁性、性能优化为理念提供高效不可变集合和实用工具类。本文深入剖析Guava的核心功能为开发者呈现集合操作的全新视角无论经验水平都能获得实用技巧和深刻见解。
一、不可变集合
1、为什么使用不可变集合
不可变对象有很多优点包括
当对象被不可信的库调用时不可变形式是安全的不可变对象被多个线程调用时不存在竞态条件问题可变集合不需要考虑变化因此可以节省时间和空间。所有不可变的集合都比它们的可变形式有更好的内存利用率分析和测试细节不可变对象因为有固定不变可以作为常量来安全使用。
2、创建不可变集合的方式
copyOf方法如ImmutableSet.copyOf(set);of方法如ImmutableSet.of(“a”, “b”, “c”)或 ImmutableMap.of(“a”, 1, “b”, 2);Builder工具如
private static final ImmutableSetString SET ImmutableSet.Stringbuilder().add(a,b).addAll(Lists.newArrayList(c,d)).build();此外对有序不可变集合来说排序是在构造集合的时候完成的如 ImmutableSortedSet.of(a, b, c, a, d, b);
会在构造时就把元素排序为a, b, c, d。
3、asList视图
所有不可变集合都有一个asList()方法提供ImmutableList视图来帮助你用列表形式方便地读取集合元素。例如你可以使用sortedSet.asList().get(k)从ImmutableSortedSet中读取第k个最小元素。
asList()返回的ImmutableList通常是——并不总是——开销稳定的视图实现而不是简单地把元素拷贝进List。也就是说asList返回的列表视图通常比一般的列表平均性能更好比如在底层集合支持的情况下它总是使用高效的contains方法。
二、关联可变集合和不可变集合
可变集合接口属于JDK还是Guava不可变版本CollectionJDKImmutableCollectionListJDKImmutableListSetJDKImmutableSetSortedSet/NavigableSetJDKImmutableSortedSetMapJDKImmutableMapSortedMapJDKImmutableSortedMapMultisetGuavaImmutableMultisetSortedMultisetGuavaImmutableSortedMultisetMultimapGuavaImmutableMultimapListMultimapGuavaImmutableListMultimapSetMultimapGuavaImmutableSetMultimapBiMapGuavaImmutableBiMapClassToInstanceMapGuavaImmutableClassToInstanceMapTableGuavaImmutableTable
三、新集合类型
1、Multiset
Multiset可以多次添加相等元素集合[set]概念的延伸它的元素可以重复出现…与集合[set]相同而与元组[tuple]相反的是Multiset元素的顺序是无关紧要的Multiset {a, a, b}和{a, b, a}是相等的
可以用两种方式看待Multiset
没有元素顺序限制的ArrayListMapE, Integer键为元素值为计数
1、常见方法
方法描述int count(E)给定元素在Multiset中的计数SetE elementSet()Multiset中不重复元素的集合类型为SetESetMultiset.EntryE entrySet()和Map的entrySet类似返回SetMultiset.EntryE其中包含的Entry支持getElement()和getCount()方法int add(E, int)增加给定元素在Multiset中的计数boolean add(E element)增加一个指定的元素到multisetboolean contains(E element)判断此多集中是否包含指定的元素boolean containsAll(Collection? elements)判断此多集至少包含一个出现指定集合的所有元素remove(E, int)减少给定元素在Multiset中的计数,删除指定元素removeAll(Collection? c)删除包含在指定集合中的元素boolean retainAll(Collection? e)保持包含指定集合中的元素int setCount(E, int)设置给定元素在Multiset中的计数不可以为负数添加/删除指定元素使其达到所期望的元素个数int size()返回集合元素的总个数包括重复的元素Iterator iterator()返回一个迭代器包含Multiset的所有元素包括重复的元素
(2)、示例 /*** MultiSet*/Testpublic void multiSetTest(){MultisetString multiset HashMultiset.create();ListString list Lists.newArrayList(a,b,c,d,a,c,d,a,d,a);multiset.addAll(list);System.out.println(a的个数multiset.count(a));System.out.println(multiset的个数multiset.size());SetString set multiset.elementSet();System.out.println(不重复元素 Joiner.on(,).join(set));IteratorString iterator multiset.iterator();System.out.println(multiset元素Joiner.on(,).join(iterator));SetMultiset.EntryString entrySet multiset.entrySet();MapString,Integer setMap Maps.newHashMap();entrySet.forEach(e - {setMap.put(e.getElement(),e.getCount());});System.out.println(元素详情Joiner.on(;).withKeyValueSeparator().join(setMap));multiset.remove(a,2);System.out.println(删除a后a的个数multiset.count(a));System.out.println(是否包含Listmultiset.containsAll(Lists.newArrayList(a,c)));System.out.println(是否包含Listmultiset.containsAll(Lists.newArrayList(a,c,e)));}3、SortedMultiset
SortedMultiset是Multiset 接口的变种它支持高效地获取指定范围的子集
2、MultiMap
Multimap可以很容易地把一个键映射到多个值。换句话说Multimap是把键映射到任意多个值的一般方式。 可以用两种方式思考Multimap的概念”键-单个值映射”的集合 a - 1 a - 2 a -4 b - 3 c - 5 或者”键-值集合映射”的映射 a - [1, 2, 4] b - 3 c - 5 一般来说Multimap接口应该用第一种方式看待但asMap()视图返回MapK, Collection让你可以按另一种方式看待Multimap。重要的是不会有任何键映射到空集合一个键要么至少到一个值要么根本就不在Multimap中。 很少会直接使用Multimap接口更多时候你会用ListMultimap或SetMultimap接口它们分别把键映射到List或Set。
(1)、常用方法
方法描述等价于boolean put(K, V)添加键到单个值的映射multimap.get(key).add(value)boolean putAll(K, IterableV)依次添加键到多个值的映射Iterables.addAll(multimap.get(key), values)remove(K, V)移除键到值的映射如果有这样的键值并成功移除返回true。multimap.get(key).remove(value)removeAll(K)清除键对应的所有值返回的集合包含所有之前映射到K的值但修改这个集合就不会影响Multimap了。multimap.get(key).clear()replaceValues(K, IterableV)清除键对应的所有值并重新把key关联到Iterable中的每个元素。返回的集合包含所有之前映射到K的值。multimap.get(key).clear(); Iterables.addAll(multimap.get(key), values)MapK,CollectionV asMap()获取MultiMap的视图键值K以及K对应的集合void clear()清除所有的键值对boolean containsEntry(Object key,Object value)判断是否包含key-value对应的键值对boolean containsKey(Object key)判断是否包含键值keyboolean containsValue(Object value)判断是否包含值valueCollectionMap.EntryK,V entries()MultiMap为MapEntry情况下返回所有的键值对集合CollectionV get(K k)返回键k对应的所有集合boolean isEmpty()判断MultiMap是否是空即不包含键值对MultiSetK keys()返回所有的键值K包含重复SetK keySet()返回所有的键值K不重复int size()返回键值对的数量CollectionV values返回所有的value
(2)、示例 /*** MultiMap*/Testpublic void multiMapTest(){MultimapString,String multimap HashMultimap.create();multimap.putAll(lower,Lists.newArrayList(a,b,c,d));multimap.putAll(upper,Lists.newArrayList(A,B,C,D));MapString, CollectionString asMap multimap.asMap();System.out.println(asMap视图Joiner.on(;).withKeyValueSeparator().join(asMap));MultisetString multisetKey multimap.keys();System.out.println(所有的key:Joiner.on(,).join(multisetKey.iterator()));SetString keySet multimap.keySet();System.out.println(不重复的keyJoiner.on(,).join(keySet));System.out.println(lower:Joiner.on(,).join(multimap.get(lower)));multimap.put(lower,e);System.out.println(添加后的lower:Joiner.on(,).join(multimap.get(lower)));System.out.println(upper:Joiner.on(,).join(multimap.get(upper)));multimap.remove(upper,D);System.out.println(移除元素后的upperJoiner.on(,).join(multimap.get(upper)));System.out.println(是否包含lower-b:multimap.containsEntry(lower,b));System.out.println(是否包含lower-b:multimap.containsEntry(lower,f));System.out.println(是否包含key(upper):multimap.containsKey(upper));System.out.println(是否包含value(c):multimap.containsValue(c));CollectionMap.EntryString,String collection multimap.entries();System.out.println(MultiMap详情Joiner.on(;).withKeyValueSeparator().join(collection));CollectionString values multimap.values();System.out.println(MultiMap所有的valueJoiner.on(,).join(values));}(3)、Multimap不是Map
MultimapK, V不是MapK,Collection虽然某些Multimap实现中可能使用了map。它们之间的显著区别包括 Multimap.get(key)总是返回非null、但是可能空的集合。这并不意味着Multimap为相应的键花费内存创建了集合而只是提供一个集合视图方便你为键增加映射值——译者注如果有这样的键返回的集合只是包装了Multimap中已有的集合如果没有这样的键返回的空集合也只是持有Multimap引用的栈对象让你可以用来操作底层的Multimap。因此返回的集合不会占据太多内存数据实际上还是存放在Multimap中。 如果你更喜欢像Map那样为Multimap中没有的键返回null请使用asMap()视图获取一个MapK, CollectionV。或者用静态方法Multimaps.asMap()为ListMultimap返回一个MapK, ListV。对于SetMultimap和SortedSetMultimap也有类似的静态方法存在 当且仅当有值映射到键时Multimap.containsKey(key)才会返回true。尤其需要注意的是如果键k之前映射过一个或多个值但它们都被移除后Multimap.containsKey(key)会返回false。 Multimap.entries()返回Multimap中所有”键-单个值映射”——包括重复键。如果你想要得到所有”键-值集合映射”请使用asMap().entrySet()。 Multimap.size()返回所有”键-单个值映射”的个数而非不同键的个数。要得到不同键的个数请改用Multimap.keySet().size()。
(4)、Multimap的各种实现
实现键行为类似值行为类似ArrayListMultimapHashMapArrayListHashMultimapHashMapHashSetLinkedListMultimapLinkedHashMapLinkedListLinkedHashMultimapLinkedHashMapLinkedHashMapTreeMultimapTreeMapTreeSetImmutableListMultimapImmutableMapImmutableListImmutableSetMultimapImmutableMapImmutableSet
除了两个不可变形式的实现其他所有实现都支持null键和null值 LinkedListMultimap.entries()保留了所有键和值的迭代顺序。详情见doc链接。 LinkedHashMultimap保留了映射项的插入顺序包括键插入的顺序以及键映射的所有值的插入顺序。 请注意并非所有的Multimap都和上面列出的一样使用MapK, CollectionV来实现特别是一些Multimap实现用了自定义的hashTable以最小化开销
3、BiMap
BiMapK, V是特殊的Map
可以用 inverse()反转BiMapK, V的键值映射保证值是唯一的因此 values()返回Set而不是普通的Collection
在BiMap中如果你想把键映射到已经存在的值会抛出IllegalArgumentException异常。
1、常用方法
方法描述V forcePut(String key, V value)对于特定的值强制替换它的键BiMapK,V inverse()k-v键值对的转换即v-kV putK key,V value关联v到kvoid putAll(Map? extend k,? extend V map)将map加入到BiMapSet values()返回BiMap映射中包含的Collection视图
(2)、BiMap的各种实现
键–值实现值–键实现对应的BiMap实现HashMapHashMapHashBiMapImmutableMapImmutableMapImmutableBiMapEnumMapEnumMapEnumBiMapEnumMapHashMapEnumHashBiMap
3、示例 /*** BiMap*/Testpublic void biMapTest(){BiMapString, String biMap HashBiMap.create();biMap.putAll(ImmutableMap.of(a,1,b,2,c,3,d,4,e,5));System.out.println(所有的值Joiner.on(,).join(biMap.values()));System.out.println(转换后所有的值Joiner.on(,).join(biMap.inverse().values()));String v biMap.forcePut(a,10);System.out.println(替换的值v);System.out.println(所有的值Joiner.on(,).join(biMap.values()));}4、Table
Table是Guava提供的一个接口 Interface TableR,C,V由rowKeycolumnKeyvalue组成 它有两个键一个值和一个n行三列的数据表类似n行取决于Table对对象中存储了多少个数据。
1、常用方法
方法描述SetTable.CellR,C,V cellSet()返回集合中的行键列键值三元组void clear()清除所有的键值对MapR,V column(C columnKey)获取列键对应的键值对MapC,V row(R row)获取行键对应的列以及值SetC columnKeySet()获取所有的列键SetR rowKeySet()获取行键MapC,MapR,V columnMap返回列键对应的行键-值的视图boolean contains(Object rowKey,Object columnKey)判断是否包含指定的行键列键boolean containsColumn(Object columnKey)判断是否包含指定的列键boolean containsRow(Object rowKey)判断是否包含指定的行键boolean containsValue(Object value)判断是否包含值V get(Object rowKey,Object columnKey)返回指定的行键列键对应的值不存在则返回nullboolean isEmpty()判断集合是否为空V put(Object rowKey,Object columnKey,Object value)put值void putAll(Table? extend R,? extend C,? extend V table)put指定的tableV remove(Object rowKey,Object columnKey)如果有则移除指定行键列键MapR,MapC,V rowMap()获取每个行键对应的列键值的视图int size()集合的个数(行键/列键/值)CollectionV values()集合值的集合包括重复的
(2)、示例 /*** Table*/Testpublic void tableTest(){TableString,String,Integer table HashBasedTable.create();table.put(grade_1,class_1,100);table.put(grade_1,class_2,95);table.put(grade_1,class_3,80);table.put(grade_2,class_1,88);table.put(grade_2,class_2,95);table.put(grade_2,class_3,99);table.put(grade_2,class_3,100);SetTable.CellString,String,Integer cellSet table.cellSet();cellSet.forEach(cell - {System.out.println(table中的行cell.getRowKey();列cell.getColumnKey();值cell.getValue());});System.out.println(grade1对应的classJoiner.on(;).withKeyValueSeparator().join(table.row(grade_1)));System.out.println(class1对应的gradeJoiner.on(;).withKeyValueSeparator().join(table.column(class_1)));System.out.println(所有的grade:Joiner.on(,).join(table.rowKeySet()));System.out.println(所有的classJoiner.on(,).join(table.columnKeySet()));MapString,MapString,Integer rowMap table.rowMap();rowMap.forEach((row,map) - {System.out.println(row 行对应的列值Joiner.on(;).withKeyValueSeparator().join(map));});MapString,MapString,Integer columnMap table.columnMap();columnMap.forEach((column,map) - {System.out.println(column 列对应的行值Joiner.on(;).withKeyValueSeparator().join(map));});System.out.println(是否包含grade_1 和 class_2:table.contains(grade_1,class_2));table.remove(grade_1,class_2);System.out.println(是否包含grade_1 和 class_2:table.contains(grade_1,class_2));}3、Table有如下几种实现 HashBasedTable本质上用HashMapR, HashMapC, V实现 TreeBasedTable本质上用TreeMapR, TreeMapC,V实现 ImmutableTable本质上用ImmutableMapR, ImmutableMapC, V实现注ImmutableTable对稀疏或密集的数据集都有优化。 ArrayTable要求在构造时就指定行和列的大小本质上由一个二维数组实现以提升访问速度和密集Table的内存利用率。ArrayTable与其他Table的工作原理有点不同。
5、ClassToInstanceMap
ClassToInstanceMap是一种特殊的Map它的键是类型而值是符合键所指类型的对象。 为了扩展Map接口ClassToInstanceMap额外声明了两个方法T getInstance(Class T) 和T putInstance(Class , T)从而避免强制类型转换同时保证了类型安全。
ClassToInstanceMap有唯一的泛型参数通常称为B代表Map支持的所有类型的上界。
对于ClassToInstanceMapGuava提供了两种有用的实现MutableClassToInstanceMap和 ImmutableClassToInstanceMap。
示例 /*** ClassToInstanceMap*/Testpublic void classToInstanceMapTest(){ClassToInstanceMapNumber instanceMap MutableClassToInstanceMap.create();instanceMap.putInstance(Integer.class,123);instanceMap.putInstance(Long.class,456L);instanceMap.putInstance(Double.class,789.09);System.out.println(Integer:instanceMap.getInstance(Integer.class));System.out.println(Long:instanceMap.getInstance(Long.class));System.out.println(Double:instanceMap.getInstance(Double.class));}6、RangSet
RangeSet描述了一组不相连的、非空的区间。当把一个区间添加到可变的RangeSet时所有相连的区间会被合并空区间会被忽略。
结论
通过深入探索Google Guava库的集合操作我们不仅仅发现了一个功能强大的工具更是领略到了一个高效、简洁的Java编程理念。Guava不仅提供了基础数据结构还为开发者提供了一整套处理集合的利器从不可变集合到高效工具类无一不展现出其设计的巧妙之处。
在实际项目中Guava为我们提供了更清晰、更简单的集合操作方式帮助我们避免了许多常见的错误和异常。它的性能优化更是让我们在处理大规模数据时事半功倍。
作为Java开发者我们应该充分了解并灵活运用Guava库以提高代码的可读性、可维护性和性能。无论是新手还是老手Guava都能为我们的开发工作带来便捷和效率。
本文已收录于我的个人博客码农Academy的博客专注分享Java技术干货包括Java基础、Spring Boot、Spring Cloud、Mysql、Redis、Elasticsearch、中间件、架构设计、面试题、程序员攻略等