Guava工具类学习
一、介绍
1、常用点:集合 [collections] 、缓存 [caching] 、原生类型支持 [primitives support] 、并发库 [concurrency libraries] 、通用注解 [common annotations] 、字符串处理 [string processing] 、I/O 等等
2、开源的Java库,这个库是为了方便编码,并减少编码错误。
二、Optional类
1、定义
官方解释:用于包含非空对象的不可变对象,Optional对象不存在值表示null。
个人解释:换言之就是把变量转成Optional对象,其中null都转成Optional.absent()(就是一个空的Optional对象),然后就可以对Optional对象进行操作。好处就是发现null可选择抛出异常。
2、java8自带Optional
所以建议直接用java8自带的就好了,用法一样,只是方法名字稍微不一样
3、使用
关键方法(解释看代码):fromNullable、of、get、or、isPresent
@Test
public void OptionalTest() {
handleParam(null,1); //后面两个参数自己随意写
}
//处理参数举例子
void handleParam(String a,Integer b){
//对于处理string参数
Optional<String> aOpt = Optional.fromNullable(a); //允许参数空。如果非空返回一个包含引用Optional实例,否则返回Optional.absent()。
System.out.println(aOpt.isPresent());//输出aOpt是否为空
a=aOpt.or("defaultValue");//如果为空,就给一个默认值defaultValue
System.out.println(a);
//同理对于处理int参数
Optional<Integer> bOpt = Optional.of(b);//不允许参数空,不然会抛出异常
b=bOpt.get();//get的时候不允许变量为空
System.out.println(b);
}
三、Preconditions类
1、定义
官方解释:检查方法的参数是否符合业务
个人感觉:我不喜欢这个,我觉得没什么用,用到的几率太小,要一直捕获exception
2、使用
关键方法(解释看代码):checkArgument、checkState;checkPositionIndex、checkElementIndex;checkNotNull
@Test
public void PreconditionsTest() {
try {
System.out.println(sqrt(-3.0)); //计算开方
}catch(Exception e){
System.out.println(e.getMessage());
}
try {
System.out.println(sum(null,3)); //计算和
}catch(NullPointerException e){
System.out.println(e.getMessage());
}
try {
System.out.println(getValue(-1,6));//判断下标是否有效
}catch(IndexOutOfBoundsException e){
System.out.println(e.getMessage());
}
}
public double sqrt(double input) {
Preconditions.checkArgument(input > 0.0, "非法参数: 参数为%s", input);//和checkState差不了多少
return Math.sqrt(input);
}
public int sum(Integer a, Integer b){
Preconditions.checkNotNull(a, "非法参数: 参数为空");
Preconditions.checkNotNull(b, "非法参数: 参数为空");
return a+b;
}
public int getValue(int index, int size){
Preconditions.checkPositionIndex(index, size, "无效的下标");//和checkElementIndex差不了多少
return index;
}
四、Ordering类
1、定义
官方解释:Ordering(排序)可以被看作是一个丰富的比较具有增强功能的链接,多个实用方法,多类型排序功能等
个人解释:反正就是列表的排序,倒序,取最大最小,取从最大到最小的k个最大的元素,取从小到最大的k个最小的元素,亮点是可以兼容null
2、使用
关键方法(解释看代码):greatestOf、leastOf;min、max;sortedCopy、isOrdered、reverse;nullsFirst、nullsLast
@Test
public void OrderingTest() {
List<Integer> myList = new ArrayList<Integer>();
myList.add(new Integer(5));
myList.add(new Integer(2));
myList.add(new Integer(15));
myList.add(new Integer(51));
myList.add(new Integer(53));
myList.add(new Integer(35));
myList.add(new Integer(45));
myList.add(new Integer(32));
myList.add(new Integer(43));
myList.add(new Integer(16));
Ordering ordering = Ordering.natural();
//从最大到最小的k个最大的元素
List list1 = ordering.greatestOf(myList, 3);
System.out.println("从最大到最小的k个最大的元素list1:"+list1);//输出[53, 51, 45]
//从小到最大的k个最小的元素
List list2 = ordering.leastOf(myList, 3);
System.out.println("从小到最大的k个最小的元素list2:"+list2);//输出[2, 5, 15]
//最小元素
int min = (int) ordering.min(myList);
System.out.println("最小元素:"+min);//输出2
//最大元素
int max = (int) ordering.max(myList);
System.out.println("最大元素:"+max);//输出53
//排序后复制,原list不变,从小到大排序
List list3 = ordering.sortedCopy(myList);
System.out.println("从小到大排序后复制给list3:"+list3);//输出[2, 5, 15, 16, 32, 35, 43, 45, 51, 53]
System.out.println("原mylist:"+myList);//输出[5, 2, 15, 51, 53, 35, 45, 32, 43, 16]
//排序也可以直接用Collections.sort,从小到大排序,正常情况下此方法禁止list含null
Collections.sort(myList);
System.out.println("从小到大排序好原mylist:"+myList);//输出[2, 5, 15, 16, 32, 35, 43, 45, 51, 53]
//查看是否已经从小到大好了序,该方法禁止list含null,如果是从大到小则返回false
boolean isOrdered = ordering.isOrdered(myList);
System.out.println("查看是否已经排好了序:"+isOrdered);//输出true
//换成从大到小排序或者是翻转
Collections.sort(myList,ordering.reverse());
System.out.println("从大到小排序好原mylist:"+myList);//输出53, 51, 45, 43, 35, 32, 16, 15, 5, 2]
//ordering允许list含有null
myList.add(null);
//对待null小于所有其他值
Collections.sort(myList,ordering.nullsFirst());
System.out.println("添加null以后,对待null小于所有其他值,myList为:"+myList);//输出[null, 2, 5, 15, 16, 32, 35, 43, 45, 51, 53]
//对待null大于所有其他值
Collections.sort(myList,ordering.nullsLast());
System.out.println("添加null以后,对待null大于所有其他值,myList为:"+myList);//输出[2, 5, 15, 16, 32, 35, 43, 45, 51, 53, null]
}
五、Range类
1、定义
官方解释:Range 表示一个间隔或一个序列。它被用于获取一组数字/串在一个特定范围之内。Range定义了连续跨度的范围边界,这个连续跨度是一个可以比较的类型(Comparable type)。比如1到100之间的整型数据。
个人解释:可以生产区间,就是一段连续的整型数据,可以判断是否存在或者获取两个区间的交集和并集,可以判断是否存在或者获取一个区间的最低端点和上限端点,可以查询一个区间是包含一个或者多个元素。
2、生产区间的方法
概念 | 表示范围 | guava对应功能方法 |
---|---|---|
(a..b) | ` {x | a < x < b} ` |
[a..b] | ` {x | a <= x <= b} ` |
[a..b) | ` {x | a <= x < b} ` |
(a..b] | ` {x | a < x <= b}` |
(a..+∞) | ` {x | x > a}` |
[a..+∞) | ` {x | x >= a}` |
(-∞..b) | ` {x | x < b} |
(-∞..b] | ` {x | x <= b}` |
(-∞..+∞) | all values |
all() |
3、使用
关键方法(解释看代码):lowerEndpoint,upperEndpoint,hasUpperBound,hasLowerBound;encloses,isConnected,intersection
@Test
public void RangeTest(){
System.out.println("-------------------基本获取---------------------");
//创建一个范围 [a,b]
Range<Integer> range1 = Range.closed(0, 9);
System.out.println("生成range1 [0,9]: "+ range1.toString()); //输出[0‥9]
//查看Range是否存在某个或某些值
System.out.println("range1是否存在5: " + range1.contains(5));//输出true
System.out.println("range1是否存在(1,2,3): " + range1.containsAll(Ints.asList(1, 2, 3)));//输出true
//查看Range的最低端点和上限端点。
System.out.println("range1的最低端点: " + range1.lowerEndpoint());//输出0
System.out.println("range1是否有上端点: " + range1.hasUpperBound());//输出true
System.out.println("range1的上限端点: " + range1.upperEndpoint());//输出9
System.out.println("------------------闭区间和开区间----------------------");
//创建一个范围 (a,b)
Range<Integer> range2 = Range.open(0, 9);
System.out.println("生成range2 (0,9) :"+range2.toString());
//创建一个范围 (a,b]
Range<Integer> range3 = Range.openClosed(0, 9);
System.out.println("生成range3 (0,9] : "+range3.toString());
//创建一个范围 [a,b)
Range<Integer> range4 = Range.closedOpen(0, 9);
System.out.println("生成range4 [0,9) : "+range4.toString());
//返回一个包含所有值严格大于端点的范围内 (a,∞)
Range<Integer> range5 = Range.greaterThan(9);
System.out.println("生成range5 (9,∞) : "+range5.toString());
System.out.println("range5是否有上端点: " + range5.hasUpperBound());
System.out.println("------------------交集和并集----------------------");
Range<Integer> range6 = Range.closed(3, 5);
//是否包含子集
System.out.println("range1 [0,9] 是否包含 range6 [3,5]:" + range1.encloses(range6));
Range<Integer> range7 = Range.closed(9, 20);
//是否有交集
System.out.println("range1 [0,9] 和 range7 [9,20]是否有交集:" + range1.isConnected(range7));
Range<Integer> range8 = Range.closed(5, 15);
//返回由两者范围和connectedRange封闭,如果这样的范围存在的最大范围。如果不相连时,直接抛出异常
System.out.println("range1 [0,9]和 range8 [5,15]的交集:"+range1.intersection(range8).toString());
//返回最小的范围包围两者这个范围和other等。
System.out.println("range1 [0,9]和 range8 [5,15]的并集:"+range1.span(range8).toString());
System.out.println("-------------------拓展---------------------");
//输出有限range
ContiguousSet<Integer> set = ContiguousSet.create(range8, DiscreteDomain.integers());
for(int grade : set) {
System.out.print(grade +" ");
}
}
六、Multiset接口
1、定义
Multiset接口扩展设置有重复的元素,并提供了各种实用的方法来处理这样的元素在集合中出现。
2、使用
关键方法(解释看代码):elementSet、iterator;count、contains、containsAll、size;add、remove
@Test
public void MultisetTest(){
//创建一个multiset
Multiset<String> multiset = HashMultiset.create();
multiset.add("a");multiset.add("b");multiset.add("c");
multiset.add("d");multiset.add("a");multiset.add("b");
multiset.add("c");multiset.add("b");multiset.add("b");
System.out.print("把multiset转成无重复set:");
Set<String> set = multiset.elementSet();
set.forEach(e-> System.out.print(e+" "));//输出a b c d
System.out.print("\n把multiset转成可重复且有序的Iterator:");
Iterator<String> iterator = multiset.iterator();
iterator.forEachRemaining(e-> System.out.print(e+" "));//输出a a b b b b c c d
System.out.println("\n含有几个'b' : "+multiset.count("b"));//输出4
System.out.println("是否包含e' : "+multiset.contains("e"));//输出false
System.out.println("总size : "+multiset.size());//输出9
//移除i个某元素
System.out.println("移除1一个元素a");
multiset.remove("a",1);
//增加i个某元素
System.out.println("增加2个元素c");
multiset.add("c",2);
//显示集合的不同元素及其出现次数
System.out.print("遍历打印出multiset的元素和个数:");
multiset.entrySet().forEach(entry->System.out.print("元素"+entry.getElement() +"的个数为 " + entry.getCount()+";"));//元素a的个数为 1;元素b的个数为 4;元素c的个数为 4;元素d的个数为 1;
}
七、Bimap接口
1、定义
BiMap确保没有重复的值,且可以从value获取key
2、使用
关键方法(解释看代码):put、forcePut;inverse
@Test
public void BimapTest(){
BiMap<Integer, String> empIDNameMap = HashBiMap.create();
empIDNameMap.put(101, "Mahesh");
empIDNameMap.put(102, "Sohan");
empIDNameMap.put(103, "Ramesh");
//亮点:如果key已经存在,强制覆盖
empIDNameMap.forcePut(101, "Mahesh1");
//亮点:根据值找到key 。inverse():生成value到key的逆视图
System.out.println(empIDNameMap.inverse().get("Mahesh"));//输出null
System.out.println(empIDNameMap.inverse().get("Mahesh1"));//输出101
//把所有value转成set集合
Set<String> values = empIDNameMap.values();
values.forEach(e-> System.out.print(e+" "));//输出Ramesh Mahesh1 Sohan
}
八、Bimap接口
1、定义
Table代表一个特殊的映射,其中两个键可以在组合的方式被指定为单个值。它类似于创建映射的映射。
简单点说就是有rowKey,有columnKey,匹配对应value
2、使用
关键方法(解释看代码):put、row、rowKeySet、column
@Test
public void TableTest(){
//创建一个表
Table<String, String, String> employeeTable = HashBasedTable.create();
employeeTable.put("IBM", "101","Mahesh");
employeeTable.put("IBM", "102","Ramesh");
employeeTable.put("IBM", "103","Suresh");
employeeTable.put("Microsoft", "101","Sohan");
employeeTable.put("Microsoft", "102","Mohan");
employeeTable.put("Microsoft", "103","Rohan");
employeeTable.put("TCS", "121","Ram");
employeeTable.put("TCS", "122","Shyam");
employeeTable.put("TCS", "123","Sunil");
//获取某一行的所有列
Map<String,String> ibmEmployees = employeeTable.row("IBM");
System.out.println("rowKey为IBM的这一行的所有列为");
ibmEmployees.entrySet().forEach(entry ->System.out.println("列字段columnKey: " + entry.getKey() + ", 值value: " + entry.getValue()));//输出列字段columnKey: 103, 值value: Suresh \n 列字段columnKey: 101, 值value: Mahesh \n 列字段columnKey: 102, 值value: Ramesh
//获取所有行的rowkey,自动按照字母顺序
Set<String> employers = employeeTable.rowKeySet();
System.out.print("所有的rowKey为: ");
employers.forEach(employer-> System.out.print(employer + "、"));//输出IBM、TCS、Microsoft
//获取某一列的所有行和值
System.out.println("\ncolumnKey为102的所有行和值为");
Map<String,String> EmployerMap = employeeTable.column("102");
EmployerMap.entrySet().forEach( entry->System.out.println("行字段rowKey: " + entry.getKey() + ", 值value: " + entry.getValue()));//输出行字段rowKey: IBM, 值value: Ramesh \n 行字段rowKey: Microsoft, 值value: Mohan
}
九、LoadingCache
1、定义
在缓存中自动加载值,它提供了许多实用的方法,在有缓存需求时非常有用。
优点:
- 线程安全的缓存,与ConcurrentMap相似,但前者增加了更多的元素失效策略,后者只能显示的移除元素。
- 提供了三种基本的缓存回收方式:基于容量回收、定时回收和基于引用回收。定时回收有两种:按照写入时间,最早写入的最先回收;按照访问时间,最早访问的最早回收。
- 监控缓存加载/命中情况
- 集成了多部操作,调用get方式,可以在未命中缓存的时候,从其他地方获取数据源(DB,redis),并加载到缓存中。
缺点:
- Guava Cache的超时机制不是精确的。
- 不能持久化本地缓存
2、使用
关键方法(驾驶看代码):getIfPresent、get、put、putAll、invalidate、
invalidateAll、refresh、size、stats、asMap、
public class GuavaTester {
public static void main(String args[]) {
//定义一个LoadingCache
LoadingCache employeeCache = CacheBuilder.newBuilder()
.concurrencyLevel(8)//设置并发级别为8,并发级别是指可以同时写缓存的线程数
.maximumSize(100) // 设置缓存最大容量为100,超过100之后就会按照LRU最近虽少使用算法来移除缓存项
.expireAfterWrite(10, TimeUnit.MINUTES)//设置写缓存后10分钟后过期
.expireAfterAccess(30, TimeUnit.MINUTES) // 设置缓存在访问30分钟后过期
.initialCapacity(10) //设置缓存容器的初始容量为10
.recordStats() //设置要统计缓存的命中率,开启统计信息开关,
.removalListener(notification -> System.out.println("key为"+notification.getKey() + "的缓存被移除了, 因为" + notification.getCause()))//设置缓存的移除通知
.build(new CacheLoader() { //build方法中可以指定CacheLoader,,用于从数据源加载数据,在缓存不存在时可自定义策略,比如从数据库获取数据
@Override
public Object load(Object key) throws Exception {
//从数据库拿数据
return getFromDatabase(key.toString()); //自定义
}
});
try {
//第一次调用时,缓存没有命中,会从数据库拿数据,如果数据库没有就直接抛异常
System.out.println("第一次调用:");
System.out.println(employeeCache.get("100"));//输出Employee(name=Mahesh, dept=Finance, emplD=100)
System.out.println(employeeCache.get("103"));//输出Employee(name=Rohan, dept=IT, emplD=103)
System.out.println(employeeCache.get("110"));//输出EmployeeEmployee(name=Sohan, dept=Admin, emplD=110)
//第二次调用时,直接从缓存中拿
System.out.println("第二次调用:");
System.out.println(employeeCache.get("100"));//输出Employee(name=Mahesh, dept=Finance, emplD=100)
System.out.println(employeeCache.get("103"));//输出Employee(name=Rohan, dept=IT, emplD=103)
System.out.println(employeeCache.get("110"));//输出EmployeeEmployee(name=Sohan, dept=Admin, emplD=110)
System.out.println("---------------------分割线-----------------------");
//常用方法
System.out.println("用getIfPresent获取key为104的值:"+employeeCache.getIfPresent("104"));//获取缓存中key对应的value,如果缓存没命中,返回null
//如果缓存有值,覆盖,否则,新增
employeeCache.put("104",new Employee("Aria","Boss","104"));
//删除缓存
employeeCache.invalidate("100");
//获取缓存中元素的大概个数
System.out.println("获取缓存中元素的大概个数为:"+employeeCache.size());
//缓存的状态数据,包括(未)命中个数,加载成功/失败个数,总共加载时间,删除个数等。
System.out.println("缓存的状态数据为:"+employeeCache.stats());//输出CacheStats{hitCount=3, missCount=4, loadSuccessCount=3, loadExceptionCount=0, totalLoadTime=966956, evictionCount=0}
//asMap()方法获得缓存数据的ConcurrentMap快照
System.out.println("把LoadingCache转换成concurrentMap:"+ employeeCache.asMap());//输出{103=Employee(name=Rohan, dept=IT, emplD=103), 110=Employee(name=Sohan, dept=Admin, emplD=110)}
//刷新缓存,即重新取缓存数据,更新缓存
employeeCache.refresh("110");
//清除缓存
employeeCache.invalidateAll();
//清空所有数据
employeeCache.cleanUp();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
//模拟从数据库拿数据
private static Object getFromDatabase(String empId) {
//局部变量,取一次,清除一次
Employee e1 = new Employee("Mahesh", "Finance", "100");
Employee e2 = new Employee("Rohan", "IT", "103");
Employee e3 = new Employee("Sohan", "Admin", "110");
Map database = new HashMap();
database.put("100", e1);
database.put("103", e2);
database.put("110", e3);
System.out.println("从数据库找empId为" + empId);
return database.get(empId);
}
}
@Data
@AllArgsConstructor
class Employee {
private String name;
private String dept;
private String emplD;
}
十、Multimap
1、定义
可以含有多个一样的key
2、使用
关键方法:containsEntry、containsKey、containsValue;get、asMap、keySet、values;add、remove
public static void main(String args[]){
//初始化一个Multimap
Multimap<String,String> multimap = ArrayListMultimap.create();
multimap.put("lower", "a");
multimap.put("lower", "b");
multimap.put("lower", "c");
multimap.put("lower", "d");
multimap.put("lower", "e");
multimap.put("upper", "A");
multimap.put("upper", "B");
multimap.put("upper", "C");
multimap.put("upper", "D");
System.out.println("是否包含键值对lower-a:"+multimap.containsEntry("lower", "a"));//输出 true
System.out.println("是否包含键lower:"+multimap.containsKey("lower"));//输出 true
System.out.println("是否包含值a:"+multimap.containsValue("a"));//输出 true
System.out.println("multimap包含键值对数量:"+multimap.size());//输出9
System.out.println("---------------分割线---------------");
List<String> lowerList = (List<String>)multimap.get("lower");
System.out.println("所有key为lower的值有:"+lowerList.toString());//输出[a, b, c, d, e]
List<String> upperList = (List<String>)multimap.get("upper");
System.out.println("所有key为lower的值有:"+upperList.toString());//输出[A, B, C, D]
lowerList.add("f");
upperList.remove("D");
System.out.println("---------------分割线---------------");
Map<String, Collection<String>> map = multimap.asMap();
System.out.println("遍历整个multimap:");
map.entrySet().forEach(entry->{
String key = entry.getKey();
Collection<String> value = multimap.get(key);
System.out.println(key + ":" + value); //输出lower:[a, b, c, d, e, f] \n upper:[A, B, C]
});
System.out.println("---------------分割线---------------");
System.out.println("所有唯一key:");
Set<String> keys = multimap.keySet();
keys.forEach(key-> System.out.print(key+" "));//输出lower upper
System.out.println("\n所有的值:");
Collection<String> values = multimap.values();
System.out.println(values);//输出[a, b, c, d, e, f, A, B, C]
}