JAVA程序员必学的Google Guava库(第二篇)

这篇文章是第二篇,如果没有看过第一篇的,建议先去看看JAVA程序员必学的Google Guava库(第一篇),不过直接看第2篇也没关系,不影响。Google Guava的内容着实有点多,先把目录列在这里,慢慢写,就总结一下日常用的多的东西,一些用的少的或者特别细节的内容,就不写了。

目录
3、缓存
4、字符串处理

3、缓存

如果要实现一个内存类型的缓存,大部分人可能会尝试使用Map,这样写也可以,大部分情况都适用,担心多线程问题,可以使用ConcurrentHashMap,但是还有一些问题例如内存无限膨胀、过期等问题,没有解决,这个时候,可以考虑使用Guava的缓存。

通常来说,Guava Cache适用于:

  • 你愿意消耗一些内存空间来提升速度。
  • KEY会被查询一次以上。
  • 缓存中存放的数据总量不会超出内存容量。(Guava Cache是单个应用运行时的本地缓存。它不把数据存放到文件或外部服务器。如果这不符合你的需求,请尝试Memcached这类工具) 如果你的场景符合上述的每一条,Guava Cache就适合你。直接来代码吧:
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
        .maximumSize(1000)
        .build(
            new CacheLoader<Key, Graph>() {
                public Graph load(Key key) throws AnyException {
                    return createExpensiveGraph(key);
                }
            });
...
try {
    return graphs.get(key);
} catch (ExecutionException e) {
    throw new OtherException(e.getCause());
}

这里直接把缓存没命中,回溯DB的方法,放在了build()里面,也可以使用另外一种方法,调用get(key,Callable(){})

Cache<Key, Graph> cache = CacheBuilder.newBuilder()
        .maximumSize(1000)
        .build(); // look Ma, no CacheLoader
...
try {
    // If the key wasn't in the "easy to compute" group, we need to
    // do things the hard way.
    cache.get(key, new Callable<Key, Graph>() {
        @Override
        public Value call() throws AnyException {
            return doThingsTheHardWay(key);
        }
    });
} catch (ExecutionException e) {
    throw new OtherException(e.getCause());
}

获取缓存-如果没有-则计算"[get-if-absent-compute]这个操作是线程安全的。也可以手工添加缓存,调用put(K,V)方法就可以了。
接下来聊一下缓存的失效问题,内存不可能无限大,总是有限制的,Guava提供了以下办法:

  • 基于容量回收 注意刚才的 .maximumSize(1000) ,表示最大1000个,超过了就回收。
  • 定时回收
    expireAfterAccess(long, TimeUnit):缓存项在给定时间内没有被读/写访问,则回收。请注意这种缓存的回收顺序和基于大小回收一样。
    expireAfterWrite(long, TimeUnit):缓存项在给定时间内没有被写访问(创建或覆盖),则回收。如果认为缓存数据总是在固定时候后变得陈旧不可用,这种回收方式是可取的。
  • 基于引用回收
  • 手工回收 调用 Cache.invalidate(key) Cache.invalidateAll() 来清楚个别缓存或者全部缓存
清理什么时候发生?

写操作的时候顺带清理下,必要的时候清理下,有点类似Redis清理过期KEY的机制,当然,也可以自己手工起个线程去清理。

怎么修改缓存值?

Cache.refersh(key)可以刷新,当然也可以是异步的。如果刷新期间出现异常,那么旧值也会保留。

asMap视图

asMap视图提供了缓存的ConcurrentMap形式,但asMap视图与缓存的交互需要注意:

  • cache.asMap()包含当前所有加载到缓存的项。因此相应地,cache.asMap().keySet()包含当前所有已加载键;
  • asMap().get(key)实质上等同于cache.getIfPresent(key),而且不会引起缓存项的加载。这和Map的语义约定一致。
  • 所有读写操作都会重置相关缓存项的访问时间,包括Cache.asMap().get(Object)方法和Cache.asMap().put(K, V)方法,但不包括Cache.asMap().containsKey(Object)方法,也不包括在Cache.asMap()的集合视图上的操作。比如,遍历Cache.asMap().entrySet()不会重置缓存项的读取时间。
线程中断

A线程取缓存 - 没有命中 - 加载 - 返回 这个过程在单线程的时候没什么问题,如果多个线程同时访问,只有其中1个线程去回溯缓存,假设是A,如果A失败了,那么其他的也就失败了,这应该是个BUG,可以用AsyncLoadingCache来替代LoadingCache

4、字符串处理

你可能会想到apache的StringUtils,大多数情况下会满足要求,这里来看看Guava提供了哪些功能。

4.1 Joiner 把字符串连接起来
Joiner joiner = Joiner.on("; ").skipNulls();
return joiner.join("Harry", null, "Ron", "Hermione");
//Harry; Ron; Hermione 自动处理了NULL值
Joiner.on(",").join(Arrays.asList(1, 5, 7)); // returns "1,5,7"

警告:joiner实例总是不可变的。用来定义joiner目标语义的配置方法总会返回一个新的joiner实例。这使得joiner实例都是线程安全的,你可以将其定义为static final常量。

4.2 Splitter 分割字符串
Iterable<String> iterable = Splitter.on(',')
        .trimResults()
        .omitEmptyStrings()
        .split(",a,,b,");
Iterator it = iterable.iterator();
while (it.hasNext()) {
    System.out.println(it.next());
}
//返回a b 自动去掉了空格

考虑清楚要不要用这个特性,大部分情况下,是不需要空格的数据的

4.3 字符匹配器[CharMatcher]

在以前的Guava版本中,StringUtil类疯狂地膨胀,其拥有很多处理字符串的方法:allAscii、collapse、collapseControlChars、collapseWhitespace、indexOfChars、lastIndexNotOf、numSharedChars、removeChars、removeCrLf、replaceChars、retainAllChars、strip、stripAndCollapse、stripNonDigits。 所有这些方法指向两个概念上的问题:

  1. 怎么才算匹配字符?
  2. 如何处理这些匹配字符?
    为了收拾这个泥潭,我们开发了CharMatcher。
    直观上,你可以认为一个CharMatcher实例代表着某一类字符,如数字或空白字符。事实上来说,CharMatcher实例就是对字符的布尔判断——CharMatcher确实也实现了Predicate——但类似”所有空白字符”或”所有小写字母”的需求太普遍了,Guava因此创建了这一API。
    然而使用CharMatcher的好处更在于它提供了一系列方法,让你对字符作特定类型的操作:修剪[trim]、折叠[collapse]、移除[remove]、保留[retain]等等。CharMatcher实例首先代表概念1:怎么才算匹配字符?然后它还提供了很多操作概念2:如何处理这些匹配字符?这样的设计使得API复杂度的线性增加可以带来灵活性和功能两方面的增长。
String noControl = CharMatcher.JAVA_ISO_CONTROL.removeFrom(string); //移除control字符
String theDigits = CharMatcher.DIGIT.retainFrom(string); //只保留数字字符
String spaced = CharMatcher.WHITESPACE.trimAndCollapseFrom(string, ' ');
//去除两端的空格,并把中间的连续空格替换成单个空格
String noDigits = CharMatcher.JAVA_DIGIT.replaceFrom(string, "*"); //用*号替换所有数字
String lowerAndDigit = CharMatcher.JAVA_DIGIT.or(CharMatcher.JAVA_LOWER_CASE).retainFrom(string);
// 只保留数字和小写字母

获取字符匹配器 CharMatcher中的常量可以满足大多数字符匹配需求:

ANYNONEWHITESPACEBREAKING_WHITESPACE
INVISIBLEDIGITJAVA_LETTERJAVA_DIGIT
JAVA_LETTER_OR_DIGITJAVA_ISO_CONTROLJAVA_LOWER_CASEJAVA_UPPER_CASE
ASCIISINGLE_WIDTH

其他获取字符匹配器的常见方法包括:

方法描述
anyOf(CharSequence)枚举匹配字符。如CharMatcher.anyOf(“aeiou”)匹配小写英语元音
is(char)给定单一字符匹配。
inRange(char, char)给定字符范围匹配,如CharMatcher.inRange(‘a’, ‘z’)

此外,CharMatcher还有negate()and(CharMatcher)or(CharMatcher)方法。
使用字符匹配器
CharMatcher提供了多种多样的方法操作CharSequence中的特定字符。其中最常用的罗列如下:

方法描述
collapseFrom(CharSequence, char)把每组连续的匹配字符替换为特定字符。如WHITESPACE.collapseFrom(string, ‘ ‘)把字符串中的连续空白字符替换为单个空格。
matchesAllOf(CharSequence)测试是否字符序列中的所有字符都匹配。
removeFrom(CharSequence)从字符序列中移除所有匹配字符。
retainFrom(CharSequence)在字符序列中保留匹配字符,移除其他字符。
trimFrom(CharSequence)移除字符序列的前导匹配字符和尾部匹配字符。
replaceFrom(CharSequence, CharSequence)用特定字符序列替代匹配字符。

所有这些方法返回String,除了matchesAllOf返回的是boolean。

4.4 字符集[Charsets]
// 不要这么做
try {
    bytes = string.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
    // how can this possibly happen?
    throw new AssertionError(e);
}
// 要这么做
bytes = string.getBytes(Charsets.UTF_8);
4.5 大小写格式[CaseFormat]

CaseFormat被用来方便地在各种ASCII大小写规范间转换字符串——比如,编程语言的命名规范。CaseFormat支持的格式如下:

格式范例
LOWER_CAMELlowerCamel
LOWER_HYPHENlower-hyphen
LOWER_UNDERSCORElower_underscore
UPPER_CAMELUpperCamel
UPPER_UNDERSCOREUPPER_UNDERSCORE

CaseFormat的用法很直接:

CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, "CONSTANT_NAME")); // returns "constantName"

全文完。
转发自 并发编程网 http://ifeve.com/google-guava/ 略有修改
备注:Google Guava还提供了更多的功能,有兴趣可以更加深入学习,有一些平时少用的,就没有放在这里了。

关注我的博客,获取更多Java编程知识: 双King的技术博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值