1,基本工具
Preconditions
String abc = null;
Preconditions.checkArgument(abc!=null);
Preconditions.checkArgument(abc!=null,"abc !=null",abc);
//抛出非法参数异常
常见Object方法
Objects.equal("a", "a"); // returns true
Objects.equal(null, "a"); // returns false
Objects.equal("a", null); // returns false
Objects.equal(null, null); // returns true
Objects.hashCode("dsaf");
ComparisonChain 处理比较器
ComparisonChain.start().compare(1,1).compare(2,2).result();
Ordering 比较器
Ordering<String> byLengthOrdering = new Ordering<String>() {
@Override
public int compare(@Nullable String left, @Nullable String right) {
return Ints.compare(left.length(),right.length());
}
};
Ordering<String> ordering = Ordering.natural().nullsFirst().onResultOf(new Function<String, Comparable>() {
@Nullable
@Override
public Comparable apply(@Nullable String s) {
return s;
}
});
List<String> list = Lists.newArrayList();
Collections.sort(list,ordering);
集合
不可变集合
ImmutableSet<String> set = ImmutableSet.of("1","2");
ImmutableList<String> list = ImmutableList.of("1","2");
ImmutableBiMap<String,String> bimap = ImmutableBiMap.of("k1","v1","k2","v2");
ImmutableListMultimap<String,String> listMultimap = ImmutableListMultimap.of("k1","v1");
不可变集合优点
当集合被不可信的库调用时,不可变形式是安全的
不可变对象被多个线程调用时,不存在竞态条件
不可变对象因为有固定不变,可以作为常量来安全使用
新集合类型
Multiset
它可以多次添加相等的元素
可以用两种方式看待Multiset:
- 没有元素顺序限制的ArrayList
- Map<E, Integer>,键为元素,值为计数
Multiset<String> multiset = HashMultiset.create();
multiset.add("a1");
multiset.add("a1");
multiset.add("a1");
int a1 = multiset.count("a1");
for (String key:multiset.elementSet()){
System.out.println(key);
}
System.out.println(a1);
Guava提供了Multiset的多种实现,这些实现基本对应了JDK中Map的实现
Map Corresponding Multiset Supports null elements
HashMap HashMultiset Yes
TreeMap TreeMultiset Yes (if the comparator does)
LinkedHashMap LinkedHashMultiset Yes
ConcurrentHashMap ConcurrentHashMultiset No
ImmutableMap ImmutableMultiset No
Multimap
Multimap<String,String> multimap = HashMultimap.create();
multimap.put("k1","a");
multimap.put("k1","aa");
multimap.put("k1","aaa");
Collection<String> k1 = multimap.get("k1");
System.out.println(k1);
实现 | 键行为类似 | 值行为类似 |
---|---|---|
ArrayListMultimap | HashMap | ArrayList |
HashMultimap | HashMap | HashSet |
LinkedListMultimap* | LinkedHashMap* | LinkedList* |
LinkedHashMultimap** | LinkedHashMap | LinkedHashMap |
TreeMultimap | TreeMap | TreeSet |
ImmutableListMultimap | ImmutableMap | ImmutableList |
ImmutableSetMultimap | ImmutableMap | ImmutableSet |
BiMap
BiMap<String,String> biMap = HashBiMap.create();
biMap.put("k1","v1");
biMap.put("k2","v2");
//biMap.put("k1","v2"); //报错
biMap.forcePut("k1","v2");
System.out.println(biMap.inverse().get("v2"));//k1
System.out.println(biMap.get("k2")); // null
BiMap的各种实现
键**–**值实现 | 值**–**键实现 | 对应的BiMap实现 |
---|---|---|
HashMap | HashMap | HashBiMap |
ImmutableMap | ImmutableMap | ImmutableBiMap |
EnumMap | EnumMap | EnumBiMap |
EnumMap | HashMap | EnumHashBiMap |
Table
Table<Integer, Integer,String> table = HashBasedTable.create();
for (int r = 0; r <10 ; r++) {
for (int c = 0; c <10 ; c++) {
table.put(r,c,r + "*" + c + "=" + r*c);
}
}
System.out.println(table.get(2,1));
Table有如下几种实现:
- HashBasedTable:本质上用HashMap<R, HashMap<C, V>>实现;
- TreeBasedTable:本质上用TreeMap<R, TreeMap<C,V>>实现;
- ImmutableTable:本质上用ImmutableMap<R, ImmutableMap<C, V>>实现;注:ImmutableTable对稀疏或密集的数据集都有优化。
- ArrayTable:要求在构造时就指定行和列的大小,本质上由一个二维数组实现,以提升访问速度和密集Table的内存利用率。ArrayTable与其他Table的工作原理有点不同,请参见Javadoc了解详情。
ClassToInstanceMap
ClassToInstanceMap<Number> numberDefaults= MutableClassToInstanceMap.create();
numberDefaults.putInstance(Integer.class, Integer.valueOf(100));
Integer instance = numberDefaults.getInstance(Integer.class);
System.out.println(instance);
ClassToInstanceMap,Guava提供了两种有用的实现:
MutableClassToInstanceMap和 ImmutableClassToInstanceMap。
RangeSet
RangeSet描述了一组不相连的、非空的区间。当把一个区间添加到可变的RangeSet时,所有相连的区间会被合并,空区间会被忽略
RangeSet<Integer> 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
RangeMap描述了”不相交的、非空的区间”到特定值的映射。和RangeSet不同,RangeMap不会合并相邻的映射,即便相邻的区间映射到相同的值
RangeMap<Integer, String> 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"}
RangeMap提供两个视图:
- asMapOfRanges():用Map<Range, V>表现RangeMap。这可以用来遍历RangeMap。
- subRangeMap(Range):用RangeMap类型返回RangeMap与给定Range的交集视图。这扩展了传统的headMap、subMap和tailMap操作。
集合扩展工具类
Forwarding装饰器
针对所有类型的集合接口,Guava都提供了Forwarding抽象类以简化装饰者模式的使用。
Map<String,String> map = new HashMap<>();
ForwardingMap<String,String> fmap = new ForwardingMap<String, String>() {
@Override
protected Map<String, String> delegate() {
return map;
}
};
fmap.put("a","a11");
fmap.put("b","b111");
fmap.put("c","c111");
System.out.println(fmap.entrySet());
缓存
Guava Cache与ConcurrentMap很相似,但也不完全一样,Guava Cache为了限制内存占用,通常都设定为自动回收元素
RemovalListener removalListener = new RemovalListener() {
@Override
public void onRemoval(RemovalNotification removalNotification) {
//移除监听
System.out.println( "removalListener:"+removalNotification);
}
};
LoadingCache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(1) //最大容量 可以存1个
.expireAfterWrite(1, TimeUnit.SECONDS) // 过期时间 1秒
.removalListener(removalListener) //移除监听
.build(new CacheLoader<String, String>() {
@Override
public String load(String s) throws Exception {
//当缓存不存在时 调用该方法
System.out.println("load:"+s);
return "空";
}
});
for (int i = 0; i <5 ; i++) {
cache.put(String.valueOf(i),String.valueOf(i));
Thread.sleep(1100);
}
for (int i = 0; i <5 ; i++) {
String s = cache.get(String.valueOf(i));
System.out.println("for :"+s);
Thread.sleep(1100);
}
显式插入
使用[cache.put(key, value)](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/cache/Cache.html#put(K, V))方法可以直接向缓存中插入值,这会直接覆盖掉给定键之前映射的值
使用Cache.asMap()视图提供的任何方法也能修改缓存。但请注意,asMap视图的任何方法都不能保证缓存项被原子地加载到缓存中。进一步说,asMap视图的原子运算在Guava Cache的原子加载范畴之外,所以相比于Cache.asMap().putIfAbsent(K,
V),Cache.get(K, Callable) 应该总是优先使用。
缓存回收
基于容量的回收
如果要规定缓存项的数目不超过固定值,只需使用
CacheBuilder.maximumSize(long)
定时回收
CacheBuilder提供两种定时回收的方法:
- [
expireAfterAccess(long, TimeUnit)
](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/cache/CacheBuilder.html#expireAfterAccess(long, java.util.concurrent.TimeUnit)):缓存项在给定时间内没有被读/写访问,则回收。请注意这种缓存的回收顺序和基于大小回收一样。 - [
expireAfterWrite(long, TimeUnit)
](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/cache/CacheBuilder.html#expireAfterWrite(long, java.util.concurrent.TimeUnit)):缓存项在给定时间内没有被写访问(创建或覆盖),则回收。如果认为缓存数据总是在固定时候后变得陈旧不可用,这种回收方式是可取的。
基于引用的回收
通过使用弱引用的键、或弱引用的值、或软引用的值,Guava Cache可以把缓存设置为允许垃圾回收:
CacheBuilder.weakKeys()
:使用弱引用存储键。当键没有其它(强或软)引用时,缓存项可以被垃圾回收。因为垃圾回收仅依赖恒等式(),使用弱引用键的缓存用而不是equals比较键。CacheBuilder.weakValues()
:使用弱引用存储值。当值没有其它(强或软)引用时,缓存项可以被垃圾回收。因为垃圾回收仅依赖恒等式(),使用弱引用值的缓存用而不是equals比较值。CacheBuilder.softValues()
:使用软引用存储值。软引用只有在响应内存需要时,才按照全局最近最少使用的顺序回收。考虑到使用软引用的性能影响,我们通常建议使用更有性能预测性的缓存大小限定(见上文,基于容量回收)。使用软引用值的缓存同样用==而不是equals比较值。
显式清除
- 个别清除:
Cache.invalidate(key)
- 批量清除:
Cache.invalidateAll(keys)
- 清除所有缓存项:
Cache.invalidateAll()
刷新
如果刷新过程抛出异常,缓存将保留旧值,而异常会在记录到日志后被丢弃
重载[CacheLoader.reload(K, V)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/cache/CacheLoader.html#reload(K, V))可以扩展刷新时的行为,这个方法允许开发者在计算新值时使用旧的值。
[CacheBuilder.refreshAfterWrite(long, TimeUnit)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/cache/CacheBuilder.html#refreshAfterWrite(long, java.util.concurrent.TimeUnit))可以为缓存增加自动定时刷新功能
refreshAfterWrite通过定时刷新可以让缓存项保持可用
因此,如果你在缓存上同时声明expireAfterWrite和refreshAfterWrite,缓存并不会因为刷新盲目地定时重置,如果缓存项没有被检索,那刷新就不会真的发生,缓存项在过期时间后也变得可以回收。
其他特性
CacheBuilder.recordStats()
用来开启Guava Cache的统计功能。统计打开后,
Cache.stats()
方法会返回
CacheStats
对象以提供如下统计信息
-
hitRate()
:缓存命中率; -
averageLoadPenalty()
:加载新值的平均时间,单位为纳秒; -
evictionCount()
:缓存项被回收的总数,不包括显式清除。
并发
ListenableFuture解析
ListenableFuture的创建
ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));
ListenableFuture<String> future = service.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return "hello call";
}
});
Futures.addCallback(future, new FutureCallback<String>() {
@Override
public void onSuccess(@Nullable String s) {
}
@Override
public void onFailure(Throwable throwable) {
}
},service);
使用ListenableFuture
最重要的理由是它可以进行一系列的复杂链式的异步操作
ListenableFuture queryFuture = Futures.transform(rowKeyFuture, new Function<String, String>() {
@Nullable
@Override
public String apply(@Nullable String s) {
System.out.println("apply:"+s);
return "apply = "+s;
}
}, service);
Futures.addCallback(queryFuture, new FutureCallback<String>() {
@Override
public void onSuccess(@Nullable String s) {
System.out.println("onSuccess:"+s);
}
@Override
public void onFailure(Throwable throwable) {
System.out.println("onFailure");
}
},service);
Concurrent包里的Service框架浅析
一个服务正常生命周期有
- Service.State.NEW
- Service.State.STARTING
- Service.State.RUNNING
- Service.State.STOPPING
- Service.State.TERMINATED
服务一旦被停止就无法再重新启动了。如果服务在starting、running、stopping状态出现问题、会进入Service.State.FAILED
.状态
调用 startAsync()
方法可以异步开启一个服务,同时返回this对象形成方法调用链
停止一个服务也是类似的、使用异步方法stopAsync()
。但是不像startAsync(),多次调用这个方法是安全的。这是为了方便处理关闭服务时候的锁竞争问题。
Service也提供了一些方法用于等待服务状态转换的完成:
通过 addListener()方法异步添加监听器。此方法允许你添加一个 Service.Listener 、它会在每次服务状态转换的时候被调用
同步使用awaitRunning()。如果服务没有成功启动,会抛出IllegalStateException异常
awaitTerminated()
方法会等待服务达到终止状态(TERMINATED
或者 FAILED
)。两个方法都有重载方法允许传入超时时间。
Service
接口本身实现起来会比较复杂、且容易碰到一些捉摸不透的问题。因此我们不推荐直接实现这个接口。而是请继承Guava包里已经封装好的基础抽象类。每个基础类支持一种特定的线程模型。
基础实现类
AbstractIdleService
AbstractIdleService
类简单实现了Service接口、其在running状态时不会执行任何动作–因此在running时也不需要启动线程–但需要处理开启/关闭动作。要实现一个此类的服务,只需继承AbstractIdleService类,然后自己实现startUp()
和shutDown()
方法就可以了。
AbstractExecutionThreadService
AbstractExecutionThreadService
通过单线程处理启动、运行、和关闭等操作。你必须重载run()方法,同时需要能响应停止服务的请求。具体的实现可以在一个循环内做处理:
另外,你还可以重载triggerShutdown()
方法让run()方法结束返回。
start()内部会调用startUp()方法,创建一个线程、然后在线程内调用run()方法。stop()会调用 triggerShutdown()方法并且等待线程终止。
AbstractScheduledService
AbstractScheduledService
类用于在运行时处理一些周期性的任务。子类可以实现 runOneIteration()
方法定义一个周期执行的任务,以及相应的startUp()和shutDown()方法。为了能够描述执行周期,你需要实现scheduler()
方法。通常情况下,你可以使用AbstractScheduledService.Scheduler类提供的两种调度器:
[newFixedRateSchedule(initialDelay, delay, TimeUnit)
](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/util/concurrent/AbstractScheduledService.Scheduler.html#newFixedRateSchedule(long, long, java.util.concurrent.TimeUnit)) 和[newFixedDelaySchedule(initialDelay, delay, TimeUnit)
](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/util/concurrent/AbstractScheduledService.Scheduler.html#newFixedDelaySchedule(long, long, java.util.concurrent.TimeUnit)),类似于JDK并发包中ScheduledExecutorService类提供的两种调度方式
如要自定义schedules则可以使用 CustomScheduler
类来辅助实现;具体用法见javadoc。
AbstractService
如需要自定义的线程管理、可以通过扩展 AbstractService
类来实现。一般情况下、使用上面的几个实现类就已经满足需求了,但如果在服务执行过程中有一些特定的线程处理需求、则建议继承AbstractService类。
继承AbstractService方法必须实现两个方法.
- doStart(): 首次调用startAsync()时会同时调用doStart(),doStart()内部需要处理所有的初始化工作、如果启动成功则调用
notifyStarted()
方法;启动失败则调用notifyFailed()
- doStop(): 首次调用stopAsync()会同时调用doStop(),doStop()要做的事情就是停止服务,如果停止成功则调用
notifyStopped()
方法;停止失败则调用notifyFailed()
方法。
doStart和doStop方法的实现需要考虑下性能,尽可能的低延迟。如果初始化的开销较大,如读文件,打开网络连接,或者其他任何可能引起阻塞的操作,建议移到另外一个单独的线程去处理。
使用ServiceManager
通过实例化ServiceManager类来创建一个Service集合,你可以通过以下方法来管理它们:
- startAsync() : 将启动所有被管理的服务。如果当前服务的状态都是NEW的话、那么你只能调用该方法一次、这跟 Service#startAsync()是一样的。
- stopAsync() **:**将停止所有被管理的服务。
- addListener **:**会添加一个
ServiceManager.Listener
,在服务状态转换中会调用该Listener - awaitHealthy() **:**会等待所有的服务达到Running状态
- **awaitStopped():**会等待所有服务达到终止状态
检测类的方法有:
- isHealthy() **:**如果所有的服务处于Running状态、会返回True
- **servicesByState():**以状态为索引返回当前所有服务的快照
- **startupTimes() :**返回一个Map对象,记录被管理的服务启动的耗时、以毫秒为单位,同时Map默认按启动时间排序。
我们建议整个服务的生命周期都能通过ServiceManager来管理,不过即使状态转换是通过其他机制触发的、也不影响ServiceManager方法的正确执行。
例如:当一个服务不是通过startAsync()、而是其他机制启动时,listeners 仍然可以被正常调用、awaitHealthy()也能够正常工作。ServiceManager 唯一强制的要求是当其被创建时所有的服务必须处于New状态。
static class AbstractExecutionThreadServiceTest extends AbstractExecutionThreadService{
//声明一个状态
volatile boolean running = true;
@Override
protected void startUp() throws Exception {
在这里我们可以做一些初始化操作
}
@Override
protected void run() throws Exception {
while (running){
// do our work
try{
Uninterruptibles.sleepUninterruptibly(2, TimeUnit.SECONDS);
System.out.println("do our work.....");
}catch (Exception e){
e.printStackTrace();
}
}
}
@Override
protected void triggerShutdown() {
//这里我们改变状态值,run方法中就能够得到响应。
running = false;
//可以做一些清理操作,也可以移到shutDown()方法中执行
}
@Override
protected void shutDown() throws Exception {
//可以关闭资源,关闭连接。。。
}
}
public static void main(String[] args)throws Exception {
AbstractExecutionThreadServiceTest service = new AbstractExecutionThreadServiceTest();
service.addListener(new Service.Listener() {
@Override
public void starting() {
System.out.println("服务开始启动.....");
}
@Override
public void running() {
System.out.println("服务开始运行");;
}
@Override
public void stopping(Service.State from) {
System.out.println("服务关闭中");
}
@Override
public void terminated(Service.State from) {
System.out.println("服务终止");
}
@Override
public void failed(Service.State from, Throwable failure) {
System.out.println("失败,cause:" + failure.getCause());
}
}, MoreExecutors.directExecutor());
service.startAsync().awaitRunning();
System.out.println("服务状态为:" + service.state());
Thread.sleep(10 * 1000);
service.stopAsync().awaitTerminated();
System.out.println("服务状态为:" + service.state());
}
//triggerShutdown() 方法会在执行方法stopAsync调用,startUp方法会在执行startAsync方法时调用,这个类的实现都是委托给AbstractService这个方法实现的
//AbstractScheduledService类用于在运行时处理一些周期性的任务
static class AbstractScheduledServiceTest extends AbstractScheduledService{
@Override
protected void startUp() throws Exception {
super.startUp();
}
@Override
protected void shutDown() throws Exception {
super.shutDown();
}
@Override
protected void runOneIteration() throws Exception {
// //处理异常,这里如果抛出异常,会使服务状态变为failed同时导致任务终止
try {
System.out.println("do work....");
} catch (Exception e) {
//处理异常
}
}
@Override
protected Scheduler scheduler() {
return Scheduler.newFixedDelaySchedule(1, 5, TimeUnit.SECONDS);
}
}
private static void startSchedulerService()throws Exception{
AbstractScheduledServiceTest service = new AbstractScheduledServiceTest();
service.addListener(new Service.Listener() {
@Override
public void starting() {
System.out.println("服务开始启动.....");
}
@Override
public void running() {
System.out.println("服务开始运行");
;
}
@Override
public void stopping(Service.State from) {
System.out.println("服务关闭中");
}
@Override
public void terminated(Service.State from) {
System.out.println("服务终止");
}
@Override
public void failed(Service.State from, Throwable failure) {
System.out.println("失败,cause:" + failure.getCause());
}
}, MoreExecutors.directExecutor());
service.startAsync().awaitRunning();
System.out.println("服务状态为:" + service.state());
Thread.sleep(10 * 1000);
service.stopAsync().awaitTerminated();
System.out.println("服务状态为:" + service.state());
}
//AbstractScheduledServic默认使用Executors.newSingleThreadScheduledExecutor来执行的
字符串处理:分割,连接,填充
连接器[Joiner]
String s = Joiner.on(",").skipNulls().join(Arrays.asList("HH","BB","CC"));
System.out.println(s);
拆分器[Splitter]
Iterable<String> split = Splitter.on(',').trimResults().omitEmptyStrings().split("foor,ddd, ,iii");
for (String s:split){
System.out.println(s);
}
List<String> list = Splitter.on(',').trimResults().omitEmptyStrings().splitToList(" foo, gg, ,,gg ");
System.out.println(list);
字符匹配器[CharMatcher]
//只保留数字字符
String digit = CharMatcher.digit().retainFrom("fdsfsf234324");
System.out.println(digit);
//移除control字符
String sdfa = CharMatcher.javaIsoControl().removeFrom("=sdfa");
System.out.println(sdfa);
String s = CharMatcher.is('a').retainFrom("afsdssaf");
System.out.println(s);
String s = CharMatcher.anyOf("12345").retainFrom("df32vewwrw2");
System.out.println(s);
创建
CharMatcher is(char match): 返回匹配指定字符的Matcher
CharMatcher isNot(char match): 返回不匹配指定字符的Matcher
CharMatcher anyOf(CharSequence sequence): 返回匹配sequence中任意字符的Matcher
CharMatcher noneOf(CharSequence sequence): 返回不匹配sequence中任何一个字符的Matcher
CharMatcher inRange(char startInclusive, char endIncludesive): 返回匹配范围内任意字符的Matcher
CharMatcher forPredicate(Predicate<? super Charater> predicate): 返回使用predicate的apply()判断匹配的Matcher
CharMatcher negate(): 返回以当前Matcher判断规则相反的Matcher
CharMatcher and(CharMatcher other): 返回与other匹配条件组合做与来判断的Matcher
CharMatcher or(CharMatcher other): 返回与other匹配条件组合做或来判断的Matcher
使用方法
boolean matchesAnyOf(CharSequence sequence): 只要sequence中有任意字符能匹配Matcher,返回true
boolean matchesAllOf(CharSequence sequence): sequence中所有字符都能匹配Matcher,返回true
boolean matchesNoneOf(CharSequence sequence): sequence中所有字符都不能匹配Matcher,返回true
int indexIn(CharSequence sequence): 返回sequence中匹配到的第一个字符的坐标
int indexIn(CharSequence sequence, int start): 返回从start开始,在sequence中匹配到的第一个字符的坐标
int lastIndexIn(CharSequence sequence): 返回sequence中最后一次匹配到的字符的坐标
int countIn(CharSequence sequence): 返回sequence中匹配到的字符计数
String removeFrom(CharSequence sequence): 删除sequence中匹配到到的字符并返回
String retainFrom(CharSequence sequence): 保留sequence中匹配到的字符并返回
String replaceFrom(CharSequence sequence, char replacement): 替换sequence中匹配到的字符并返回
String trimFrom(CharSequence sequence): 删除首尾匹配到的字符并返回
String trimLeadingFrom(CharSequence sequence): 删除首部匹配到的字符
String trimTrailingFrom(CharSequence sequence): 删除尾部匹配到的字符
String collapseFrom(CharSequence sequence, char replacement): 将匹配到的组(连续匹配的字符)替换成replacement
String trimAndCollapseFrom(CharSequence sequence, char replacement): 先trim在replace
//去掉控制字符(\t,\n,\b...)
System.out.println(CharMatcher.JAVA_ISO_CONTROL.removeFrom(string));
//获取所有的数字
System.out.println(CharMatcher.DIGIT.retainFrom(string));
//把多个空格替换为一个包括\t,并去掉首位的空格
System.out.println(CharMatcher.WHITESPACE.trimAndCollapseFrom(string, ' '));
//把所有的数字用"*"代替
System.out.println(CharMatcher.JAVA_DIGIT.replaceFrom(string, "*"));
//获取所有的数字和小写字母
System.out.println(CharMatcher.JAVA_DIGIT.or(CharMatcher.JAVA_LOWER_CASE).retainFrom(string));
//获取所有的大写字母
System.out.println(CharMatcher.JAVA_UPPER_CASE.retainFrom(string));
//获取所有单字节长度的符号
System.out.println(CharMatcher.SINGLE_WIDTH.retainFrom(string));
原生类型
Java的原生类型就是指基本类型:byte、short、int、long、float、double、char和boolean。
原生类型不能当作对象或泛型的类型参数使用,这意味着许多通用方法都不能应用于它们
Guava提供了若干通用工具,包括原生类型数组与集合API的交互,原生类型和字节数组的相互转换,以及对某些原生类型的无符号形式的支持。
原生类型 | Guava工具类(都在com.google.common.primitives包) |
---|---|
byte | Bytes , SignedBytes , UnsignedBytes |
short | Shorts |
int | Ints , UnsignedInteger , UnsignedInts |
long | Longs , UnsignedLong , UnsignedLongs |
float | Floats |
double | Doubles |
char | Chars |
boolean | Booleans |
区间
凸性表示对a<=b<=c, range.contains(a)且range.contains©意味着range.contains(b)。
区间可以延伸至无限——例如,范围”x>3″包括任意大于3的值——也可以被限制为有限,如” 2<=x<5″
构建区间
由Range类的静态方法获取
(a…b) | open(C, C) |
---|---|
[a…b] | closed(C, C) |
[a…b) | closedOpen(C, C) |
(a…b] | openClosed(C, C) |
(a…+∞) | greaterThan© |
[a…+∞) | atLeast© |
(-∞…b) | lessThan© |
(-∞…b] | atMost© |
(-∞…+∞) | all() |
Range.closed(“left”, “right”); //字典序在"left"和"right"之间的字符串,闭区间
Range.lessThan(4.0); //严格小于4.0的double值
也可以明确地指定边界类型来构造区间:
有界区间 | range(C, BoundType, C, BoundType) |
---|---|
无上界区间:((a…+∞) 或[a…+∞)) | downTo(C, BoundType) |
无下界区间:((-∞…b) 或(-∞…b]) | upTo(C, BoundType) |
Range.downTo(4, boundType);// (a…+∞)或[a…+∞),取决于boundType
Range.range(1, CLOSED, 4, OPEN);// [1…4),等同于Range.closedOpen(1, 4)
Range.closed(1, 3).contains(2);//return true
Range.closed(1, 3).contains(4);//return false
Range.lessThan(5).contains(5); //return false
Range.closed(1, 4).containsAll(Ints.asList(1, 2, 3)); //return true
查询运算
Range类提供了以下方法来 查看区间的端点:
- hasLowerBound()和hasUpperBound():判断区间是否有特定边界,或是无限的;
- lowerBoundType()和upperBoundType():返回区间边界类型,CLOSED或OPEN;如果区间没有对应的边界,抛出IllegalStateException;
- lowerEndpoint()和upperEndpoint():返回区间的端点值;如果区间没有对应的边界,抛出IllegalStateException;
- isEmpty():判断是否为空区间。
Range.closedOpen(4, 4).isEmpty(); // returns true
Range.openClosed(4, 4).isEmpty(); // returns true
Range.closed(4, 4).isEmpty(); // returns false
Range.open(4, 4).isEmpty(); // Range.open throws IllegalArgumentException
Range.closed(3, 10).lowerEndpoint(); // returns 3
Range.open(3, 10).lowerEndpoint(); // returns 3
Range.closed(3, 10).lowerBoundType(); // returns CLOSED
Range.open(3, 10).upperBoundType(); // returns OPEN
关系运算
包含[enclose]
- [3…6] 包含[4…5] ;
- (3…6) 包含(3…6) ;
- [3…6] 包含[4…4),虽然后者是空区间;
- (3…6]不 包含[3…6] ;
- [4…5]不 包含(3…6),虽然前者包含了后者的所有值,离散域[discrete domains]可以解决这个问题(见8.5节);
- [3…6]不 包含(1…1],虽然前者包含了后者的所有值。
相连[isConnected]
Range.isConnected(Range)
判断区间是否是相连的
相连是一种自反的[reflexive]、对称的[symmetric]关系
Range.closed(3, 5).isConnected(Range.open(5, 10)); // returns true
Range.closed(0, 9).isConnected(Range.closed(3, 4)); // returns true
Range.closed(0, 5).isConnected(Range.closed(3, 9)); // returns true
Range.open(3, 5).isConnected(Range.open(5, 10)); // returns false
Range.closed(1, 5).isConnected(Range.closed(6, 10)); // returns false
跨区间[span]
Range.span(Range)
返回”同时包括两个区间的最小区间”,如果两个区间相连,那就是它们的并集。
Range.closed(3, 5).span(Range.open(5, 10)); // returns [3, 10)
Range.closed(0, 9).span(Range.closed(3, 4)); // returns [0, 9]
Range.closed(0, 5).span(Range.closed(3, 9)); // returns [0, 9]
Range.open(3, 5).span(Range.open(5, 10)); // returns (3, 10)
Range.closed(1, 5).span(Range.closed(6, 10)); // returns [1, 10]
离散域
部分(但不是全部)可比较类型是离散的,即区间的上下边界都是可枚举的。
I/O
ByteStreams 和CharStreams
ByteStreams | CharStreams |
---|---|
byte[] toByteArray(InputStream) | String toString(Readable) |
N/A | List readLines(Readable) |
[long copy(InputStream, OutputStream) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/io/ByteStreams.html#copy(java.io.InputStream, java.io.OutputStream)) | [long copy(Readable, Appendable) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/io/CharStreams.html#copy(java.lang.Readable, java.lang.Appendable)) |
void readFully | N/A |
[void skipFully(InputStream, long) ](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/io/ByteStreams.html#skipFully(java.io.InputStream, long)) | [void skipFully(Reader, long) ](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/io/CharStreams.html#skipFully(java.io.Reader, long)) |
OutputStream nullOutputStream() | Writer nullWriter() |
关于InputSupplier 和OutputSupplier要注意:
在ByteStreams、CharStreams以及com.google.common.io包中的一些其他类中,某些方法仍然在使用InputSupplier和OutputSupplier接口。这两个借口和相关的方法是不推荐使用的:它们已经被下面描述的source和sink类型取代了,并且最终会被移除。
源与汇
Guava有一系列关于源与汇的抽象
源是可读的,汇是可写的
源与汇按照字节和字符划分类型
字节 | 字符 | |
---|---|---|
读 | ByteSource | CharSource |
写 | ByteSink | CharSink |
创建源与汇
Guava提供了若干源与汇的实现:
字节 | 字符 |
---|---|
Files.asByteSource(File) | [Files.asCharSource(File, Charset) ](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/io/Files.html#asCharSource(java.io.File, java.nio.charset.Charset)) |
[Files.asByteSink(File, FileWriteMode...) ](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/io/Files.html#asByteSink(java.io.File, com.google.common.io.FileWriteMode…)) | [Files.asCharSink(File, Charset, FileWriteMode...) ](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/io/Files.html#asCharSink(java.io.File, java.nio.charset.Charset, com.google.common.io.FileWriteMode…)) |
Resources.asByteSource(URL) | [Resources.asCharSource(URL, Charset) ](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/io/Resources.html#asCharSource(java.net.URL, java.nio.charset.Charset)) |
ByteSource.wrap(byte[]) | CharSource.wrap(CharSequence) |
ByteSource.concat(ByteSource...) | CharSource.concat(CharSource...) |
[ByteSource.slice(long, long) ](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/io/ByteSource.html#slice(long, long)) | N/A |
N/A | ByteSource.asCharSource(Charset) |
N/A | ByteSink.asCharSink(Charset) |
通用操作
- openStream():根据源与汇的类型,返回InputStream、OutputStream、Reader或者Writer。
- openBufferedStream():根据源与汇的类型,返回InputStream、OutputStream、BufferedReader或者BufferedWriter。返回的流保证在必要情况下做了缓冲。例如,从字节数组读数据的源就没有必要再在内存中作缓冲,这就是为什么该方法针对字节源不返回BufferedInputStream。字符源属于例外情况,它一定返回BufferedReader,因为BufferedReader中才有readLine()方法。
源操作
汇操作
字节汇 | 字符汇 |
---|---|
void write(byte[]) | void write(CharSequence) |
long writeFrom(InputStream) | long writeFrom(Readable) |
N/A | void writeLines(Iterable) |
N/A | [void writeLines(Iterable, String) ](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/io/CharSink.html#writeLines(java.lang.Iterable, java.lang.String)) |
范例
File file = new File("/Users/btcc/Documents/workspace/qqface/web_qqface/qqface/src/main/java/com/dangao/qqface/demo/test2/GuavaTest02.java");
CharSource charSource = Files.asCharSource(file, Charsets.UTF_8);
ImmutableList<String> strings = charSource.readLines();
HashMultiset<String> hashMultiset = HashMultiset.create(
Splitter.on(CharMatcher.whitespace())
.trimResults()
.omitEmptyStrings()
.split(Files.asCharSource(file, Charsets.UTF_8).read())
);
HashCode hash = Files.asByteSource(file).hash(Hashing.sha256());
System.out.println(hash.toString());
//Resources.asByteSource(file.toURL()).copyTo(Files.asByteSink(file));
文件操作
除了创建文件源和文件的方法,Files类还包含了若干你可能感兴趣的便利方法。
createParentDirs(File) | 必要时为文件创建父目录 |
---|---|
getFileExtension(String) | 返回给定路径所表示文件的扩展名 |
getNameWithoutExtension(String) | 返回去除了扩展名的文件名 |
simplifyPath(String) | 规范文件路径,并不总是与文件系统一致,请仔细测试 |
fileTreeTraverser() | 返回TreeTraverser用于遍历文件树 |
散列
散列包的组成
HashFunction hf = Hashing.md5();
HashCode hc = hf.newHasher()
.putLong(1000)
.putString("name", Charsets.UTF_8)
//.putObject(person, personFunnel)
.hash();
System.out.println(hc);
HashFunction
HashFunction是一个单纯的(引用透明的)、无状态的方法,它把任意的数据块映射到固定数目的位指,并且保证相同的输入一定产生相同的输出,不同的输入尽可能产生不同的输出。
Hasher
HashFunction的实例可以提供有状态的Hasher,Hasher提供了流畅的语法把数据添加到散列运算,然后获取散列值。Hasher可以接受所有原生类型、字节数组、字节数组的片段、字符序列、特定字符集的字符序列等等,或者任何给定了Funnel实现的对象。
Hasher实现了PrimitiveSink接口,这个接口为接受原生类型流的对象定义了fluent风格的API
Funnel
Funnel描述了如何把一个具体的对象类型分解为原生字段值,从而写入PrimitiveSink
比如,如果我们有这样一个类:
class Person {
final int id;
final String firstName;
final String lastName;
final int birthYear
}
//它对应的Funnel实现可能是:
Funnel<Person> personFunnel = new Funnel<Person>() {
@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(birthYear);
}
}
HashCode
一旦Hasher被赋予了所有输入,就可以通过hash()方法获取HashCode实例(多次调用hash()方法的结果是不确定的)。HashCode可以通过asInt()、asLong()、asBytes()方法来做相等性检测,此外,[writeBytesTo(array, offset, maxLength)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/hash/HashCode.html#writeBytesTo(byte[], int, int))把散列值的前maxLength字节写入字节数组。
布鲁姆过滤器[BloomFilter]
它允许你检测某个对象是一定不在过滤器中,还是可能已经添加到过滤器了
Guava散列包有一个内建的布鲁姆过滤器实现,你只要提供Funnel就可以使用它
static class Person {
int id;
String firstName;
String lastName;
int birthYear;
}
//它对应的Funnel实现可能是:
static Funnel<Person> personFunnel = new Funnel<Person>() {
@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);
}
};
public static void main(String[] args)throws Exception {
BloomFilter<Person> bloomFilter = BloomFilter.create(personFunnel,50000,0.001);
for (int i = 0; i <100 ; i++) {
Person person = new Person();
person.id = i;
person.birthYear = i;
person.firstName = String.valueOf(i);
person.lastName = String.valueOf(i);
bloomFilter.put(person);
}
int i = 1;
Person person = new Person();
person.id = i;
person.birthYear = i;
person.firstName = String.valueOf(i);
person.lastName = String.valueOf(i);
if (bloomFilter.mightContain(person)){
//dude不是朋友还运行到这里的概率为1%
//在这儿,我们可以在做进一步精确检查的同时触发一些异步加载
System.out.println("可能包含");
}else {
System.out.println("不包含");
}
}
Hashing类
Hashing类提供了若干散列函数,以及运算HashCode对象的工具方法。
已提供的散列函数
md5() | murmur3_128() | murmur3_32() | sha1() |
---|---|---|---|
sha256() | sha512() | goodFastHash(int bits) |
HashCode运算
方法 | 描述 |
---|---|
HashCode`` ``combineOrdered( Iterable) | 以有序方式联接散列码,如果两个散列集合用该方法联接出的散列码相同,那么散列集合的元素可能是顺序相等的 |
HashCode combineUnordered( Iterable) | 以无序方式联接散列码,如果两个散列集合用该方法联接出的散列码相同,那么散列集合的元素可能在某种排序下是相等的 |
[int consistentHash( HashCode, int buckets) ](http://docs.guava-libraries.googlecode.com/git-history/release12/javadoc/com/google/common/hash/Hashing.html#consistentHash(com.google.common.hash.HashCode, int)) | 为给定的”桶”大小返回一致性哈希值。当”桶”增长时,该方法保证最小程度的一致性哈希值变化。详见一致性哈希。 |
事件总线
传统上,Java的进程内事件分发都是通过发布者和订阅者之间的显式注册实现的。设计EventBus就是为了取代这种显示注册方式,使组件间有了更好的解耦。
static EventBus eventBus = new EventBus();
public static class EventBusChangeRecorder {
@Subscribe
public void recordCustomerChange(String e) {
System.out.println(e);
}
}
public static void main(String[] args)throws Exception {
eventBus.register(new EventBusChangeRecorder());
eventBus.post("abcd");
}
事件监听者[Listeners]
- 传统实现:定义相应的事件监听者类,如CustomerChangeEventListener;
- EventBus实现:以CustomerChangeEvent为唯一参数创建方法,并用Subscribe注解标记。
数学运算
范例
int logFloor = LongMath.log2(4, RoundingMode.FLOOR);
int mustNotOverflow = IntMath.checkedMultiply(2, 3);
long quotient = LongMath.divide(9, 3, RoundingMode.UNNECESSARY); // fail fast on non-multiple of 3
BigInteger nearestInteger = DoubleMath.roundToBigInteger(10, RoundingMode.HALF_EVEN);
BigInteger sideLength = BigIntegerMath.sqrt(nearestInteger, RoundingMode.CEILING);
为什么使用Guava Math
- Guava Math针对各种不常见的溢出情况都有充分的测试;对溢出语义,Guava文档也有相应的说明;如果运算的溢出检查不能通过,将导致快速失败;
- Guava Math的性能经过了精心的设计和调优;虽然性能不可避免地依据具体硬件细节而有所差异,但Guava Math的速度通常可以与Apache Commons的MathUtils相比,在某些场景下甚至还有显著提升;
- Guava Math在设计上考虑了可读性和正确的编程习惯;IntMath.log2(x, CEILING) 所表达的含义,即使在快速阅读时也是清晰明确的。而32-Integer.numberOfLeadingZeros(x – 1)对于阅读者来说则不够清晰。
注意:Guava Math和GWT格外不兼容,这是因为Java和Java Script语言的运算溢出逻辑不一样。
整数运算
Guava Math主要处理三种整数类型:int、long和BigInteger。这三种类型的运算工具类分别叫做IntMath、LongMath和BigIntegerMath。
有溢出检查的运算
Guava Math提供了若干有溢出检查的运算方法:结果溢出时,这些方法将快速失败而不是忽略溢出
[IntMath.checkedAdd ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/IntMath.html#checkedAdd(int, int)) | [LongMath.checkedAdd ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/LongMath.html#checkedAdd(long, long)) |
---|---|
[IntMath.checkedSubtract ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/IntMath.html#checkedSubtract(int, int)) | [LongMath.checkedSubtract ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/LongMath.html#checkedSubtract(long, long)) |
[IntMath.checkedMultiply ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/IntMath.html#checkedMultiply(int, int)) | [LongMath.checkedMultiply ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/LongMath.html#checkedMultiply(long, long)) |
[IntMath.checkedPow ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/IntMath.html#checkedPow(int, int)) | [LongMath.checkedPow ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/LongMath.html#checkedPow(long, long)) |
实数运算
IntMath、LongMath和BigIntegerMath提供了很多实数运算的方法,并把最终运算结果舍入成整数
些方法接受一个java.math.RoundingMode枚举值作为舍入的模式:
- DOWN:向零方向舍入(去尾法)
- UP:远离零方向舍入
- FLOOR:向负无限大方向舍入
- CEILING:向正无限大方向舍入
- UNNECESSARY:不需要舍入,如果用此模式进行舍入,应直接抛出ArithmeticException
- HALF_UP:向最近的整数舍入,其中x.5远离零方向舍入
- HALF_DOWN:向最近的整数舍入,其中x.5向零方向舍入
- HALF_EVEN:向最近的整数舍入,其中x.5向相邻的偶数舍入
运算 | IntMath | LongMath | BigIntegerMath |
---|---|---|---|
除法 | [divide(int, int, RoundingMode) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/IntMath.html#divide(int, int, java.math.RoundingMode)) | [divide(long, long, RoundingMode) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/LongMath.html#divide(long, long, java.math.RoundingMode)) | [divide(BigInteger, BigInteger, RoundingMode) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/BigIntegerMath.html#divide(java.math.BigInteger, java.math.BigInteger, java.math.RoundingMode)) |
2为底的对数 | [log2(int, RoundingMode) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/IntMath.html#log2(int, java.math.RoundingMode)) | [log2(long, RoundingMode) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/LongMath.html#log2(long, java.math.RoundingMode)) | [log2(BigInteger, RoundingMode) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/BigIntegerMath.html#log2(java.math.BigInteger, java.math.RoundingMode)) |
10为底的对数 | [log10(int, RoundingMode) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/IntMath.html#log10(int, java.math.RoundingMode)) | [log10(long, RoundingMode) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/LongMath.html#log10(long, java.math.RoundingMode)) | [log10(BigInteger, RoundingMode) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/BigIntegerMath.html#log10(java.math.BigInteger, java.math.RoundingMode)) |
平方根 | [sqrt(int, RoundingMode) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/IntMath.html#sqrt(int, java.math.RoundingMode)) | [sqrt(long, RoundingMode) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/LongMath.html#sqrt(long, java.math.RoundingMode)) | [sqrt(BigInteger, RoundingMode) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/BigIntegerMath.html#sqrt(java.math.BigInteger, java.math.RoundingMode)) |
附加功能
Guava还另外提供了一些有用的运算函数
运算 | IntMath | LongMath | BigIntegerMath***** |
---|---|---|---|
最大公约数 | [gcd(int, int) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/IntMath.html#gcd(int, int)) | [gcd(long, long) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/LongMath.html#gcd(long, long)) | BigInteger.gcd(BigInteger) |
取模 | [mod(int, int) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/IntMath.html#mod(int, int)) | [mod(long, long) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/LongMath.html#mod(long, long)) | BigInteger.mod(BigInteger) |
取幂 | [pow(int, int) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/IntMath.html#pow(int, int)) | [pow(long, int) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/LongMath.html#pow(long, int)) | BigInteger.pow(int) |
是否2的幂 | isPowerOfTwo(int) | isPowerOfTwo(long) | isPowerOfTwo(BigInteger) |
阶乘* | factorial(int) | factorial(int) | factorial(int) |
二项式系数* | [binomial(int, int) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/IntMath.html#binomial(int, int)) | [binomial(int, int) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/LongMath.html#binomial(int, int)) | [binomial(int, int) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/BigIntegerMath.html#binomial(int, int)) |
*BigInteger的最大公约数和取模运算由JDK提供
*阶乘和二项式系数的运算结果如果溢出,则返回MAX_VALUE
浮点数运算
JDK比较彻底地涵盖了浮点数运算,但Guava在DoubleMath类中也提供了一些有用的方法。
isMathematicalInteger(double) | 判断该浮点数是不是一个整数 |
---|---|
[roundToInt(double, RoundingMode) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/DoubleMath.html#roundToInt(double, java.math.RoundingMode)) | 舍入为int;对无限小数、溢出抛出异常 |
[roundToLong(double, RoundingMode) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/DoubleMath.html#roundToLong(double, java.math.RoundingMode)) | 舍入为long;对无限小数、溢出抛出异常 |
[roundToBigInteger(double, RoundingMode) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/DoubleMath.html#roundToBigInteger(double, java.math.RoundingMode)) | 舍入为BigInteger;对无限小数抛出异常 |
[log2(double, RoundingMode) ](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/math/DoubleMath.html#log2(double, java.math.RoundingMode)) | 2的浮点对数,并且舍入为int,比JDK的Math.log(double) 更快 |
反射
由于类型擦除,你不能够在运行时传递泛型类对象——你可能想强制转换它们,并假装这些对象是有泛型的,但实际上它们没有。
ArrayList<String> stringList = Lists.newArrayList();
ArrayList<Integer> intList = Lists.newArrayList();
//true
System.out.println(stringList.getClass().isAssignableFrom(intList.getClass()));
Guava提供了TypeToken, 它使用了基于反射的技巧甚至让你在运行时都能够巧妙的操作和查询泛型类型
想象一下TypeToken是创建,操作,查询泛型类型(以及,隐含的类)对象的方法
介绍
获取一个基本的、原始类的TypeToken非常简单:
TypeToken<String> stringTok = TypeToken.of(String.class);
TypeToken<Integer> intTok = TypeToken.of(Integer.class);
为获得一个含有泛型的类型的TypeToken —— 当你知道在编译时的泛型参数类型 —— 你使用一个空的匿名内部类:
TypeToken<List<String>> stringListTok = new TypeToken<List<String>>() {};
或者你想故意指向一个通配符类型:
TypeToken<Map<?, ?>> wildMapTok = new TypeToken<Map<?, ?>>() {};
TypeToken提供了一种方法来动态的解决泛型类型参数,如下所示:
static <K, V> TypeToken<Map<K, V>> mapToken(TypeToken<K> keyToken, TypeToken<V> valueToken) {
return new TypeToken<Map<K, V>>() {}
.where(new TypeParameter<K>() {}, keyToken)
.where(new TypeParameter<V>() {}, valueToken);
}
public static void main(String[] args)throws Exception {
TypeToken<Map<String, BigInteger>> mapToken = mapToken(
TypeToken.of(String.class),
TypeToken.of(BigInteger.class)
);
}
注意如果mapToken只是返回了new TypeToken>(),它实际上不能把具体化的类型分配到K和V上面,举个例子
abstract class IKnowMyType<T> {
TypeToken<T> type = new TypeToken<T>(getClass()) {};
}
...
new IKnowMyType<String>() {}.type; // returns a correct TypeToken<String>
使用这种技术,你可以,例如,获得知道他们的元素类型的类。
查询
TypeToken支持很多种类能支持的查询,但是也会把通用的查询约束考虑在内。
支持的查询操作包括:
方法 | 描述 |
---|---|
getType() | 获得包装的java.lang.reflect.Type. |
getRawType() | 返回大家熟知的运行时类 |
getSubtype(Class<?>) | 返回那些有特定原始类的子类型。举个例子,如果这有一个Iterable并且参数是List.class,那么返回将是List。 |
getSupertype(Class<?>) | 产生这个类型的超类,这个超类是指定的原始类型。举个例子,如果这是一个Set并且参数是Iterable.class,结果将会是Iterable。 |
isAssignableFrom(type) | 如果这个类型是 assignable from 指定的类型,并且考虑泛型参数,返回true。List<? extends Number>是assignable from List,但List没有. |
getTypes() | 返回一个Set,包含了这个所有接口,子类和类是这个类型的类。返回的Set同样提供了classes()和interfaces()方法允许你只浏览超类和接口类。 |
isArray() | 检查某个类型是不是数组,甚至是<? extends A[]>。 |
getComponentType() | 返回组件类型数组。 |
resolveType
TypeToken<Function<Integer, String>> funToken = new TypeToken<Function<Integer, String>>() {};
TypeToken<?> funResultToken = funToken.resolveType(Function.class.getTypeParameters()[1]);
TypeToken将Java提供的TypeVariables和context token中的类型变量统一起来。这可以被用来一般性地推断出在一个类型相关方法的返回类型:
TypeToken<Map<String, Integer>> mapToken = new TypeToken<Map<String, Integer>>() {};
TypeToken<?> entrySetToken = mapToken.resolveType(Map.class.getMethod("entrySet").getGenericReturnType());
// returns a TypeToken<Set<Map.Entry<String, Integer>>>
Invokable
Guava的Invokable是对java.lang.reflect.Method和java.lang.reflect.Constructor的流式包装。它简化了常见的反射代码的使用。一些使用例子:
Dynamic Proxies
Foo foo = Reflection.newProxy(Foo.class, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return "method:"+method;
}
});
String abc = foo.abc();
System.out.println(abc);
AbstractInvocationHandler
有时候你可能想动态代理能够更直观的支持equals(),hashCode()和toString(),那就是:
- 一个代理实例equal另外一个代理实例,只要他们有同样的接口类型和equal的invocation handlers。
- 一个代理实例的toString()会被代理到invocation handler的toString(),这样更容易自定义。
除此之外,AbstractInvocationHandler确保传递给[handleInvocation(Object, Method, Object[])](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/reflect/AbstractInvocationHandler.html#handleInvocation(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]))的参数数组永远不会空,从而减少了空指针异常的机会。
ClassPath
严格来讲,Java没有平台无关的方式来浏览类和类资源。不过一定的包或者工程下,还是能够实现的,比方说,去检查某个特定的工程的惯例或者某种一直遵从的约束。
ClassPath是一种实用工具,它提供尽最大努力的类路径扫描。用法很简单:
ClassPath classpath = ClassPath.from(GuavaTest01.class.getClassLoader()); // scans the class path used by classloader
for (ClassPath.ClassInfo classInfo : classpath.getTopLevelClasses("com.mycomp.mypackage")) {
}
ClassPath是一个尽力而为的工具。它只扫描jar文件中或者某个文件目录下的class文件。也不能扫描非URLClassLoader的自定义class loader管理的class,所以不要将它用于关键任务生产任务。
Class Loading
工具方法Reflection.initialize(Class…)能够确保特定的类被初始化——执行任何静态初始化。
使用这种方法的是一个代码异味,因为静态伤害系统的可维护性和可测试性。在有些情况下,你别无选择,而与传统的框架,操作间,这一方法有助于保持代码不那么丑。