Guava是一种基于开源的Java库,谷歌很多项目使用它的很多核心库。这个库是为了方便编码,并减少编码错误。本篇文章主要以测试实例来讲解Guava比较常用的一些类和功能
Guava提供的Preconditions类中有一些基础前置检查方法,当检查不通过时迅速抛出错误。
int i = 1;
String a = "";
int j[] = {4, 1, 2, 3};
List l = Ints.asList(j);
//前置检查方法
//条件检查 不通过抛出IllegalArgumentException及自定义描述 自定义描述支持类似于String.format()的字符串拼接但是只能用%s
checkArgument(i >= 0, "Argument was %s but expected nonnegative", i);
//空值检查 不通过抛出NullPointerException及自定义描述
checkNotNull(a, "Argument was null");
//状态检查 不通过抛出IllegalStateException及自定义描述
checkState(i >= 0, "Argument was %s but expected nonnegative", i);
//检查列表、字符串或数组某一索引是否有效 不通过抛出IndexOutOfBoundsException
checkElementIndex(2, l.size());
//检查列表、字符串或数组某一位置是否有效 不通过抛出IndexOutOfBoundsException
checkPositionIndex(2, l.size());
//检查列表、字符串或数组某一范围是否有效 不通过抛出IndexOutOfBoundsException
checkPositionIndexes(1, 2, l.size());
Objects方法提供了比较null的方法
//Objects支持null的对比方法
Objects.equal("a", "a"); // returns true
Objects.equal(null, "a"); // returns false
Objects.equal("a", null); // returns false
Objects.equal(null, null); // returns true
排序器及对比器
//对可排序类型做自然排序,如数字按大小,日期按先后排序
List sortl = Ordering.natural().sortedCopy(l);
//按对象的字符串形式做字典排序
Ordering.usingToString();
//自定义排序器 - 比较字符串长度
Ordering byLengthOrdering = new Ordering() {
public int compare(String left, String right) {
return Ints.compare(left.length(), right.length());
}
};
//排序器能够支持链式调用
Ordering.natural()
//获取语义相反的排序器
.reverse()
//使用当前排序器,但额外把null值排到最前面
.nullsFirst()
//使用当前排序器,但额外把null值排到最后面
.nullsLast()
//合成另一个比较器,以处理当前排序器中的相等情况。
.compound(new Comparator() {
@Override
public int compare(Integer i, Integer j) {
return 0;
}
})
//基于处理类型T的排序器,返回该类型的可迭代对象Iterable的排序器
.lexicographical();
//对集合中元素调用Function,再按返回值用当前排序器排序
class Foo {
@Nullable
String sortedBy;
int notSortedBy;
}
Ordering ordering = Ordering.natural().nullsFirst().onResultOf(new Function() {
public String apply(Foo foo) {
return foo.sortedBy;
}
});
//以下是具体排序方法
//获取可迭代对象中最大的k个元素。
Ordering.natural().greatestOf(l, 3);
//判断可迭代对象是否已按排序器排序:允许有排序值相等的元素。
Ordering.natural().isOrdered(l);
//进行排序 并把排序结果copy成另一个集合
List sortedl = Ordering.natural().sortedCopy(l);
//返回两个参数中最小的那个。如果相等,则返回第一个参数。
Ordering.natural().min(1, 2);
//返回多个参数中最小的那个。如果有超过一个参数都最小,则返回第一个最小的参数。
Ordering.natural().min(1, 2, 3, 4);
//返回迭代器中最小的元素。如果可迭代对象中没有元素,则抛出NoSuchElementException。
Ordering.natural().min(l);
//同理max
Ordering.natural().max(1, 2);
不可变集合:静态集合不可修改
//以set为例
ImmutableSet foob = ImmutableSet.of("a", "b", "c");
ImmutableSet GOOGLE_COLORS = ImmutableSet.builder().add(new Color(0, 190, 255)).build();
ImmutableSet defensiveCopy = ImmutableSet.copyOf(l);
//同理类似的类就是集合前面加上Immutable 包括Collection
Multiset接口与MultiMap的系列集合:提供一个可以统计元素插入重复次数的无序集合
//Multiset
Multiset countMap = HashMultiset.create();
//插入字符串"aa"2次
countMap.add("aa", 3);
//删除n次
countMap.remove("aa", 1);
//获取不重复的元素只获取到一个aa
countMap.elementSet();
//统计aa插入的次数
countMap.count("aa");
//和Map的entrySet类似 返回set类型的entry集 支持getElement()和getCount()方法
countMap.entrySet();
//给定元素固定次数
countMap.setCount("aa", 5);
//返回集合元素的总个数(重复的也记录 若想去重使用countMap.elementSet().size())
countMap.size();
//Multimap系列接口集合 和Multiset很相似 可以理解成Multiset的map版本 对应的实现将上面的对应map改为Multimap
//Multimap主要是为了一个键映射到多个值。换句话说,Multimap是把键映射到任意多个值的一般方式。
//可以和传统的缓存Map>进行对比
Multimap multimap = HashMultimap.create();
//转换成Map>格式 且对转换后的map做操作会影响原有的Multimap
multimap.asMap();
//添加键到单个值的映射
multimap.put(1, "a");
//添加多个值的映射
multimap.putAll(2, Lists.newArrayList("a", "b", "c", "d"));
//移除键到值的映射;如果有这样的键值并成功移除,返回true。
multimap.remove(2, "b");
//移除一个key所有的映射值
multimap.removeAll(1);
//替换原有的映射值集合
multimap.replaceValues(2, Lists.newArrayList("a", "b", "c", "d"));
BiMap系列接口集合:为了解决实现键值对的双向映射需要维护两个单独的map的问题
//其对应的实现HashBiMap,ImmutableBiMap,EnumBiMap,EnumHashBiMap
BiMap userId = HashBiMap.create();
userId.put(1, "tom");
//在想使用反向映射时使用inverse反转键值对关系
userId.inverse().get("tom");
table :行 列 值
Table weightedGraph = HashBasedTable.create();
weightedGraph.put(1, 2, 4);
weightedGraph.put(1, 3, 20);
weightedGraph.put(2, 3, 5);
//返回某一行的列对应值的map
weightedGraph.row(1); // returns a Map mapping 2 to 4, 3 to 20
//返回某一列的行对应值的map
weightedGraph.column(3); // returns a Map mapping 1 to 20, 2 to 5
ClassToInstanceMap系列接口:是一种特殊的Map:它的键是类型,而值是符合键所指类型的对象
//Guava提供了两种有用的实现:MutableClassToInstanceMap和 ImmutableClassToInstanceMap。
ClassToInstanceMap numberDefaults = MutableClassToInstanceMap.create();
numberDefaults.putInstance(Integer.class, Integer.valueOf(0));
RangeSet与RangeMap类:用来表述区间
//当把一个区间添加到可变的RangeSet时,所有相连的区间会被合并,空区间会被忽略。
RangeSet 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)}
//RangeMap类除了上述区间形容外 描述了”不相交的、非空的区间”到特定值的映射
//和RangeSet不同,RangeMap不会合并相邻的映射,即便相邻的区间映射到相同的值。
//Range区间也是Guava提供的一种新类型
RangeMap rangeMap = TreeRangeMap.create();
rangeMap.put(Range.closed(1, 10), "foo"); //{[1,10] => "foo"}
rangeMap.put(Range.open(3, 6), "bar"); //{[1,3] => "foo", (3,6) => "bar", [6,10] => "foo"}
rangeMap.put(Range.open(10, 20), "foo"); //{[1,3] => "foo", (3,6) => "bar", [6,10] => "foo", (10,20) => "foo"}
rangeMap.remove(Range.closed(5, 11)); //{[1,3] => "foo", (3,5) => "bar", (11,20) => "foo"}
集合类工具:对应集合类后面加上s
//Lists
List intList = Ints.asList(4, 5, 6);
List theseElements = Lists.newArrayList("alpha", "beta", "gamma");
//反转List
List thoseElements = Lists.reverse(theseElements);
//指定大小分割
List> conList = Lists.partition(thoseElements, 3);
//Sets
Set wordsWithPrimeLength = ImmutableSet.of("one", "two", "three", "six", "seven", "eight");
Set primes = ImmutableSet.of("two", "three", "five", "seven");
//联合两个set
Sets.union(wordsWithPrimeLength, primes);
//求交集
Sets.intersection(wordsWithPrimeLength, primes);
//Maps
Map left = ImmutableMap.of("a", 1, "b", 2, "c", 3);
Map right = ImmutableMap.of("b", 2, "c", 4, "d", 4);
//Maps.difference(Map, Map)用来比较两个Map以获取所有不同点。该方法返回MapDifference对象,把不同点的维恩图分解
MapDifference diff = Maps.difference(left, right);
//找到键值对都相等的
diff.entriesInCommon(); // {"b" => 2}
//找到键相等值不想等的
diff.entriesDiffering(); // {"c" => 3},{"c" => 4}
//找到只存在于左边的
diff.entriesOnlyOnLeft(); // {"a" => 1}
//找到只存在于右边的
diff.entriesOnlyOnRight(); // {"d" => 5}
//Iterables工具类连接两个Iterable(集合)
Iterable concatenated = Iterables.concat(
Ints.asList(1, 2, 3),
Ints.asList(4, 5, 6)); // concatenated包括元素 1, 2, 3, 4, 5, 6
//Tables
Table table = HashBasedTable.create();
//把Table转置成Table 也就是行列互换
Tables.transpose(table);
Guava提供了一套缓存类,如果希望系统牺牲内存空间去执行的更快速,可以使用它
//cache缓存
// LoadingCache是Cache的缓存实现
LoadingCache cache = CacheBuilder.newBuilder()
//设置缓存大小
.maximumSize(1000)
//设置到期时间
.expireAfterWrite(10, TimeUnit.MINUTES)
//设置缓存里的值两分钟刷新一次
.refreshAfterWrite(2,TimeUnit.MINUTES)
//开启缓存的统计功能
.recordStats()
//构建缓存
.build(new CacheLoader() {
//此处实现如果根据key找不到value需要去如何获取
@Override
public Object load(String s) throws Exception {
return new Foo();
}
//如果批量加载有比反复调用load更优的方法则重写这个方法
@Override
public Map loadAll(Iterable extends String> keys) throws Exception {
return super.loadAll(keys);
}
});
cache.put("aa", new Foo());
cache.get("aa");
//除了在build的时候设置没有key的调用方法外我们还能在调用的时候手动写
String key = "bb";
cache.get(key, new Callable
简化流操作
//guava提供了一个源与汇的概念对应读写字节字符共有4个类ByteSource,CharSource,ByteSink,CharSink
URL url=new URL("this the resource url");
File file=new File("your file path");
Resources.toByteArray(url);//从url中获得字节数组
Files.toByteArray(file);//从文件中获得字节数组
CharSource charSource = Files.asCharSource(file, Charsets.UTF_8);//获得字符源
ByteSource byteSource=Files.asByteSource(file);//获得字节源
Resources.asCharSource(url, Charsets.UTF_8);//从url中获得字符源
Resources.asByteSource(url);//从url中获得字节源
CharSink charSink=Files.asCharSink(file, Charsets.UTF_8, FileWriteMode.APPEND);//获得字符汇 以append追加方式(覆盖方式可以省略)
ByteSink byteSink = Files.asByteSink(file,FileWriteMode.APPEND);//或者字节汇 以append追加方式(覆盖方式可以省略)
byteSource.asCharSource(Charsets.UTF_8);//字节源转字符源
byteSink.asCharSink(Charsets.UTF_8);//字节汇转字符汇
byte[] bytes=byteSource.read();//读取出byte[]
String string=charSource.read();//读取出String
byteSource.copyTo(byteSink);//字节源copy到汇 可以使用OutputStream
charSource.copyTo(charSink);//字符源copy到汇 可以使用Appendable
byteSink.write(bytes);//写入字节
charSink.write(string);//写入字符(使用的是CharSequence,可以用String)
//案例
//逐行读取 以utf-8编码
ImmutableList lines = Files.asCharSource(file, Charsets.UTF_8).readLines();
//读取单词
Multiset wordOccurrences = HashMultiset.create(
//以空格拆分
Splitter.on(CharMatcher.WHITESPACE)
.trimResults()
.omitEmptyStrings()
.split(Files.asCharSource(file, Charsets.UTF_8).read()));
//获取文件按照sha1生成的hash码
HashCode hash = Files.asByteSource(file).hash(Hashing.sha1());
//File的工具类Files
//将url资源copy到file中
Resources.asByteSource(url).copyTo(Files.asByteSink(file));
并发多线程编程
ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));
ListenableFuture explosion = service.submit(new Callable() {
public String call() {
return "this guava callable";
}
});
Futures.addCallback(explosion, new FutureCallback() {
//调用成功后执行的方法
@Override
public void onSuccess(Object o) {
}
////调用失败后执行的方法
public void onFailure(Throwable thrown) {
}
});
字符串处理
//字符串拼接-连接器Joiner
List names = Lists.newArrayList("John", null, "Jane", "Adam", "Tom");
Joiner.on(",")
//跳过null
.skipNulls()//.useForNull("nameless")将null换为nameless
.join(names); //John,Jane,Adam,Tom
Map salary = Maps.newHashMap();
salary.put("John", 1000);
salary.put("Jane", 1500);
String result = Joiner.on(" , ")
//keyvalue中的拼接
.withKeyValueSeparator(" = ")
.join(salary);//John=1000,Jane=1500
//字符串拆分--拆分器Splitter
Splitter.on(',')
//去除前后空格
.trimResults()
//去除空值
.omitEmptyStrings()
//限制拆分字符的长度
.limit(3)
.split("foo,bar,, qux");
//Splitter.on中支持CharMacher按字符匹配器 还能放入Pattern按正则 或者使用Splitter.onPattern(“\r?\n”)这样正则拆分
Guava提供了更加优质的散列算法
//Person类
class Person {
int id;
String firstName;
String lastName;
int birthYear;
public Person(int id, String firstName, String lastName, int birthYear) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.birthYear = birthYear;
}
}
//hash过滤器
Funnel personFunnel = new Funnel() {
@Override
public void funnel(Person person, PrimitiveSink into) {
into
.putInt(person.id)
.putString(person.firstName, Charsets.UTF_8)
.putString(person.lastName, Charsets.UTF_8)
.putInt(person.birthYear);
}
};
//使用md5算法计算hashcode
HashFunction hf = Hashing.md5();//已经提供了md5(),sha1(),sha256(),sha512(),murmur3_128(), murmur3_32(),goodFastHash(int bits)
HashCode hc = hf.newHasher()
//额外增加hash运算,可以用来放密码
.putLong(123456789)
.putString("数字", Charsets.UTF_8)
.putObject(new Person(1,"张","三",2000), personFunnel)
.hash();
//BloomFilter布鲁姆过滤器
BloomFilter friends = BloomFilter.create(personFunnel, 500, 0.01);
Person dude=new Person(1,"aa","bb",1000);
//假设多人
List friendsList=Lists.newArrayList(dude);
for(Person friend : friendsList) {
friends.put(friend);
}
// 很久以后
if (friends.mightContain(dude)) {
//dude不是朋友还运行到这里的概率为1%
//在这儿,我们可以在做进一步精确检查的同时触发一些异步加载
}
函数式风格
//在guava中我们经常见到参数为function的方法 这里的方法为你提供了一种自定义转换函数的使用
//拿Lists.transform举例子 可以新建一个函数进行自定义的元素转换 实际上实现并不复杂使用了一种命令模式
List intTypeList=Lists.newArrayList();
List stringTypeList = Lists.transform(intTypeList, new Function() {
@Override
public String apply(Integer integer) {
return integer.toString()+integer.toString();
}
});
//当然可以使用lambda表达式
List stringTypeList2 = Lists.transform(intTypeList,(Integer integer)->integer.toString()+integer.toString());
Guava事件总线:内部的订阅通知模型,无需使用事件+事件listener模型 只有一个事件类
// 给予事件类中方法以@Subscribe注解
class EventBusChangeRecorder {
@Subscribe
public void recordCustomerChange(ChangeEvent e) {
//事件发生时做一些事
System.out.println("触发事件");
}
}
//在程序的某处创建事件总线并注册事件
EventBus eventBus=new EventBus();
eventBus.register(new EventBusChangeRecorder());
// 在之后的程序中 提交发生的事件
ChangeEvent event=new ChangeEvent(new EventBusChangeRecorder());
eventBus.post(event);
//需要异步执行可以使用EventBus的子类AsyncEventBus
数学运算
IntMath.checkedAdd(1,2);//加
IntMath.checkedSubtract(1,2);//减
IntMath.checkedMultiply(1,2);//乘
IntMath.divide(2,1, RoundingMode.DOWN);//除 需要指定舍入格式
IntMath.checkedPow(1,2);//次方
IntMath.gcd(3,2);//最大公约数
IntMath.mod(3, 5);//取模
IntMath.pow(3, 5);//取幂
IntMath.isPowerOfTwo(4);//是否2的幂
IntMath.factorial(7);//阶乘
IntMath.binomial(4, 3);//二项式系数
//LongMath与BigIntegerMath同理
//浮点数运算JDK的Math包已经涵盖比较全面 但是guava依旧提供了一些方法
DoubleMath.isMathematicalInteger(4.0);//判断该浮点数是不是一个整数
DoubleMath.roundToInt(3.14,RoundingMode.DOWN);// 舍入为int;对无限小数、溢出抛出异常同理tolong,toBigInteger
DoubleMath.log2(3.14, RoundingMode.DOWN);//2的浮点对数,并舍入为int
反射工具
//类型工具TypeToken
//获取原始类
TypeToken stringTok = TypeToken.of(String.class);
TypeToken intTok = TypeToken.of(Integer.class);
//获取泛型类
TypeToken> stringListTok = new TypeToken>() {};
//获取通配符类型
TypeToken
更多Guava介绍参考
Guava开源Git链接