Guava开发总结
一. 字符串相关
1.CharMatcher的使用
①inRange() 结合matches()
// 判断match中的元素是否在inRange()的区间中 中文不支持
boolean matches1 = CharMatcher.inRange('a', 'z').or(CharMatcher.inRange('A', 'Z')).matches('K');
boolean matches2 = CharMatcher.inRange('1', '9').or(CharMatcher.inRange('一', '九')).matches('二');
// true false
System.out.println(matches1 + " " + matches2);
②inRange()结合retainFrom()、removeFrom()
// 只保留字符串中的数字,digit()被废弃,用inRange('0', '9')代替
String s = CharMatcher.inRange('0', '9').retainFrom("sC,DFc!x456$7sfac8@0wxwAAsq2#ew%q");
// 4567802
System.out.println(s);
// 删除字符串中的字母,javaLetter()被废弃,用inRange('a', 'z').or(inRange('A', 'Z'))代替
String s1 =
CharMatcher.inRange('a', 'z')
.or(CharMatcher.inRange('A', 'Z'))
.removeFrom("sC,DFc!x456$7sfac8@0wxwAAsq2#ew%q");
// ,!456$78@02#%
System.out.println(s1);
// 只取字符串中的大写字母
String s2 = CharMatcher.inRange('A', 'Z').retainFrom("sC, D Fc!x45 6$7sfac8@0wxwA Asq2#ew%q");
// CDFAA
System.out.println(s2);
// 只保留字母和数字
String s3 =
CharMatcher.inRange('0', '9')
.or(CharMatcher.inRange('a', 'z').or(CharMatcher.inRange('A', 'Z')))
.retainFrom("a A1 a,5.b b.6.cc,, =.d3d+");
// aA1a5bb6ccd3d
System.out.println(s3);
2.Strings工具类
①非空判断
// true | false | true
System.out.println(Strings.isNullOrEmpty("")+ " | "+ Strings.isNullOrEmpty(" ")+ " | " + Strings.isNullOrEmpty(null));
②空转
// | Guava
System.out.println(Strings.nullToEmpty(null) + " | " + Strings.nullToEmpty(" Guava "));
// | Guava
System.out.println(Strings.emptyToNull(" ") + " | " + Strings.emptyToNull(" Guava "));
③前后同缀
// 取两字符串相同前缀----》aa
System.out.println(Strings.commonPrefix("aabbac", "aac"));
// 取两字符串相同后缀----》 ac
System.out.println(Strings.commonSuffix("aabbac", "aac"));
④前后加缀
// 在字符串开头添加char字符,若设置长度<=原字符串长度,则不添加----》EEaabbac
System.out.println(Strings.padStart("aabbac", 8, 'E'));
// 在字符串结尾添加char字符,若设置长度<=原字符串长度,则不添加----》aabbac
System.out.println(Strings.padEnd("aabbac", 4, 'D'));
3.Splitter的使用
/**
* 关于字符串的操作都是基于Splitter的 on() 以...分割 介绍下on后面的扩展操作
*
* <p>omitEmptyStrings 用来省略空白
*
* <p>limit 用来限制结果个数,也就是前几个分隔符会生效
*
* <p>trimResults 去除结果头尾空格
*
* <p>withKeyValueSeparator 将String转换Map<String,String>
*
* <p>omitEmptyStrings().trimResults() 去除空格与空串
*
* <p>onPattern() 支持正则表达式的分割方法
*/
// 将String转化成特定的集合 "1-2-3-4-5-6" --> { 1, 2, 3, 4, 5, 6 }
String s = "1-2-3-4-5-6";
List<String> list = Splitter.on("-").splitToList(s);
// [1, 2, 3, 4, 5, 6]
System.out.println(list);
// omitEmptyStrings().trimResults() 去除空格与空串
String s1 = "1-2 -3-4- 5 - 6";
List<String> list1 = Splitter.on("-").omitEmptyStrings().trimResults().splitToList(s1);
// [1, 2, 3, 4, 5, 6]
System.out.println(list1);
String s2 = "aa = 1, bb = 2";
Map<String, String> map = Splitter.on(", ").withKeyValueSeparator(" = ").split(s2);
// {aa=1, bb=2}
System.out.println(map);
// String的正则分割 onPattern()方法,以,.=+分割,参数: [,|.|=|+]
String s3 = "a A1 a,5.b b.6.cc,, =.d3d+";
List<String> list2 = Splitter.onPattern("[,|.|=|+|]").omitEmptyStrings().trimResults().splitToList(s3);
// [a A1 a, 5, b b, 6, cc, d3d]
System.out.println(list2);
Splitter.on(",").limit(2).splitToList("123,456,789,,23").forEach(System.out::println);
// 123
// 456,789,,23
4.Joiner使用
/**
* 关于集合的操作都是基于Joiner的 on() 设置元素/Entry间连接符号 withKeyValueSeparator() 设置key,value间的连接符号
*
* <p>Joiner用来处理我们常出现的字符串拼接操作 除了useForNull之外,Joiner的扩展操作还有 skipNulls 跳过null值 withKeyValueSeparator
* 用来处理对Map的输出
*
* @return void
* @date 2020/7/30 13:58
*/
public static void joiner() {
List<String> words = Lists.newArrayList("123", "456", "789", null);
// 不使用guava
StringBuilder sb = new StringBuilder();
for (String word : words) {
if (word == null) {
sb.append("default");
} else {
sb.append(word);
}
sb.append(",");
}
if (sb.length() > 1) {
sb.deleteCharAt(sb.length() - 1);
}
// 123,456,789,default
System.out.println(sb.toString());
// 使用guava
// 123,456,789,default
System.out.println(Joiner.on(",").useForNull("default").join(words));
// 123,456,789
System.out.println(Joiner.on(",").skipNulls().join(words));
Map<String, String> data = ImmutableMap.of("a", "1", "b", "2");
// a-1,b-2
System.out.println(Joiner.on(",").withKeyValueSeparator("-").join(data));
Map<String, Integer> data2 = ImmutableMap.of("a", 1, "b", 2);
// a-1,b-2
System.out.println(Joiner.on(",").withKeyValueSeparator("-").join(data2));
}
二.Ordering排序器
import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
import com.google.common.collect.Ordering;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Guava中的排序,MoreObjects, TimeUnit
*
* @author Akang
* @date 2018/2/28
*/
public class TestOther {
/**
* Ordering排序器
*
* <p>natural() 对可排序类型做自然排序,如数字按大小,日期按先后排序
*
* <p>usingToString() 按对象的字符串形式做字典排序
*
* <p>[lexicographical ordering] from(Comparator) 把给定的Comparator转化为排序器
*
* <p>reverse() 获取语义相反的排序器
*
* <p>nullsFirst() 使用当前排序器,但额外把null值排到最前面。
*
* <p>nullsLast() 使用当前排序器,但额外把null值排到最后面。
*
* <p>compound(Comparator) 合成另一个比较器,以处理当前排序器中的相等情况。
*
* <p>lexicographical() 基于处理类型T的排序器,返回该类型的可迭代对象Iterable<T>的排序器。
*
* <p>onResultOf(Function) 对集合中元素调用Function,再按返回值用当前排序器排序。
*/
private static void testOrdering() {
Person p1 = new Person("aa", "13");
Person p2 = new Person("bb", "11");
// 提供根据年龄升序的排序器, 1, 2, 3, ... 99, 100
Ordering<Person> personOrdering =
Ordering.natural()
.nullsFirst()
.onResultOf((Function<Person, String>) input -> input.getAge());
int compare = personOrdering.compare(p1, p2);
System.out.println(compare);
// 2 (p1年龄比p2年龄大2岁)
/** 创建姓名降序的比较器(z, y, x ...c, b, a) */
Ordering<Person> reverse =
new Ordering<Person>() {
@Override
public int compare(Person left, Person right) {
// 字符串字母自然排序(a, b, c, ... x, y, z)
return Ordering.natural().compare(left.getName(), right.getName());
}
}.reverse();
int compare1 = reverse.nullsFirst().compare(p1, p2);
System.out.println(compare1);
// 1 p1姓名在p2后面1位
}
/** MoreObjects:优雅实现toString */
private static void testMoreObjects() {
Person person = new Person("aa", "11");
String s = MoreObjects.toStringHelper("Person").add("age", person.getAge()).toString();
System.out.println(s);
// Person{age=11}
String s1 =
MoreObjects.toStringHelper("AA")
.add("name", person.getName())
.add("age", person.getAge())
.toString();
System.out.println(s1);
// AA{name=aa, age=11}
}
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public static class Person {
private String name;
private String age;
}
}
三.io操作
1. 文件内容复制及文件移动,比较
/** 文件内容复制及文件移动,比较, 路径指向文件必须存在 */
private static void testCopyAndMove(@CheckForNull String fromPath, @CheckForNull String toPath) throws IOException {
File from = new File(fromPath);
File to = new File(toPath);
if (from.isFile() && to.isFile()) {
// 比较 true
System.out.println(Files.equal(from, to));
// 会覆盖to路径的文件内容
Files.copy(from, to);
System.out.println("Copy Succeed, The File is " + Files.readLines(to, Charset.defaultCharset()));
// 会删除from路径的文件
Files.move(from, to);
// 报错,from路径文件已删除
System.out.println("Move Succeed, The File is " + Files.readLines(from, Charset.defaultCharset()));
}
}
2.写入/追加/读取文件
private static void testWriteAndRead(@CheckForNull String path, @CheckForNull String content) throws IOException {
File file = new File(path);
Files.write(content.getBytes(), file);
// 写入,会覆盖文件原内容
List<String> list = Files.readLines(file, Charset.defaultCharset());
// 读取并返回
System.out.println(list);
// append()废弃,asCharSink(to, charset, FileWriteMode.APPEND).write(from)代替,把from以charset的编码格式追加到文件to的最后
Files.asCharSink(file, Charset.defaultCharset(), FileWriteMode.APPEND).write("追加");
// 在文件最后追加(不换行),若换行,write("\n追加")
System.out.println(Files.readLines(file, Charset.defaultCharset()));
}
3.大文件处理
/**
* 大文件特殊处理,大文件不能直接加载进内存,否则会内存溢出
* 可利用readLines()的重载方法,但已废弃,需要实现LineProcessor的泛型接口,getResult()获得处理结果, processLine()对文本进行处理
*/
private static void testBigFile(@CheckForNull String path) throws IOException {
final File file = new File(path);
/**
* 大文件处理需要实现LineProcessor的泛型接口,getResult()获得处理结果,processLine()对文本进行处理 例子:
* 取出文件中所有数字并追加至源文件,然后读取文件内容
*/
String readLines = Files.asCharSource(file, Charset.defaultCharset())
.readLines(
new LineProcessor<String>() {
StringBuffer s = new StringBuffer();
@Override
public boolean processLine(String line) { // line 文件中的每一行文本
// 只保留line中的数字
s.append(CharMatcher.inRange('0', '9').retainFrom(line));
return true;
}
// 返回文件内容,toString(file, charset)返回文件中所有文本,已废弃,asCharSource(file,charset).read()代替
@Override
public String getResult() {
try {
// 换行追加s至源文件最后
Files.asCharSink(file,Charset.defaultCharset(),FileWriteMode.APPEND).write("\n" + s);
return Files.asCharSource(file, Charset.defaultCharset()).read();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
});
System.out.println(readLines);
}
4.文件名称、类型
/** 其他方法 */
private static void testOther(@CheckForNull String path) throws IOException {
// 获取文件扩展名 txt
System.out.println(Files.getFileExtension(path));
// 获取不带扩展名的文件名 test
System.out.println(Files.getNameWithoutExtension(path));
// 读取文件第一行,readFirstLine()废弃,asCharSource(file, charset).readFirstLine()代替
System.out.println(Files.asCharSource(new File(path), Charset.defaultCharset()).readFirstLine());
}
四.集合操作
1.普通集合创建
/** 普通Collection的创建 */
private static void commonCollectionsOption() {
List<String> list = Lists.newArrayList();
Set<String> set = Sets.newHashSet();
Map<String, String> map = Maps.newHashMap();
}
2.不可变集合创建
/**
* 不可变Collection的创建
*
* <p>不可变Collection: 类似于显示声明的数组(int[] a = new int[] { 1, 2, 3 };),长度不能改变
*
* <p>1.多线程下线程安全
*
* <p>2.所有不可变集合会比可变集合更有效利用资源
*
* <p>3.中途不可改变,所有对集合做改变的方法都已废弃且抛异常
*
* <p>注: subList()返回列表中指定的 fromIndex(包括)和 toIndex(不包括)之间的一个新的不可变Collections,不会对原不可变集合做改变,
* JDK中List下的subList(int fromIndex, int toIndex)返回列表中指定的 fromIndex(包括)和
* toIndex(不包括)之间的部分视图,截取子列表,最终反映到原始列表上。
*/
private static void immutableCollectionsOption() {
ImmutableList<String> immutableList = ImmutableList.of("a", "b", "c");
ImmutableList<String> immutableList1 = immutableList.subList(0, 1);
ImmutableSet<String> immutableSet = ImmutableSet.of("a", "b");
ImmutableMap<String, String> immutableMap = ImmutableMap.of("k1", "v1", "k2", "v2");
}
3.Multiset统计集合元素个数
/** 统计集合相同元素个数 */
public static void multiSet() {
List<String> words = Lists.newArrayList("a", "b", "c", "b", "b", "c");
Map<String, Integer> counts = new HashMap<>(16);
for (String word : words) {
Integer count = counts.get(word);
if (count == null) {
counts.put(word, 1);
} else {
counts.put(word, count + 1);
}
}
// {a=1, b=3, c=2}
Multiset<String> multiset1 = HashMultiset.create();
for (String word : words) {
multiset1.add(word);
}
// [a, b x 3, c x 2]
System.out.println(multiset1);
Multiset<String> multiset2 = HashMultiset.create(words);
multiset2.add("d", 4);
// a count:1
// b count:3
// c count:2
// d count:4
multiset2.elementSet().forEach(e -> System.out.println(e + " count:" + multiset2.count(e)));
}
4.SortedMultiset集合排序
/**
* 不过这个SortedMultiset是针对元素进行排序的,而不是元素次数,所以使用这个集合类的时候,
* 最好保存数字类型的元素。并且它的subMultiset是针对这个排序规则来的,比如我上面是倒序的, 使用subMultiset是3到2,而不是2到3。
*
* @return void
* @date 2020/7/30 14:38
*/
public static void sortedMultiset() {
SortedMultiset<Integer> sortedMultiset = TreeMultiset.create();
sortedMultiset.add(2, 3);
sortedMultiset.add(3, 5);
sortedMultiset.add(4, 4);
// [2 x 3, 3 x 5, 4 x 4]
System.out.println(sortedMultiset);
// 倒序
sortedMultiset = sortedMultiset.descendingMultiset();
// [4 x 4, 3 x 5, 2 x 3]
System.out.println(sortedMultiset);
// 4
System.out.println(sortedMultiset.firstEntry().getElement());
sortedMultiset = sortedMultiset.subMultiset(3, BoundType.OPEN, 2, BoundType.CLOSED);
// [2 x 3]
System.out.println(sortedMultiset);
}
5.Multimap、ListMultimap重复元素
/**
* 可存放相等元素的Set集合Multiset和Map集合multimap
*
* <p>MultiMap可以理解为对Map<K, List<V>>或Map<K, Set<V>> 的抽象,我们在开发中也肯定经常有统计一个key下有哪些value之类场景。
*
* <p>首先我们可以看到MultiMap的初始化采用建造者模式,key和value 的实现是定制化的,可以根据自己具体需求选择对应实现。选择treeKeys就代表key是有序的。
* 其次通过get方法拿到的value List是浅拷贝。 SetMultimap是另外一种MultiMap的实现,不同之处么,Set去重。
*
* @return void
* @date 2020/7/30 14:51
*/
public static void multiMap() {
ListMultimap<String, Integer> listMultimap = MultimapBuilder.treeKeys().arrayListValues().build();
listMultimap.put("1", 1);
listMultimap.put("1", 2);
listMultimap.put("2", 1);
// {1=[1, 2], 2=[1]}
System.out.println(listMultimap);
List<Integer> value = listMultimap.get("1");
value.add(3);
// {1=[1, 2, 3], 2=[1]}
System.out.println(listMultimap);
// 4
System.out.println(listMultimap.size());
listMultimap.removeAll("2");
listMultimap.remove("1", 1);
// {1=[2, 3]}
System.out.println(listMultimap);
Map<String, Collection<Integer>> mapView = listMultimap.asMap();
// 1
System.out.println(mapView.size());
Multimap<String, String> multiMap = HashMultimap.create();
multiMap.put("chen", "11");
multiMap.put("chen", "22");
// key:chen value:11
// key:chen value:22
multiMap.forEach((k, v) -> System.out.println("key:" + k + " value:" + v));
}
6.BiMap双向集合
/** 双向Map,key,value都唯一,inverse()--返回键值互换的双向Map,可以通过get(value)获取key */
private static void biMap() {
BiMap<String, String> biMap = HashBiMap.create();
biMap.put("a", "1");
// biMap.put("a", "3"); 报错,key重复
biMap.put("b", "2");
// biMap.put("c", "2"); 报错,value重复
System.out.println(biMap.get("a"));
// 1
System.out.println(biMap.inverse().get("2"));
// b
// key,value必须都是Enum类型的双向Map, inverse() key,value反转
BiMap<WeekInEnglish, WeekInChinese> week = EnumBiMap.create(WeekInEnglish.class, WeekInChinese.class);
WeekInChinese[] weekInChineses = WeekInChinese.values();
WeekInEnglish[] weekInEnglishs = WeekInEnglish.values();
for (int i = 0; i < weekInChineses.length; i++) {
week.put(weekInEnglishs[i], weekInChineses[i]);
}
// 周日
System.out.println(week.get(WeekInEnglish.SUNDAY));
// MONDAY
System.out.println(week.inverse().get(WeekInChinese.周一));
}
public enum WeekInChinese {
周一,
周二,
周三,
周四,
周五,
周六,
周日
}
public enum WeekInEnglish {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
}
7.map、set元素比较【集合的复制copy】
有集合的copy方法
/**
* 找出2个map或set相同之处与相同之处
*
* @return void
* @since 2020/10/10 10:53
*/
public static void difference() {
System.out.println("------找出2个Map的不相同的元素和相同的元素,以map形式返回------------");
ImmutableMap<String, Integer> map1 = ImmutableMap.of("key1", 1, "key2", 2);
ImmutableMap<String, Integer> map2 = ImmutableMap.of("key11", 11, "key2", 2);
MapDifference<String, Integer> diffMap = Maps.difference(map1, map2);
// 若无共同Entry,则返回长度为0的Map
Map<String, Integer> commonMap = diffMap.entriesInCommon();
// {key2=2}
System.out.println(commonMap);
// 返回左边的Map中不同或者特有的元素
Map<String, Integer> diffOnLeft = diffMap.entriesOnlyOnLeft();
// {key1=1}
System.out.println(diffOnLeft);
// 返回右边的Map中不同或者特有的元素
Map<String, Integer> diffOnRight = diffMap.entriesOnlyOnRight();
// 共同Map中key:key11 value:11
diffOnRight.forEach((k, v) -> System.out.println("右边Map独有的key:" + k + " value:" + v));
System.out.println("------找出2个Set的不相同的元素和相同的元素,以Set形式返回------------");
Set<String> set1 = Sets.newHashSet("chen", "lei", "java");
Set<String> set2 = Sets.newHashSet("chen", "lei", "hadoop");
// 是得到左边中不同或者特有的元素,若无,则返回长度为0的集合
Sets.SetView<String> diffSet = Sets.difference(set1, set2);
// 返回一个不可变的左边Set中特有元素集合的Set拷贝
Set<String> diffImmutable = diffSet.immutableCopy();
// [java]
System.out.println(diffImmutable);
Iterator iter = diffSet.iterator();
// Set的不同元素:java
while (iter.hasNext()) {
System.out.println("Set的不同元素:" + iter.next().toString());
}
Sets.SetView<String> commonSet = Sets.intersection(set1, set2);
// 返回一个不可变的2个Set中共同元素集合的Set拷贝
Set<String> commonImmutable = commonSet.immutableCopy();
// [chen, lei]
System.out.println(commonImmutable);
}
8.table
/**
* 双键Map,类似于数据库的联合主键,通过两个key获取一个value,类似于表格结构,通过行和列取值
*
* @return void
* @since 2020/10/10 11:19
*/
public static void table() {
Table<String, String, String> table = HashBasedTable.create();
table.put("male", "programmer", "scj");
table.put("female", "beauty", "ss");
table.put("female", "programmer", "s2");
// scj
System.out.println(table.get("male", "programmer"));
// scj
System.out.println(table.row("male").get("programmer"));
// s2
System.out.println(table.column("programmer").get("female"));
}
9.排序Ordering、反转reverse、分割partition
/**
* 排序、反转、分割
*
* @return void
* @date 2020/7/30 15:06
*/
@SuppressWarnings("unchecked")
public static void collectionNatural() {
List<String> list = Lists.newArrayList("2", "3", "5", "4");
System.out.println(Ordering.natural().sortedCopy(list));
// [2, 3, 4, 5]
System.out.println(Ordering.natural().reverse().sortedCopy(list));
// [5, 4, 3, 2]
System.out.println(Ordering.natural().min(list));
// 2
System.out.println(Ordering.natural().max(list));
// 5
List test2 = Lists.newArrayList(30, 20, 60, 80, null, 10);
System.out.println(Ordering.natural().nullsLast().sortedCopy(test2));
// [10, 20, 30, 60, 80, null]
System.out.println(Ordering.natural().nullsFirst().sortedCopy(test2));
// [null, 10, 20, 30, 60, 80]
// 以3个长度分割
List<List<String>> partition = Lists.partition(list, 3);
System.out.println(partition);
// [[2, 3, 5], [4]]
}
10.List、Map元素过滤
/** 按照条件过滤集合中元素 */
public static void filterCollections() {
ImmutableList<String> list = ImmutableList.of("aa", "BB", "cc", "DD");
// 选取集合中的aa或AA元素并输出,or() --> 或, and() --> 且
Iterable<String> filter = Iterables.filter(list, or(equalTo("aa"), equalTo("AA")));
System.out.println(filter);
// [aa]
// 自定义过滤条件,将集合中value != 1的所有数据的value置为0
ImmutableMap<String, String> map = ImmutableMap.of("a", "1", "b", "2");
// Function<F, T> F表示apply()参数input的类型,T表示apply()的返回类型
Map<String, String> map1 =
Maps.transformValues(
map,
input -> {
if (Objects.equals("1", input)) {
return input;
}
return "0";
});
System.out.println(map1);
// {a=1, b=0}
// 获取集合中键为"a"或"c"的Entry元素并返回
Map<String, String> map2 = Maps.filterKeys(map, or(equalTo("a"), equalTo("c")));
System.out.println(map2);
// {a=1}
// 获取集合中值为"1"或"2"的Entry元素并返回
Map<String, String> map3 = Maps.filterValues(map, or(equalTo("1"), equalTo("2")));
System.out.println(map3);
// {a=1, b=2}
}
11.Guava Map、Set交差并,JDK List交差并
/** Guava中计算Map的交集,差集,并集 */
private static void countMap() {
ImmutableMap<String, String> map1 = ImmutableMap.of("k1", "v1", "k2", "v2");
ImmutableMap<String, String> map2 = ImmutableMap.of("k1", "v1", "k3", "v3");
MapDifference<String, String> difference = Maps.difference(map1, map2);
// not equal: only on left={k2=v2}: only on right={k3=v3}
System.out.println(difference);
// 交集,若无共同Entry,返回长度为0的Map
Map<String, String> entriesInCommon = difference.entriesInCommon();
// {k1=v1}
System.out.println(entriesInCommon);
// 以map1为基础求差集
Map<String, String> entriesOnlyOnLeft = difference.entriesOnlyOnLeft();
// {k2=v2}
System.out.println(entriesOnlyOnLeft);
// 以map2为基础求差集
Map<String, String> entriesOnlyOnRight = difference.entriesOnlyOnRight();
// {k3=v3}
System.out.println(entriesOnlyOnRight);
// 两个Map是否相同
boolean areEqual = difference.areEqual();
// false
System.out.println(areEqual);
Multimap<String, Integer> multimap1 = ArrayListMultimap.create();
multimap1.put("a", 1);
multimap1.put("a", 2);
multimap1.put("b", 3);
multimap1.put("b", 4);
Multimap<String, Integer> multimap2 = ArrayListMultimap.create();
multimap2.put("a", 1);
multimap2.put("c", 5);
// 并集
multimap1.putAll(multimap2);
// {a=[1, 2, 1], b=[3, 4], c=[5]}
System.out.println(multimap1);
}
/** Guava中计算Set的交集,差集,并集 */
private static void countSet() {
Set<String> set1 = Sets.newHashSet("a", "b", "c", "d");
Set<String> set2 = Sets.newHashSet("c", "d", "e", "f");
// 并集
Sets.SetView<String> union = Sets.union(set1, set2);
// [a, b, c, d, e, f]
System.out.println(union);
// 差集(以set1为基础)
Sets.SetView<String> difference = Sets.difference(set1, set2);
// [a, b]
System.out.println(difference);
// 交集(以set1为基础)
Sets.SetView<String> intersection = Sets.intersection(set1, set2);
// [c, d]
System.out.println(intersection);
}
/** JDK中计算List的交集,差集,并集 */
private static void countList() {
List<String> list1 = Lists.newArrayList("a", "b", "c", "a", "b");
List<String> list2 = Lists.newArrayList("d", "e", "c", "a", "c");
// 获得list1和list2的并集
list1.addAll(list2);
// [a, b, c, a, b, d, e, c, a, c]
System.out.println(list1);
// 获得list1和list2的交集(以list1为基础)
list1.retainAll(list2);
// [a, c, a, d, e, c, a, c]
System.out.println(list1);
System.out.println("-----------------");
// 获得list1和list2的交集(以list2为基础)
list2.retainAll(list1);
// [d, e, c, a, c]
System.out.println(list2);
// 获得list1和list2的差集(属于list1,不属于list2)
list1.removeAll(list2);
// []
System.out.println(list1);
// 求list1 和list2的差集(属于list2,不属于list1)
list2.removeAll(list1);
// 求list1 和list2的并集
list1.addAll(list2);
// [d, e, c, a, c]
System.out.println(list1);
}
五.Guava缓存
1.CacheBuilder构建一个缓存实例
/**
* 同样的,使用建造者模式进行初始化。针对初始化,我总结了以下几点。
* 设置缓存容量 设置缓存过期策略 设置缓存生成策略 缓存大小和过期策略都是为了解决就是应用内存有限以及缓存有效性的问题。
* 对于缓存大小有Size和Weight两种模式。
* Size针对缓存的个数来设置上限。
* Weight可以通过Weigher函数针对不同的缓存来返回不同Weight,所有缓存累加值不能超过maximumWeight。
* 当缓存容量超过限制值后,我们就需要根据缓存过期策略淘汰一些缓存。
* expireAfterAccess会在缓存read或write后指定时间后失效。
* expireAfterWrite会在缓存write后指定时间后失效。
* 缓存生成策略通过CacheLoader来封装我们缓存的生成逻辑。
* 我们可以预先初始化缓存,当get的时候,如果key不在缓存中,就会通过CacheLoader来生成我们的缓存。
* 设置并发级别为10,Guava提供了设置并发级别的api,使得缓存支持并发的写入和读取。同ConcurrentHashMap类似Guavacache的并发也是通过分离锁实现。在一般情况下,将并发级别设置为服务器cpu核心数是一个比较不错的选择
*
* .maximumSize()设置最大值
* .weakValues()弱引用
* .removalListener(listener)移除监听,即后面的缓存都见听不到了
* cache.invalidateAll(list);批量清除,list缓存的键
* @author lhv
* @date 2021/01/11 15:54
* @return void
*/
public static void simpleCache() {
// 通过CacheBuilder构建一个缓存实例
Cache<String, String> cache =
CacheBuilder.newBuilder()
// 设置缓存的最大容量
.maximumSize(100)
// 设置缓存在写入一分钟后失效
.expireAfterWrite(1, TimeUnit.MINUTES)
// 设置并发级别为10
.concurrencyLevel(10)
// 开启缓存统计
.recordStats()
.build();
// 放入缓存
cache.put("key", "value");
// 获取缓存---》value
System.out.println(cache.getIfPresent("key"));
}
2.CacheLoader创建缓存
/**
* Guava缓存: CacheLoader
*
* @author lhv
* @date 2021/01/11 15:55
* @return void
*/
public static void cacheLoader() throws ExecutionException {
// 通过CacheBuilder构建一个缓存实例,LoadingCache继承自Cache,在构建LoadingCache时,
// 需要通过CacheBuilder的build(CacheLoader<? super K1, V1> loader)方法构建
LoadingCache<String, String> loadingCache =
CacheBuilder.newBuilder()
.build(
new CacheLoader<String, String>() {
@Override
public String load(String key) {
return "hello" + key;
}
});
System.out.println(loadingCache.get("111"));
// hello111
loadingCache.put("a", "A");
System.out.println(loadingCache.get("a"));
// A
// 获取缓存,当缓存不存在时,会通过CacheLoader自动加载,该方法会抛出ExecutionException异常
loadingCache.get("k1");
// 以不安全的方式获取缓存,当缓存不存在时,会通过CacheLoader自动加载,该方法不会抛出异常
loadingCache.getUnchecked("k1");
}