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

要说Java程序员必须要学习的东西的话,Spring当仁不让,必须是第一个框架,也有另外一个东西,也是新手程序员必须要学习的东西,那就是Google的Guava库,这个库的作者,和Effective Java的作者是同一人,都来自于Google,学好了这个库,你的Java的能力,相信会更上一个台阶。Java的优势是什么?那就是各种成熟的轮子,Guava就是其中之一,如果想代码很少BUG,就开始使用它吧。

目录
1、基本工具
2、集合

1、基本工具

1.1 避免NULL

NULL的含义是不确定的,应该避免,尤其是在Map和Set中,返回NULL,并不清楚是不存在,还是有值,但是是NULL,最常用的一个技巧就是:

// 好的代码
List arr = new ArrayList();
// 不好的代码
List arr = null;
Map<Integer,String> map = new HashMap<>();
//处理默认值
map.getOrDefault(123,"默认值");
//没有123这个KEY的时候,才执行put方法,如果map中已经有值了,就不执行put方法
//并且返回旧值
map.putIfAbsent(123,"123");

这样可以避免空指针。Guava的建议是: 使用快速失败操作拒绝null值对开发者更有帮助。

1.2 Guava用Optional表示可能为null的T类型引用:

Optional<Integer> possible = Optional.of(50);
possible.isPresent(); // true
possible.get(); // returns 50

2、集合类

2.1 不可变集合

这个属于防御性编程内容,不可变集合的意思就是集合创建了之后,就不能改变了,类似于:

// SIZE 的值是不可以改变的
final int SIZE = 10;
// final修饰传统的map,起不到效果
final Map<Integer,String> map = new HashMap<>();
// 不可以
map = new HashMap<>();
// 可以
map.put(123,"123")

想要设计不可变集合类的主要原因有:

  • 当对象被不可信的库调用时,不可变形式是安全的。
  • 线程安全。
  • 容易测试。
  • 安全,不容易出BUG,以防某些错误的代码修改了集合的内容。

创建的方法(还有1个copyOf方法,这里不打算引入了):

final ImmutableSet<Integer> set = ImmutableSet.<Integer>builder()
                .add(12)
                .add(30)
                .add(12)
                .build();
ImmutableSortedSet<Integer> sortedSet = ImmutableSortedSet.of(1, 2, 3, 4, 5);
//转List,取第K小个元素
sortedSet.asList().get(k)

2.2 新集合类型

有一些新的类型可以使用,例如java没有Table类型,下面将一一介绍。

2.2.1 Multiset

这个东西,可以想象成1个ArrayList,没有顺序,但是它是一个Set,不过可以有重复的Key,还记录了重复的Key的出现的数量。比方说你要统计一篇文章中出现的单词的次数:

Map<String, Integer> counts = new HashMap<String, Integer>();
for (String word : words) {
    Integer count = counts.get(word);
    if (count == null) {
        counts.put(word, 1);
    } else {
        counts.put(word, count + 1);
    }
}

这种写法很笨拙,也容易出错,并且不支持同时收集多种统计信息,如总词数。我们可以做的更好。替代方法:

//模拟文章内容
String [] arr = {"123","123","1234","12345"};
Multiset<String> multiset = HashMultiset.create();
for(String s : arr) {
    multiset.add(s);
}
//统计123出现了几次
System.out.println(multiset.count("123"));
//统计set中一共有几个元素
System.out.println(multiset.size());

Multiset接口的方法:

方法描述
count(E)给定元素在Multiset中的计数
elementSet()Multiset中不重复元素的集合,类型为Set
entrySet()和Map的entrySet类似,返回Set<Multiset.Entry>,其中包含的Entry支持getElement()和getCount()方法
add(E, int)增加给定元素在Multiset中的计数
remove(E, int)减少给定元素在Multiset中的计数
setCount(E, int)设置给定元素在Multiset中的计数,不可以为负数
size()返回集合元素的总个数(包括重复的元素)

该接口的实现类:

Map对应的****Multiset是否支持null元素
HashMapHashMultiset
TreeMapTreeMultiset是(如果comparator支持的话)
LinkedHashMapLinkedHashMultiset
ConcurrentHashMapConcurrentHashMultiset
ImmutableMapImmutableMultiset

2.2.2 Multimap

一般都遇到过以下场景,这种可以使用Multimap替代,需要注意Multimap不是Map。

//传统方法
Map<K, List<V>>
Map<K, Set<V>>
// 替代方法 这里用ArrayListMultimap来举例
ArrayListMultimap<String,String> arrayListMultimap = ArrayListMultimap.create();
arrayListMultimap.put("abc","1");
arrayListMultimap.put("abc","12");
arrayListMultimap.put("abc","123");
arrayListMultimap.put("abc","1234");
System.out.println(arrayListMultimap.get("abc"));
// [1, 12, 123, 1234]
//arrayListMultimap.get("abc") 返回的是1个List<String>的值

Multimap提供了多种形式的实现。在大多数要使用Map<K, Collection>的地方,你都可以使用它们:

实现键行为类似值行为类似
ArrayListMultimapHashMapArrayList
HashMultimapHashMapHashSet
LinkedListMultimapLinkedHashMapLinkedList
LinkedHashMultimapLinkedHashMapLinkedHashMap
TreeMultimapTreeMapTreeSet
ImmutableListMultimapImmutableMapImmutableList
ImmutableSetMultimapImmutableMapImmutableSet

2.2.3 BiMap

传统上,实现键值对的双向映射需要维护两个单独的map,并保持它们间的同步。但这种方式很容易出错,而且对于值已经在map中的情况,会变得非常混乱。例如:

Map<String, Integer> nameToId = Maps.newHashMap();
Map<Integer, String> idToName = Maps.newHashMap();
nameToId.put("Bob", 42);
idToName.put(42, "Bob");
//如果"Bob"和42已经在map中了,会发生什么?
//如果我们忘了同步两个map,会有诡异的bug发生...

替代方法:

BiMap<String, Integer> nameIdBiMap = HashBiMap.create();
nameIdBiMap.put("a",1);
nameIdBiMap.put("b",2);
nameIdBiMap.put("c",3);
//nameIdBiMap.put("a",4);
System.out.println("a的id:" + nameIdBiMap.get("a"));
System.out.println("id为1:" + nameIdBiMap.inverse().get(1));

如果不是唯一的,会出现什么情况呢? 例如2个人重名了。如果我们放开了上面的注释语句,模拟下这种冲突,就会出现ID为1的人被覆盖掉了,这点在使用的时候,需要特别注意。

BiMap的各种实现

键值实现值键实现对应的BiMap实现
HashMapHashMapHashBiMap
ImmutableMapImmutableMapImmutableBiMap
EnumMapEnumMapEnumBiMap
EnumMapHashMapEnumHashBiMap

2.3.4 Table

Table有点类似于2维数组,根据行和列,确认某一个值,没有Table,老方法就是搞个Map<V,Map<K,V>>这样的结构来实现。替代方法:

Table<Integer, Integer, Integer> table = HashBasedTable.create();
table.put(1, 1 ,3);
table.put(1, 2, 4);
table.put(2, 1, 5);
table.put(2, 2, 6);

System.out.println(table.row(1)); //{1=3, 2=4}
System.out.println(table.column(1)); //{1=3, 2=5}
System.out.println(table.get(1, 1)); //3

是不是感觉很方便?

2.3.5 RangeSet

这个东西很有意思,在某些场景,我感觉可能会有用,RangeSet的意思是一些非连续区间,例如:
{[1,10], [11,15)} {[1,10], [11,20)} 在数学上,它是不连续的,也可能是连续的。这个时候,想要描述这段区间的时候,可以使用RangeSet这个类。
注:RangeSet不支持GWT,也不支持JDK5和更早版本;因为,RangeSet需要充分利用JDK6中NavigableMap的特性。

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)}
System.out.println(rangeSet.contains(1)); //true
System.out.println(rangeSet.contains(6)); //false

1、RangeSet的实现支持非常广泛的视图:

  • complement():返回RangeSet的补集视图。complement也是RangeSet类型,包含了不相连的、非空的区间。
  • subRangeSet(Range):返回RangeSet与给定Range的交集视图。这扩展了传统排序集合中的headSet、subSet和tailSet操作。
  • asRanges():用Set<Range>表现RangeSet,这样可以遍历其中的Range。
  • asSet(DiscreteDomain)(仅ImmutableRangeSet支持):用ImmutableSortedSet表现RangeSet,以区间中所有元素的形式而不是区间本身的形式查看。(这个操作不支持DiscreteDomain 和RangeSet都没有上边界,或都没有下边界的情况)
    2、RangeSet的查询方法 为了方便操作,RangeSet直接提供了若干查询方法,其中最突出的有:
  • contains©:RangeSet最基本的操作,判断RangeSet中是否有任何区间包含给定元素。
  • rangeContaining©:返回包含给定元素的区间;若没有这样的区间,则返回null。
  • encloses(Range):简单明了,判断RangeSet中是否有任何区间包括给定区间。
  • span():返回包括RangeSet中所有区间的最小区间。

2.3.6 其它

还有一些其它的类,就不多写了,可以自己去看看API文档

  • ClassToInstanceMap
  • RangeMap 这个和RangeSet有点像,相当于某个区间对应了某个值,例如:
RangeMap<Integer, String> rangeMap = TreeRangeMap.create();
rangeMap.put(Range.closed(1, 10), "foo"); //{[1,10] => "foo"}

具体用到了的时候,可以再研究一下。
全文完。 JAVA程序员必学的Google Guava库(第二篇)
参考文档:
并发编程网 http://ifeve.com/google-guava/

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值