com.google.guava
guava
19.0
package com.mtons.mblog.modules.guava;
import com.google.common.base.CharMatcher;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.*;
import com.mtons.mblog.BootApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Map;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = BootApplication.class)
/**
* Guava API使用教程
*/
public class GuavaTest {
/**
* Immutable特点
* 1.在多线程操作下,是线程安全的。
*
* 2.所有不可变集合会比可变集合更有效的利用资源。
*
* 3.中途不可改变
* 输出:
* iList: [a, b, c]
* iSet: [e1, e2]
* iMap: {k1=v1, k2=v2}
*/
@Test
public void testGuavaCollection() {
ImmutableList<String> iList = ImmutableList.of("a", "b", "c");
ImmutableSet<String> iSet = ImmutableSet.of("e1", "e2");
ImmutableMap<String, String> iMap = ImmutableMap.of("k1", "v1", "k2", "v2");
System.out.println("iList: " + iList.toString());
System.out.println("iSet: " + iSet.toString());
System.out.println("iMap: " + iMap.toString());
}
/**
*输出:[1, 2]
*/
@Test
public void testGuavaMap() {
Multimap<String,Integer> mapM = ArrayListMultimap.create();
mapM.put("test",1);
mapM.put("test",2);
System.out.println(mapM.get("test"));
}
/**
* Guava 字符串连接器Joiner
* 输出:嗨,jim|jack|kevin
*/
@Test
public void testGuavaJoiner() {
StringBuilder stringBuilder = new StringBuilder("嗨,");
// 字符串连接器,以|为分隔符,同时去掉null元素
Joiner joiner1 = Joiner.on("|").skipNulls();
// 构成一个字符串jim|jack|kevin并添加到stringBuilder
stringBuilder = joiner1.appendTo(stringBuilder, "jim", "jack", null, "kevin");
System.out.println(stringBuilder);
}
/**
* Guava MapToString
* 输出:Cookies:12332#Content-Length:30000#Date:2018.07.04#Mime:text/html
*/
@Test
public void testMapToString() {
Map<String, String> testMap = Maps.newLinkedHashMap();
testMap.put("Cookies", "12332");
testMap.put("Content-Length", "30000");
testMap.put("Date", "2018.07.04");
testMap.put("Mime", "text/html");
// 用:分割键值对,并用#分割每个元素,返回字符串
String returnedString = Joiner.on("#").withKeyValueSeparator(":").join(testMap);
System.out.println(returnedString);
}
/**
* Guava MapToString
* 输出:
* Cookies -> 12332
* Content-Length -> 30000
* Date -> 2018.07.04
* Mime -> text/html
*/
@Test
public void testStringToMap() {
// 接上一个,内部类的引用,得到分割器,将字符串解析为map
Splitter.MapSplitter ms = Splitter.on("#").withKeyValueSeparator(':');
Map<String, String> ret = ms.split("Cookies:12332#Content-Length:30000#Date:2018.07.04#Mime:text/html");
for (String it2 : ret.keySet()) {
System.out.println(it2 + " -> " + ret.get(it2));
}
}
/**
* Guava 字符串工具类Strings
* 输出:
* true
* true
* false
* helloTTT
*/
@Test
public void testStrings() {
System.out.println(Strings.isNullOrEmpty("")); // true
System.out.println(Strings.isNullOrEmpty(null)); // true
System.out.println(Strings.isNullOrEmpty("hello")); // false
// 将null转化为""
System.out.println(Strings.nullToEmpty(null)); // ""
// 从尾部不断补充T只到总共8个字符,如果源字符串已经达到或操作,则原样返回。类似的有padStart
System.out.println(Strings.padEnd("hello", 8, 'T')); // helloTTT
}
/**
* Guava 字符匹配器CharMatcher
*/
@Test
public void testCharMatcher() {
// 空白回车换行对应换成一个#,一对一换
String stringWithLinebreaks = "hello world\r\r\ryou are here\n\ntake it\t\t\teasy";
String s6 = CharMatcher.BREAKING_WHITESPACE.replaceFrom(stringWithLinebreaks,'#');
//hello#world###you#are#here##take#it###easy
System.out.println(s6);
// 将所有连在一起的空白回车换行字符换成一个#,倒塌
String tabString = " hello \n\t\tworld you\r\nare here ";
String tabRet = CharMatcher.WHITESPACE.collapseFrom(tabString, '#');
//#hello#world#you#are#here#
System.out.println(tabRet);
// 在前面的基础上去掉字符串的前后空白,并将空白换成一个#
String trimRet = CharMatcher.WHITESPACE.trimAndCollapseFrom(tabString, '#');
//hello#world#you#are#here
System.out.println(trimRet);
String letterAndNumber = "1234abcdABCD56789";
// 保留数字
String number = CharMatcher.JAVA_DIGIT.retainFrom(letterAndNumber);
//123456789
System.out.println(number);
}
}
package com.mtons.mblog.modules.guava;
import com.google.common.cache.*;
import com.mtons.mblog.BootApplication;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = BootApplication.class)
/**
* ImmutableMap getAllPresent(Iterable> keys) 一次获得多个键的缓存值
* putAll方法向缓存中添加一个或者多个缓存项
* invalidateAll方法从缓存中移除缓存项
* ConcurrentMap快照
* refresh(Key) 刷新缓存,即重新取缓存数据,更新缓存
*/
public class GuavaCacheTest {
public static void main(String[] args) throws ExecutionException, InterruptedException{
//缓存接口这里是LoadingCache,LoadingCache在缓存项不存在时可以自动加载缓存
LoadingCache<Integer,Student> studentCache
//CacheBuilder的构造函数是私有的,只能通过其静态方法newBuilder()来获得CacheBuilder的实例
= CacheBuilder.newBuilder()
//设置并发级别为8,并发级别是指可以同时写缓存的线程数
.concurrencyLevel(8)
//设置写缓存后8秒钟过期
.expireAfterWrite(8, TimeUnit.SECONDS)
//设置缓存容器的初始容量为10
.initialCapacity(10)
//设置缓存最大容量为100,超过100之后就会按照LRU最近虽少使用算法来移除缓存项
.maximumSize(100)
//设置要统计缓存的命中率
.recordStats()
//设置缓存的移除通知
.removalListener(new RemovalListener<Object, Object>() {
@Override
public void onRemoval(RemovalNotification<Object, Object> notification) {
System.out.println(notification.getKey() + " was removed, cause is " + notification.getCause());
}
})
//build方法中可以指定CacheLoader,在缓存不存在时通过CacheLoader的实现自动加载缓存
.build(
new CacheLoader<Integer, Student>() {
@Override
public Student load(Integer key) throws Exception {
//这里可以设置数据库查询,放入缓存中
System.out.println("load student " + key);
Student student = new Student();
student.setId(key);
student.setName("name " + key);
return student;
}
}
);
for (int i=0;i<20;i++) {
//从缓存中得到数据,由于我们没有设置过缓存,所以需要通过CacheLoader加载缓存数据
//缓存没有数据会调用load(Integer key),并把查询到的数据放入缓存中
Student student = studentCache.get(1);
System.out.println(student);
//java.util.concurrent.TimeUnit 休眠1秒
TimeUnit.SECONDS.sleep(1);
}
System.out.println("cache stats:");
//最后打印缓存的命中率等 情况
System.out.println(studentCache.stats().toString());
}
}
输出:
load student 1
Student{id=1, name='name 1'}
Student{id=1, name='name 1'}
Student{id=1, name='name 1'}
Student{id=1, name='name 1'}
Student{id=1, name='name 1'}
Student{id=1, name='name 1'}
Student{id=1, name='name 1'}
Student{id=1, name='name 1'}
1 was removed, cause is EXPIRED
load student 1
Student{id=1, name='name 1'}
Student{id=1, name='name 1'}
Student{id=1, name='name 1'}
Student{id=1, name='name 1'}
Student{id=1, name='name 1'}
Student{id=1, name='name 1'}
Student{id=1, name='name 1'}
Student{id=1, name='name 1'}
1 was removed, cause is EXPIRED
load student 1
Student{id=1, name='name 1'}
Student{id=1, name='name 1'}
Student{id=1, name='name 1'}
Student{id=1, name='name 1'}
cache stats:
CacheStats{hitCount=17, missCount=3, loadSuccessCount=3, loadExceptionCount=0, totalLoadTime=3308375, evictionCount=2}
package com.mtons.mblog.modules.guava;
import java.nio.charset.Charset;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnel;
import com.google.common.hash.PrimitiveSink;
/**
* 布隆过滤器
*
* 布隆过滤器可以插入元素,但不可以删除已有元素。其中的元素越多,
* false positive rate(误报率)越大,但是false negative (漏报)是不可能的。
* 为了add一个元素,用k个hash function将它hash得到bloom filter中k个bit位,将这k个bit位置1。
*
* 为了query一个元素,即判断它是否在集合中,用k个hash function将它hash得到k个bit位。若这k bits全为1,
* 则此元素在集合中;若其中任一位不为1,则此元素比不在集合中(因为如果在,则在add时已经把对应的k个bits位置为1)。
*
* 不允许remove元素,因为那样的话会把相应的k个bits位置为0,而其中很有可能有其他元素对应的位。因此
* remove会引入false negative,这是绝对不被允许的。
*/
public class BloomTest {
// 预计元素个数
private long size = 1024 * 1024;
private BloomFilter<String> bloom = BloomFilter.create(new Funnel<String>() {
@Override
public void funnel(String from, PrimitiveSink into) {
// 自定义过滤条件 此处不做任何过滤
into.putString((CharSequence) from, Charset.forName("UTF-8"));
}
}, size);
public void fun() {
// 往过滤器中添加元素
bloom.put("布隆过滤器");
// 查询
boolean mightContain = bloom.mightContain("布隆过滤器");
System.out.println(mightContain);
}
public static void main(String[] args) {
BloomTest blBoolmTest = new BloomTest();
blBoolmTest.fun();
}
}
package com.mtons.mblog.modules.guava;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import com.google.common.base.Charsets;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
public class BloomFilterTest {
private static final int insertions = 1000000;// 100万
public static void main(String[] args) {
// 初始化一个存储string数据的布隆过滤器,默认fpp(误差率) 0.03
BloomFilter<String> bf = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), insertions);
Set<String> set = new HashSet<String>(insertions);
List<String> list = new ArrayList<String>(insertions);
for (int i = 0; i < insertions; i++) {
String uuid = UUID.randomUUID().toString();
bf.put(uuid);
set.add(uuid);
list.add(uuid);
}
int wrong = 0; // 布隆过滤器误判的次数
int right = 0;// 布隆过滤器正确次数
for (int i = 0; i < 10000; i++) {
String str = i % 100 == 0 ? list.get(i / 100) : UUID.randomUUID().toString();
if (bf.mightContain(str)) {
if (set.contains(str)) {
right++;
} else {
wrong++;
}
}
}
//right 为100
System.out.println("right:" + right);
//因为误差率为3%,所以一万条数据wrong的值在300左右
System.out.println("wrong:" + wrong);
}
}
package com.mtons.mblog.modules.guava;
/**
* @author Administrator
*/
public class Student {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}