├── .gitignore ├── README.md ├── doc ├── basic-utilities-defaults.md ├── basic-utilities-object-methods.md ├── basic-utilities-ordering.md ├── basic-utilities-preconditions.md ├── basic-utilities-throwables.md ├── basic-utilities-using-avoiding-null.md ├── caches.md ├── collections-extension-utilities.md ├── collections-immutable-collections.md ├── collections-new-collection-types.md ├── collections-utility-classes.md ├── concurrency-listenablefuture.md ├── concurrency-service.md ├── eventbus.md ├── functional-idioms.md ├── hash.md ├── io.md ├── math.md ├── networking.md ├── primitives.md ├── ranges.md ├── reflection.md └── strings.md ├── pom.xml └── src └── main └── java └── com └── gtt ├── basicutilities ├── DefaultsTest.java ├── ObjectsTest.java ├── OptionalTest.java ├── OrderingTest.java ├── PreconditionsTest.java └── ThrowablesTest.java └── collections ├── CollectionUtilitiesTest.java └── ImmutableCollectionsTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | /.settings 2 | /target 3 | /.classpath 4 | /.project 5 | .idea/ 6 | *.iml 7 | out/ 8 | .DS_Store 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | guava学习记录项目 2 | 3 | Guava 是一个 Google 的基于java1.6类库的扩展项目,包括 collections, caching, primitives support, concurrency libraries, common annotations, string processing, I/O, 等等. 这些高质量的API可以使你的JAVA代码更加优雅,更加简洁,让你工作更加轻松愉悦。 4 | 5 | 目录 6 | === 7 | 8 | * 基本工具 9 | + [使用和避免null](doc/basic-utilities-using-avoiding-null.md) 10 | + [参数检查](doc/basic-utilities-preconditions.md) 11 | + [比较器](doc/basic-utilities-ordering.md) 12 | + [默认值](doc/basic-utilities-defaults.md) 13 | + [常用的对象方法](doc/basic-utilities-object-methods.md) 14 | + [Throwable类](doc/basic-utilities-throwables.md) 15 | 16 | * 集合扩展 17 | + [不可变集合](doc/collections-immutable-collections.md) 18 | + [新集合类型](doc/collections-new-collection-types.md) 19 | + [强大的集合工具类](doc/collections-utility-classes.md) 20 | + [扩展工具类](doc/collections-extension-utilities.md) 21 | * [缓存工具](doc/caches.md) 22 | * [函数式](doc/functional-idioms.md) 23 | * 并发工具 24 | + [可监听的Future](doc/concurrency-listenablefuture.md) 25 | + [Service框架](doc/concurrency-service.md) 26 | * [字符串工具](doc/strings.md) 27 | * [网络工具](doc/networking.md) 28 | * [原生类型](doc/primitives.md) 29 | * [范围处理](doc/ranges.md) 30 | * [I/O工具](doc/io.md) 31 | * [哈希工具](doc/hash.md) 32 | * [事件总线](doc/eventbus.md) 33 | * [运算工具](doc/math.md) 34 | * [反射工具](doc/reflection.md) 35 | 36 | 37 | 注: 本学习记录参考了以下网络资源: 38 | [@peida](http://www.cnblogs.com/peida/p/Guava.html) 39 | [@并发编程网](http://ifeve.com/category/guava-2/) -------------------------------------------------------------------------------- /doc/basic-utilities-defaults.md: -------------------------------------------------------------------------------- 1 | 默认值(Defaults) 2 | === 3 | Defaults类提供Java各原生类型的默认值 4 | 5 | com.google.common.base.Defaults.defaultValue(Class type) 6 | 7 | ```java 8 | boolean.class //返回false 9 | char.class //返回'\0' 10 | byte.class //返回(byte)0 11 | short.class //返回(short)0 12 | int.class //返回0 13 | long.class //返回0L 14 | float.class //返回0f 15 | double.class //返回0d 16 | non-primitive types //返回null 17 | ``` 18 | 19 | ------ 20 | [返回目录](/README.md) 21 | -------------------------------------------------------------------------------- /doc/basic-utilities-object-methods.md: -------------------------------------------------------------------------------- 1 | 常用的对象方法(Objects) 2 | === 3 | Objects提供了Java对象的equals、hashCode、toString等方法 4 | 5 | #### equals 6 | 覆写equals方法时, 减少了null判断和分支处理 7 | 8 | ```java 9 | Objects.equal("a", "a"); // returns true 10 | Objects.equal(null, "a"); // returns false 11 | Objects.equal("a", null); // returns false 12 | Objects.equal(null, null); // returns true 13 | ``` 14 | 15 | #### hashCode 16 | 更方便地完成多个属性的hash 17 | 18 | ```java 19 | Objects.hashCode(Object...) 20 | Objects.hashCode(field1, field2, ..., fieldn) 21 | ``` 22 | 23 | #### toString 24 | 对象的toString方法更多是为了更好的可读性, ToStringHelper可以通过链式更方便地将对象的各属性都加入 25 | 26 | ```java 27 | Objects.toStringHelper(Persion.class) 28 | .add("name", this.name) 29 | .add("age", this.age) 30 | .toString(); 31 | ``` 32 | 33 | > Intellij Idea中可以安装*Guava equals, hashCode and toString generator*插件来快速生成这三个方法 34 | 35 | 36 | #### compare/compareTo 37 | compareTo是java.lang.Comparable接口中的方法 38 | 39 | guava提供了所有原始类型的对比工具 40 | ```java 41 | Ints.compare(int a, int b) 42 | Longs.compare(long a, long b) 43 | Shorts.compare(short a, short b) 44 | Doubles.compare(double a, double b) 45 | Floats.compare(float a, floab b) 46 | Booleans.compare(boolean a, boolean b) 47 | Chars.compare(char a, char b) 48 | ``` 49 | 50 | 同时,guava还提供了链式对比的工具ComparisonChain 51 | ```java 52 | ComparisonChain.start() 53 | .compare(this.aString, that.aString) 54 | .compare(this.anInt, that.anInt) 55 | .compare(this.anEnum, that.anEnum, Ordering.natural().nullsLast()) 56 | .result(); 57 | ``` 58 | ComparisonChain是一个lazy的比较过程, 当比较结果为0的时候, 即相等的时候, 会继续比较下去, 出现非0的情况, 就会忽略后面的比较 59 | 60 | ------ 61 | [返回目录](/README.md) 62 | -------------------------------------------------------------------------------- /doc/basic-utilities-ordering.md: -------------------------------------------------------------------------------- 1 | 犀利的比较器(Ordering) 2 | === 3 | Ordering是Guava类库提供的一个犀利强大的比较器工具,Guava的Ordering和JDK Comparator相比功能更强。它非常容易扩展,可以轻松构造复杂的comparator,然后用在容器的比较、排序等操作中。 4 | 5 | #### 常用静态方法 6 | 7 | ```java 8 | 9 | Ordering.natural(); // 使用Comparable类型的自然顺序, 例如:整数从小到大,字符串是按字典顺序; 10 | Ordering.usingToString(); // 使用toString()返回的字符串按字典顺序进行排序; 11 | Ordering.from(Comparator); // 将Comparator转换为Ordering 12 | new Ordering(){ // 或者直接构建一个Ordering对象,并实现compare方法 13 | public int compare(T left, T right){} 14 | } 15 | ``` 16 | 17 | 18 | #### 实例方法(支持链式) 19 | com.google.common.collect.Ordering 20 | 21 | ```java 22 | reverse(); //返回与当前Ordering相反的排序 23 | nullsFirst(); //返回一个将null放在non-null元素之前的Ordering,其他的和原始的Ordering一样 24 | nullsLast(); //返回一个将null放在non-null元素之后的Ordering,其他的和原始的Ordering一样 25 | compound(Comparator); //返回一个使用Comparator的Ordering,Comparator作为第二排序元素 26 | lexicographical(); //返回一个按照字典元素迭代的Ordering 27 | onResultOf(Function); //将function应用在各个元素上之后, 在使用原始ordering进行排序 28 | greatestOf(Iterable iterable, int k); //返回指定的前k个可迭代的最大的元素,按照当前Ordering从最大到最小的顺序 29 | leastOf(Iterable iterable, int k); //返回指定的前k个可迭代的最小的元素,按照当前Ordering从最小到最大的顺序 30 | isOrdered(Iterable); //是否有序(前面的元素可以大于或等于后面的元素),Iterable不能少于2个元素 31 | isStrictlyOrdered(Iterable); //是否严格有序(前面的元素必须大于后面的元素),Iterable不能少于两个元素 32 | sortedCopy(Iterable); //返回指定的元素作为一个列表的排序副本 33 | 34 | ``` 35 | 36 | ------ 37 | [返回目录](/README.md) 38 | -------------------------------------------------------------------------------- /doc/basic-utilities-preconditions.md: -------------------------------------------------------------------------------- 1 | 优雅的参数检查(Preconditions) 2 | === 3 | #### 问题 4 | 对外接口方法参数过多时,需对参数进行必要的检查,将预期之外的请求快速驳回。 5 | 6 | ```java 7 | public boolean someMethod(int arg1, String arg2, String arg3, Object arg4){ 8 | if( arg1>0 && isNotEmpty(arg2) && isNotEmpty(arg3) && arg4!=null){ 9 | return false; // maybe需要将具体的错误类型告诉调用方 10 | } 11 | ... 12 | } 13 | 14 | ``` 15 | 16 | #### 方案 17 | com.google.common.base.Preconditions 18 | 19 | ```java 20 | // 检查boolean是否为真 21 | // 失败时抛出 IllegalArgumentException 22 | Preconditions.checkArgument(boolean expression, String errMsg, Object... errMsgArgs) 23 | 24 | // 检查value是否为null 25 | // 失败时抛出 NullPointerException 26 | Preconditions.checkNotNull(T reference, String errMsg, Object... errMsgArgs) 27 | 28 | // 检查对象的一些状态, 不依赖方法参数(相比checkArgument, 在某些情况下更有语义...) 29 | // 失败时抛出 IllegalStateException 30 | Preconditions.checkState(boolean expression, String errMsg, Object... errMsgArgs) 31 | 32 | // 检查index是否在合法范围[0, size)(不包含size) 33 | // 失败时抛出 IndexOutOfBoundsException 34 | Preconditions.checkElementIndex(int index, int size, String desc) 35 | 36 | // 检查位置是否在合法范围[0, size](包含size) 37 | // 失败时抛出 IndexOutOfBoundsException 38 | Preconditions.checkPositionIndex(int index, int size, String desc) 39 | 40 | // 检查[start, end)是一个长度为size的集合合法的子集范围 41 | // 失败时抛出 IndexOutOfBoundsException 42 | Preconditions.checkPositionIndexs(int start, int index, int size) 43 | 44 | ``` 45 | 46 | 建议通过静态方式引入com.google.common.base.Preconditions.* 47 | 48 | ------ 49 | [返回目录](/README.md) 50 | -------------------------------------------------------------------------------- /doc/basic-utilities-throwables.md: -------------------------------------------------------------------------------- 1 | 简化异常处理(Throwables) 2 | === 3 | Guava提供了一个异常处理工具类, 可以简单地捕获和重新抛出多个异常 4 | 5 | #### 常用方法 6 | 7 | ```java 8 | // 把throwable包装成RuntimeException,用该方法保证异常传递,抛出一个RuntimeException异常 9 | RuntimeException propagate(Throwable); 10 | 11 | // 当且仅当它是一个X的实例时,传递throwable 12 | void propagateIfInstanceOf(Throwable, Class) throws X; 13 | 14 | // 当且仅当它是一个RuntimeException和Error时,传递throwable 15 | void propagateIfPossible(Throwable); 16 | 17 | // 当且仅当它是一个RuntimeException和Error时,或者是一个X的实例时,传递throwable 18 | void propagateIfPossible(Throwable, Class) throws X; 19 | ``` 20 | 21 | #### 异常链处理 22 | ```java 23 | Throwable getRootCause(Throwable) 24 | List getCausalChain(Throwable) 25 | String getStackTraceAsString(Throwable) 26 | ``` 27 | 28 | ------ 29 | [返回目录](/README.md) 30 | -------------------------------------------------------------------------------- /doc/basic-utilities-using-avoiding-null.md: -------------------------------------------------------------------------------- 1 | 使用和避免null 2 | === 3 | 4 | 5 | null本身不是对象,也不是Objcet的实例 6 | 7 | #### 问题: 8 | null代表不确定的对象, 是一个很模糊的概念, 容易产生二义性 9 | 10 | Map.get(key)若返回value值为null,其代表的含义可能是该键指向的value值是null,亦或者该键在map中并不存在 11 | 12 | #### 优点: 13 | 从内存消耗和效率方面,null更加廉价 14 | 15 | #### 优化: Optional 16 | com.google.common.base.Optional 17 | 18 | ```java 19 | Optional possbile = Optional.formNullable(T); //将一个T的实例转换为Optional对象(T可以为空) 20 | boolean present = possible.isPresent(); //若Optional包含的T实例不为null,则返回true;若T实例为null,返回false 21 | T t = possible.get(); //返回Optional包含的T实例,该T实例必须不为空;否则,抛出一个IllegalStateException异常 22 | ``` 23 | 24 | 构建一个Optional对象 25 | 26 | ```java 27 | Optional.of(T); //将一个T的实例转换为Optional对象(T不可以为空) 28 | Optional.absent(); //获得一个Optional对象,其内部包含了空值 29 | Optional.fromNullable(T); //将一个T的实例转换为Optional对象,T的实例可以不为空,也可以为空 30 | //Optional.fromNullable(null) 等同于 Optional.absent() 31 | ``` 32 | 33 | Optional实例方法 34 | 35 | ```java 36 | boolean isPresent(); //若Optional包含的T实例不为null, 返回true; 否则, 返回false 37 | T get(); //若Optional包含的T实例不为null, 返回T; 否则, 抛出IllegalStateException 38 | T or(T); //若Optional包含的T实例不为null, 返回T; 否则, 返回参数输入的T实例 39 | T orNull(); //若Optional包含的T实例不为null, 返回T; 否则, 返回null 40 | ``` 41 | 42 | ------ 43 | [返回目录](/README.md) -------------------------------------------------------------------------------- /doc/caches.md: -------------------------------------------------------------------------------- 1 | 缓存 2 | === 3 | 4 | ### 什么是缓存 5 | 缓存你懂的,memcached用过没?ehcache用过没?内存Map总该用过吧... 6 | 7 | 当计算或检索一个值的代价很高,并且对同样的输入需要不止一次获取值的时候,就应当考虑使用缓存。这下懂了吧 8 | 9 | 换句话说,缓存就是以空间换时间 10 | 11 | ### 问题 12 | 内存Map会一直保存所有添加的元素, 直到显示地移除, 所以会一直占用内存 13 | 而Guava Cache为了限制内存使用,通常都设定为自动回收元素。 14 | 15 | 由于存放于内存中,Guava Cache不适合存放过大的数据,数据量较大时,可以尝试使用 Memcached 等 16 | 17 | ### 主要流程 18 | get-if-absent-compute 19 | 如果有缓存则返回;否则运算、缓存、然后返回 20 | 21 | ### 缓存加载 22 | 当缓存不存在时,guava提供了多种方式来加载数据: CacheLoader、Callable、显示插入. 23 | 24 | ###### CacheLoader 25 | 26 | LoadingCache是一种基于CacheLoader的缓存实现. 27 | 28 | ```java 29 | LoadingCache graphs = CacheBuilder.newBuilder() 30 | .maximumSize(1000) 31 | .expireAfterWrite(10, TimeUnit.MINUTES) 32 | .build( 33 | new CacheLoader() { 34 | public Graph load(Key key) throws AnyException { 35 | return createExpensiveGraph(key); 36 | } 37 | }); 38 | 39 | ... 40 | try { 41 | return graphs.get(key); 42 | } catch (ExecutionException e) { 43 | throw new OtherException(e.getCause()); 44 | } 45 | ``` 46 | 使用LoadingCache.get(K)方法可以获取缓存中对应的值,如果没有缓存,则会使用CacheLoader原子地加载新值. 47 | 48 | ###### Callable 49 | 50 | 所有类型的Guava Cache, 不管有没有自动加载功能, 都支持get(K, Callable)方法。 51 | get(K, Callable)方法尝试返回缓存中对应的值; 如果值不存在,则使用Callable运算,并把结果加入缓存中。 52 | 53 | ```java 54 | Cache cache = CacheBuilder.newBuilder() 55 | .maximumSize(1000) 56 | .build(); // 看,木有CacheLoader 57 | ... 58 | try { 59 | cache.get(key, new Callable() { 60 | @Override 61 | public Value call() throws AnyException { 62 | // 缓存不存在,就会调用call()方法计算, 并把结果加入缓存 63 | return doThingsTheHardWay(key); 64 | } 65 | }); 66 | } catch (ExecutionException e) { 67 | throw new OtherException(e.getCause()); 68 | } 69 | ``` 70 | 这种方式简便地实现了"get-if-absent-compute"模式 71 | 72 | ###### 显式插入 73 | 74 | 使用cache.put(key, value)方法可以直接向缓存中插入值, 该方法会直接覆盖掉给定键之前映射的值. 75 | 76 | 77 | ### 缓存回收 78 | 由于guava缓存是将数据存放于内存中,所以确定一定以及肯定没有足够的内存存放所有的数据 79 | guava提供了三种基本的缓存回收方式: 基于容量回收、定时回收和基于引用回收。 80 | 81 | ###### 基于容量回收(Size-based Eviction) 82 | 83 | 构建Cache时,可以通过CacheBuilder.maximumSize(long)来指定缓存的容量. 84 | 在缓存容量达到指定容量时(maybe达到之前), 会尝试回收最近没有使用或总体上很少使用的缓存项. 85 | 86 | 另外,可以通过CacheBuilder.weight(Weigher), 来指定权重函数, 权重函数将在缓存创建时计算 87 | 88 | ```java 89 | LoadingCache graphs = CacheBuilder.newBuilder() 90 | .maximumWeight(100000) 91 | .weigher(new Weigher() { 92 | public int weigh(Key k, Graph g) { 93 | return g.vertices().size(); 94 | } 95 | }) 96 | .build( 97 | new CacheLoader() { 98 | public Graph load(Key key) { // no checked exception 99 | return createExpensiveGraph(key); 100 | } 101 | }); 102 | ``` 103 | 104 | ###### 定时回收(Timed Eviction) 105 | 106 | CacheBuilder提供两种定时回收的方式: 107 | 108 | * expireAfterAccess(long, TimeUnit): 缓存在给定时间内没有被读/写访问过, 则回收. 回收顺序与基于容量回收的一样 109 | * expireAfterWrite(long, TimeUnit): 缓存在给定时间内没有被写访问(创建/覆盖), 则回收. 110 | 111 | ###### 基于引用回收(Reference-based Eviction) 112 | 113 | 如果使用week references的键/值、soft references的值,则缓存允许被垃圾回收: 114 | 115 | * CacheBuilder.weakKeys() 116 | * CacheBuilder.weakValues() 117 | * CacheBuilder.softValues() 118 | 119 | ###### 显式移除 120 | 121 | 可以通过以下接口,在任何时间清除缓存 122 | 123 | * Cache.invalidate(key): 单个清除 124 | * Cache.invalidateAll(keys): 批量清除 125 | * Cache.invalidateAll(): 清除所有缓存项 126 | 127 | ###### 移除监听器 128 | 129 | CacheBuilder.removalListener(RemovalListener) 130 | 添加一个监听器,在缓存项被移除时,进行额外操作. 131 | 132 | ```java 133 | RemovalListener removalListener = new RemovalListener() { 134 | 135 | // 缓存项被移除时,RemovalListener会获取移除通知[RemovalNotification] 136 | // 其中包含移除原因[RemovalCause]、键和值 137 | public void onRemoval(RemovalNotification removal) { 138 | removal.getKey(); // 被移除的Key 139 | removal.getValue(); // 被移除的Value 140 | removal.getCause(); // 被移除的原因: EXPLICIT、REPLACED、COLLECTED、EXPIRED、SIZE 141 | } 142 | }; 143 | 144 | return CacheBuilder.newBuilder() 145 | .expireAfterWrite(2, TimeUnit.MINUTES) 146 | .removalListener(removalListener) 147 | .build(loader); 148 | ``` 149 | 150 | 用RemovalListeners.asynchronous(RemovalListener, Executor)把监听器装饰为异步操作 151 | 152 | ###### 缓存清理的时间点 153 | 154 | 使用CacheBuilder构建的缓存,不会“自动”执行清理和回收工作. 155 | guava并没有建立独立线程来完成清理工作, 而是在写操作时顺带做少量的维护工作. 156 | 使用者可以建立自己的独立线程, 来主动清理缓存, 只需要调用Cache.cleanUp()就可以了 157 | 158 | ###### 刷新 159 | 160 | LoadingCache.referesh(K) 刷新表示为键加载新值, 可以异步完成 161 | 刷新和回收不一样,刷新时,缓存仍然可以向其他线程返回旧值,而回收时,读取线程必须等待新值加载完成. 162 | 如果刷新失败(抛出异常),缓存将保留旧值 163 | 164 | CacheLoader.reload(K, V)可以扩展刷新时的行为 165 | CacheBuilder.refreshAfterWrite(long, TimeUnit)可以为缓存增加自动定时刷新功能 166 | 167 | ### 其他特性 168 | 169 | ###### 统计 170 | CacheBuilder.recordStats() 开启Guava Cache的统计功能。 171 | Cache.stats() 返回CacheStats对象 172 | 173 | CacheStatus提供如下统计信息: 174 | CacheStats.hitRate() 缓存命中率 175 | CacheStats.hitCount() 缓存命中数量 176 | CacheStats.averageLoadPenalty() 加载新值的平均时间,单位为纳秒 177 | CacheStats.evictionCount() 缓存项被回收的总数,不包括显式清除 178 | ... 179 | 180 | ###### Map视图 181 | 182 | cache.asMap() 提供了缓存的ConcurrentMap形式 183 | 184 | * asMap()包含当前所有加载到缓存的项 185 | * asMap().get(key)实质上等同于cache.getIfPresent(key),而且不会引起缓存项的加载 186 | * Cache.asMap().get(Object)方法和Cache.asMap().put(K, V)方法会重置相关缓存项的访问时间 187 | 188 | 189 | ------ 190 | [返回目录](/README.md) 191 | -------------------------------------------------------------------------------- /doc/collections-extension-utilities.md: -------------------------------------------------------------------------------- 1 | 集合扩展工具类 2 | === 3 | 有时候你需要实现自己的集合扩展。也许你想要在元素被添加到列表时增加特定的行为,或者你想实现一个Iterable,其底层实际上是遍历数据库查询的结果集。Guava提供了若干工具方法,以便让类似的工作变得更简单。 4 | 5 | 6 | #### Forwarding Decorators 7 | 针对所有类型的集合接口,Guava都提供了Forwarding抽象类以简化*装饰者模式*的使用。 8 | Forwarding抽象类定义了一个抽象方法:delegate(),你可以覆盖这个方法来返回被装饰对象。所有其他方法都会直接委托给delegate()。 9 | 通过创建ForwardingXXX的子类并实现delegate()方法,可以选择性地覆盖子类的方法来增加装饰功能,而不需要自己委托每个方法。 10 | 此外,很多集合方法都对应一个”标准方法[standardxxx]“实现,可以用来恢复被装饰对象的默认行为,比如standardAdd 11 | 12 | 示例: 13 | ```java 14 | class AddLoggingList extends ForwardingList { 15 | final List delegate; // backing list 16 | @Override 17 | protected List delegate() { 18 | return delegate; 19 | } 20 | @Override 21 | public void add(int index, E elem) { 22 | log(index, elem); 23 | super.add(index, elem); 24 | } 25 | @Override 26 | public boolean add(E elem) { 27 | return standardAdd(elem); // implements in terms of add(int, E) 28 | } 29 | @Override 30 | public boolean addAll(Collection c) { 31 | return standardAddAll(c); // implements in terms of add 32 | } 33 | } 34 | 35 | ``` 36 | 37 | 目前提供了Forwarding包装类的接口有: 38 | ForwardingCollection、ForwardingList、ForwardingSet、ForwardingSortedSet、ForwardingMap、ForwardingSortedMap、ForwardingConcurrentMap、ForwardingMapEntry、ForwardingQueue、ForwardingIterator、ForwardingListIterator、ForwardingMultiset、ForwardingMultimap、ForwardingListMultimap、ForwardingSetMultimap 39 | 40 | #### PeekingIterator 41 | Iterators提供一个Iterators.peekingIterator(Iterator)方法,来把Iterator包装为PeekingIterator,这是Iterator的子类,它能让你提前查看下一次调用next()返回的元素 42 | 注意:Iterators.peekingIterator返回的PeekingIterator不支持在peek()操作之后调用remove()方法。 43 | 44 | 示例:复制一个List,并去除连续的重复元素。 45 | ```java 46 | List result = Lists.newArrayList(); 47 | PeekingIterator iter = Iterators.peekingIterator(source.iterator()); 48 | while (iter.hasNext()) { 49 | E current = iter.next(); 50 | while (iter.hasNext() && iter.peek().equals(current)) { 51 | // skip this duplicate element 52 | iter.next(); 53 | } 54 | result.add(current); 55 | } 56 | 57 | ``` 58 | 59 | ##### AbstractIterator 60 | 61 | AbstractIterator可以让你更方便地实现自己的Iterator 62 | 63 | ```java 64 | public static Iterator skipNulls(final Iterator in) { 65 | return new AbstractIterator() { 66 | protected String computeNext() { 67 | while (in.hasNext()) { 68 | String s = in.next(); 69 | if (s != null) { 70 | return s; 71 | } 72 | } 73 | return endOfData(); 74 | } 75 | }; 76 | } 77 | 78 | ``` 79 | 80 | ------ 81 | [返回目录](/README.md) 82 | -------------------------------------------------------------------------------- /doc/collections-immutable-collections.md: -------------------------------------------------------------------------------- 1 | 不可变集合(Immutable collections) 2 | === 3 | 不可变集合是不可被修改的, 集合的数据项是在创建的时候提供, 并且在整个生命周期中都不可改变. 4 | 5 | Immutable对象有以下的优点: 6 | 7 | * 对不可靠的客户代码库来说,它使用安全,可以在未受信任的类库中安全的使用这些对象 8 | * 线程安全的:immutable对象在多线程下安全,没有竞态条件 9 | * 不需要支持可变性, 可以尽量节省空间和时间的开销. 所有的不可变集合实现都比可变集合更加有效的利用内存 (analysis) 10 | * 可以被使用为一个常量,并且期望在未来也是保持不变的 11 | 12 | Immutable对象是一个很好的防御编程(defensive programming)的技术实践 13 | 14 | #### 问题 15 | JDK自带的Collections.unmodifiableXXX实现的不是真正的不可变集合,当原始集合修改后,不可变集合也发生变化。 16 | 17 | ```java 18 | List lists = Lists.newArrayList("aa", "bb", "cc"); 19 | 20 | List unmodifiedLists = Collections.unmodifiableList(lists); 21 | assertEquals(3, unmodifiedLists.size()); 22 | 23 | lists.add("dd"); 24 | assertEquals(4, unmodifiedLists.size()); 25 | ``` 26 | 27 | JDK自带的Collections.unmodifiableXXX实现的不可变集合存在问题: 28 | 29 | * 它不安全: 如果有对象reference原始的被封装的集合类,这些方法返回的集合也就不是正真的不可改变 30 | * 效率低: 因为它返回的数据结构本质仍旧是原来的集合类,所以它的操作开销,包括并发下修改检查,hash table里的额外数据空间都和原来的集合是一样的。 31 | 32 | 33 | #### 方案 34 | com.google.common.collect.ImmutableXXX 35 | 36 | ##### 创建Immutable集合的方法 37 | ```java 38 | ImmutableSet.copyOf(set); // 使用copyOf方法 39 | ImmutableSet.of("a", "b", "c"); // 使用of方法 40 | ImmutableMap.of("a", 1, "b", 2); // 使用of方法 41 | ImmutableSet.builder() // 使用builder 42 | .add(new Color(0, 255, 255)) 43 | .add(new Color(0, 191, 255)) 44 | .build(); 45 | ``` 46 | 47 | ##### 智能的copyOf方法 48 | 一般来说,ImmutableXXX.copyOf(ImmutableCollection)会避免线性复杂度的拷贝操作: 49 | 50 | * 这个操作有可能就利用了被封装数据结构的常数复杂度的操作。但例如ImmutableSet.copyOf(list)不能在常数复杂度下实现。 51 | * 这样不会导致内存泄漏-例如,你有个ImmutableList imInfolist,然后你显式操作ImmutableList.copyOf(imInfolist.subList(0, 10))。这样的操作可以避免意外持有不再需要的在hugeList里元素的reference。 52 | * 它不会改变集合的语意-像ImmutableSet.copyOf(myImmutableSortedSet)这样的显式拷贝操作,因为在ImmutableSet里的hashCode()和equals()的含义和基于comparator的ImmutableSortedSet是不同的。 53 | 54 | 这些特性有助于最优化防御性编程的性能开销 55 | 56 | ##### asList方法 57 | 所有的immutable集合都以asList()的形式提供了ImmutableList视图(view). 58 | 比如,你把数据放在ImmutableSortedSet,你就可以调用sortedSet.asList().get(k)来取得第k个元素的集合。 59 | 返回的ImmutableList常常是个常数复杂度的视图,而不是一个真的拷贝。 60 | 61 | ##### guava中的不可变集合 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 |
可变类型集合来源Guava中的不可变集合
Collection JDKImmutableCollection
ListJDKImmutableList
SetJDKImmutableSet
SortedSet/NavigableSetJDKImmutableSortedSet
MapJDKImmutableMap
SortedMapJDKImmutableSortedMap
MultisetGuavaImmutableMultiset
SortedMultisetGuavaImmutableSortedMultiset
MultimapGuavaImmutableMultimap
ListMultimapGuavaImmutableListMultimap
SetMultimapGuavaImmutableSetMultimap
BiMapGuavaImmutableBiMap
ClassToInstanceMapGuavaImmutableClassToInstanceMap
TableGuavaImmutableTable
142 | 143 | ------ 144 | [返回目录](/README.md) 145 | -------------------------------------------------------------------------------- /doc/collections-new-collection-types.md: -------------------------------------------------------------------------------- 1 | Guava新增集合类型 2 | === 3 | Guava新增了一些JDK中没有的,但是被广泛使用到的新集合类型 4 | 5 | * [Multiset](#multiset) 6 | * [SortedMultiset](#sortedmultiset) 7 | * [MultiMap](#multimap) 8 | * [BiMap](#bimap) 9 | * [Table](#table) 10 | * [ClassToInstanceMap](#classtoinstancemap) 11 | * [RangeSet](#rangeset) 12 | 13 |

Multiset

14 | 15 | Multiset和Set的区别就是可以保存多个相同的对象。 16 | Multiset占据了List和Set之间的一个灰色地带:允许重复,但是不保证顺序。 17 | 常见使用场景:Multiset有一个有用的功能,就是跟踪每种对象的数量,所以你可以用来进行数字统计。 18 | 19 | Multiset接口定义的接口主要有: 20 | 21 | * add(E element): 向其中添加单个元素 22 | * add(E element,int occurrences): 向其中添加指定个数的元素 23 | * count(Object element): 返回给定参数元素的个数 24 | * remove(E element): 移除一个元素,其count值 会响应减少 25 | * remove(E element,int occurrences): 移除相应个数的元素 26 | * elementSet(): 将不同的元素放入一个Set中 27 | * entrySet(): 类似与Map.entrySet 返回Set。包含的Entry支持使用getElement()和getCount() 28 | * setCount(E element ,int count): 设定某一个元素的重复次数 29 | * setCount(E element,int oldCount,int newCount): 将符合原有重复个数的元素修改为新的重复次数 30 | * retainAll(Collection c): 保留出现在给定集合参数的所有的元素 31 | * removeAll(Collectionc): 去除出现给给定集合参数的所有的元素 32 | 33 | Guava提供了很多和JDK中的Map对应的Multiset的实现 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |
Map对应的MultiSet支持null值
HashMapHashMultiset
TreeMapTreeMultiSet
LinkedHashMapLinkedHashMultiset
ConcurrentHashMapConcurrentHashMultiset
ImmutableMapImmutableMultiset
66 | 67 |

SortedMultiset

68 | 69 | SortedMultiset是Multiset 接口的变种,它支持高效地获取指定范围的子集。 70 | 比如,你可以用 latencies.subMultiset(0,BoundType.CLOSED, 100, BoundType.OPEN).size()来统计你的站点中延迟在100毫秒以内的访问,然后把这个值和latencies.size()相比,以获取这个延迟水平在总体访问中的比例。 71 | 72 | TreeMultiset实现SortedMultiset接口。 73 | 74 |

MultiMap

75 | 76 | 经常会遇到这种结构 Map>或Map> 77 | Multimap可以很容易地把一个键映射到多个值。换句话说,Multimap是把键映射到任意多个值的一种方式。 78 | 79 | 可以用两种方式思考Multimap的概念: 80 | 81 | * "键-单个值映射"的集合: a->1, a->2, a->4, b->3, c->5 82 | * "键-值集合映射"的映射: a->[1,2,4], b->3, c->5 83 | 84 | 一般情况下都会使用ListMultimap或SetMultimap接口,它们分别把键映射到List或Set。 85 | Multimap.get(key)以集合形式返回键所对应的值视图, 即使没有任何对应的值,也会返回空集合。 86 | 对值视图集合进行的修改最终都会反映到底层的Multimap。 87 | 88 | ##### 修改Multimap的方法有: 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 |
方法签名描述等价于
put(K, V)添加键到单个值的映射multimap.get(key).add(value)
putAll(K, Iterable)依次添加键到多个值的映射Iterables.addAll(multimap.get(key), values)
remove(K, V)移除键到值的映射;如果有这样的键值并成功移除,返回true。multimap.get(key).remove(value)
removeAll(K)清除键对应的所有值,返回的集合包含所有之前映射到K的值,但修改这个集合就不会影响Multimap了。multimap.get(key).clear()
replaceValues(K, Iterable)清除键对应的所有值,并重新把key关联到Iterable中的每个元素。返回的集合包含所有之前映射到K的值。multimap.get(key).clear(); Iterables.addAll(multimap.get(key), values)
121 | 122 | ##### Multimap不是Map 123 | Multimap不是Map> 124 | 125 | * Multimap.get(key)总是返回非null、但是可能空的集合。这并不意味着Multimap为相应的键花费内存创建了集合,而只是提供一个集合视图方便你为键增加映射值 126 | * 如果你更喜欢像Map那样,为Multimap中没有的键返回null,请使用asMap()视图获取一个Map> 127 | * 当且仅当有值映射到键时,Multimap.containsKey(key)才会返回true 128 | * Multimap.entries()返回Multimap中所有”键-单个值映射”——包括重复键。如果你想要得到所有”键-值集合映射”,请使用asMap().entrySet()。 129 | * Multimap.size()返回所有”键-单个值映射”的个数,而非不同键的个数。要得到不同键的个数,请改用Multimap.keySet().size()。 130 | 131 |

BiMap

132 | 133 | BiMap提供了key和value双向关联的数据结构。 134 | 135 | * 可以用inverse()反转BiMap的键值映射, 反转的map不是新的map对象,它实现了一种视图关联,这样你对于反转后的map的所有操作都会影响原先的map对象 136 | * 保证值是唯一的,因此 values()返回Set而不是普通的Collection 137 | * 如果你想把键映射到已经存在的值,会抛出IllegalArgumentException异常, 使用BiMap.forcePut(key, value)可强制替换 138 | 139 | ```java 140 | BiMap logfileMap = HashBiMap.create(); 141 | BiMap filelogMap = logfileMap.inverse(); 142 | ``` 143 | 144 |

Table

145 | 146 | 当我们需要多个索引的数据结构的时候,通常情况下,我们只能用这种丑陋的Map>来实现。为此Guava提供了一个新的集合类型-Table集合类型,来支持这种数据结构的使用场景。 147 | 148 | ##### Table的视图 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 |
视图描述
rowMap()用Map>表现Table
rowKeySet()rowKeySet()返回”行”的集合Set
row(r) 用Map返回给定”行”的所有列,对这个map进行的写操作也将写入Table中。
columnMap()用Map>表现Table
columnKeySet()columnKeySet()返回”列”的集合Set
column(r) 用Map返回给定”列”的所有行,对这个map进行的写操作也将写入Table中。
cellSet()用元素类型为Table.Cell的Set表现Table。Cell类似于Map.Entry,但它是用行和列两个键区分的。
183 | 注: 基于列的访问会比基于的行访问稍微低效点 184 | 185 | Table有如下实现 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 |
实现描述
HashBasedTable本质上用HashMap>实现
TreeBasedTable本质上用TreeMap>实现
ImmutableTable本质上用ImmutableMap>实现;注:ImmutableTable对稀疏或密集的数据集都有优化。
ArrayTable要求在构造时就指定行和列的大小,本质上由一个二维数组实现,以提升访问速度和密集Table的内存利用率。
208 | 209 |

ClassToInstanceMap

210 | 211 | ClassToInstanceMap\ 相当于 Map, B>, 它的键是类型,而值是符合键所指类型的对象。 212 | ClassToInstanceMap额外声明了两个方法:T getInstance(Class) 和T putInstance(Class, T),从而避免强制类型转换,同时保证了类型安全。 213 | 214 | 215 |

RangeSet

216 | 217 | RangeSet描述了一组不相连的、非空的区间。当把一个区间添加到可变的RangeSet时,所有相连的区间会被合并,空区间会被忽略。 218 | 例如: 219 | ```java 220 | RangeSet rangeSet = TreeRangeSet.create(); 221 | rangeSet.add(Range.closed(1, 10)); // {[1, 10]} 222 | rangeSet.add(Range.closedOpen(11, 15)); // 不相连的区间: {[1, 10], [11, 15)} 223 | rangeSet.add(Range.closedOpen(15, 20)); // 相连的区间; {[1, 10], [11, 20)} 224 | rangeSet.add(Range.openClosed(0, 0)); // 空区间; {[1, 10], [11, 20)} 225 | rangeSet.remove(Range.open(5, 10)); // 分割[1, 10]; {[1, 5], [10, 10], [11, 20)} 226 | ``` 227 | 228 | RangeMap描述了"不相交的、非空的区间"到特定值的映射。和RangeSet不同,RangeMap不会合并相邻的映射,即便相邻的区间映射到相同的值。例如: 229 | ```java 230 | RangeMap rangeMap = TreeRangeMap.create(); 231 | rangeMap.put(Range.closed(1, 10), "foo"); // {[1, 10] => "foo"} 232 | rangeMap.put(Range.open(3, 6), "bar"); // {[1, 3] => "foo", (3, 6) => "bar", [6, 10] => "foo"} 233 | rangeMap.put(Range.open(10, 20), "foo"); // {[1, 3] => "foo", (3, 6) => "bar", [6, 10] => "foo", (10, 20) => "foo"} 234 | rangeMap.remove(Range.closed(5, 11)); // {[1, 3] => "foo", (3, 5) => "bar", (11, 20) => "foo"} 235 | ``` 236 | 237 | ------ 238 | [返回目录](/README.md) -------------------------------------------------------------------------------- /doc/collections-utility-classes.md: -------------------------------------------------------------------------------- 1 | 强大的集合工具类 2 | === 3 | Guava提供了很多类似java.util.Collections的静态工具类 4 | 5 | Guava中工具类与集合的对应关系如下: 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
集合接口来自于JDK/Guava对应的Guava工具类
CollectionJDKCollections2
ListJDKLists
SetJDKSets
SortedSetJDKSets
MapJDKMaps
SortedMapJDKMaps
QueueJDKQueues
MultisetGuavaMultisets
MultimapGuavaMultimaps
BiMapGuavaMaps
TableGuavaTables
68 | 69 | 70 | * [静态工厂方法](#static-constructor) 71 | * [Iterables](#iterables) 72 | * [Lists](#lists) 73 | * [Sets](#sets) 74 | * [Maps](#maps) 75 | * [Multisets](#multisets) 76 | * [Multimaps](#multimaps) 77 | * [Tables](#tables) 78 | 79 |

Static Constructor

80 | 81 | JDK7之前构造一个集合 82 | > List list = new ArrayList(); 83 | 84 | Guava提供了能够推断泛型的静态工厂方法 85 | > List list = Lists.newArrayList(); 86 | > List theseElements = Lists.newArrayList("alpha", "beta", "gamma"); //可以直接初始化的静态构造方法 87 | > List exactly100 = Lists.newArrayListWithCapacity(100); //更具可读性的工厂方法 88 | > List approx100 = Lists.newArrayListWithExpectedSize(100); //更具可读性的工厂方法 89 | > Set set = Sets.newHashSet(); 90 | > Set approx100Set = Sets.newHashSetWithExpectedSize(100); 91 | 92 |

Iterables

93 | 94 | 相比于Collection, Guava更偏向于提供Iterable类型, 原因就不写了,网上可以找到 95 | 大部分的方法都在Iterators和FluentIterable中, 后者提供了很多链式操作 96 | 97 | Iterators常用方法 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 |
方法描述参考
concat(Iterable)串联多个iterables的懒加载视图concat(Iterable...)
frequency(Iterable, Object)返回对象在iterable中出现的次数Collections.frequency(Collection, Object)
partition(Iterable, int)把iterable按指定大小分割,得到的子集都不能进行修改操作Lists.partition(List, int); paddedPartition(Iterable, int)
getFirst(Iterable, T default)返回iterable的第一个元素,若iterable为空则返回默认值Iterable.iterator().next(); FluentIterable.first()
getLast(Iterable)返回iterable的最后一个元素,若iterable为空则抛出NoSuchElementExceptiongetLast(Iterable, T default); FluentIterable.last()
elementsEqual(Iterable, Iterable)如果两个iterable中的所有元素相等且顺序一致,返回trueList.equals(Object)
unmodifiableIterable(Iterable)返回iterable的不可变视图Collections.unmodifiableCollection(Collection)
limit(Iterable, int)返回一个尽可能达到指定个数的iterableFluentIterable.limit(int)
getOnlyElement(Iterable)获取iterable中唯一的元素,如果iterable为空或有多个元素,则失败getOnlyElement(Iterable, T default)
150 | 151 | Iterators中也有很多和Collections相似的工具方法,比如addAll、removeAll、retainAll、contains、size、isEmpty等 152 | 153 |

Lists

154 | 155 | ```java 156 | // 静态工厂方法 157 | Lists.newArrayList(); 158 | Lists.newArrayList(1, 2, 3); 159 | Lists.newArrayList(Sets.newHashSet(1, 2, 3)); 160 | Lists.newArrayListWithCapacity(10); 161 | Lists.newArrayListWithExpectedSize(10); 162 | 163 | Lists.newLinkedList(); 164 | Lists.newLinkedList(Sets.newHashSet(1, 2, 3)); 165 | 166 | // 其他工具方法 167 | Lists.partition(Lists.newArrayList(1, 2, 3, 4, 5), 2); 168 | Lists.reverse(Lists.newArrayList(1, 2, 3, 4, 5)); 169 | ``` 170 | 171 |

Sets

172 | 173 | ```java 174 | // 静态工厂方法 175 | Sets.newHashSet(); 176 | Sets.newHashSet(1, 2, 3); 177 | Sets.newHashSetWithExpectedSize(10); 178 | Sets.newHashSet(Lists.newArrayList(1, 2, 3)); 179 | 180 | Sets.newLinkedHashSet(); 181 | Sets.newLinkedHashSetWithExpectedSize(10); 182 | Sets.newLinkedHashSet(Lists.newArrayList(1, 2, 3)); 183 | 184 | Sets.newTreeSet(); 185 | Sets.newTreeSet(Lists.newArrayList(1, 2, 3)); 186 | Sets.newTreeSet(Ordering.natural()); 187 | 188 | // 集合运算(返回SetView) 189 | Sets.union(Sets.newHashSet(1, 2, 3), Sets.newHashSet(4, 5, 6)); // 取并集[1,2,3,4,5,6] 190 | Sets.intersection(Sets.newHashSet(1, 2, 3), Sets.newHashSet(3, 4, 5)); // 取交集[3] 191 | Sets.difference(Sets.newHashSet(1, 2, 3), Sets.newHashSet(3, 4, 5)); // 只在set1, 不在set2[1,2] 192 | Sets.symmetricDifference(Sets.newHashSet(1, 2, 3), Sets.newHashSet(3, 4, 5)); // 交集取反[1,2,4,5] 193 | 194 | // 其他工具方法 195 | Sets.cartesianProduct(Lists.newArrayList(Sets.newHashSet(1, 2), Sets.newHashSet(3, 4))); // 返回所有集合的笛卡尔积 196 | Sets.powerSet(Sets.newHashSet(1, 2, 3)); // 返回给定集合的所有子集 197 | ``` 198 | 199 |

Maps

200 | 201 | Maps除了类似Lists、Sets一样提供基本的静态工厂方法外,还提供了很多其他有意思的方法 202 | 203 | #### uniqueIndex 204 | 205 | 场景:有一组对象,它们在某个属性上分别有独一无二的值,而我们希望能够按照这个属性值查找对象 206 | 207 | > Maps.uniqueIndex(Iterable,Function) 208 | > 这个方法返回一个Map,键为Function返回的属性值,值为Iterable中相应的元素,因此我们可以反复用这个Map进行查找操作。 209 | 210 | 示例: 211 | ```java 212 | ImmutableMap stringsByIndex = Maps.uniqueIndex(strings, new Function () { 213 | public Integer apply(String string) { 214 | return string.length(); 215 | } 216 | }); 217 | 218 | ``` 219 | 如果索引值不是独一无二的,请参见下面的Multimaps.index方法。 220 | 221 | #### difference 222 | 223 | Maps.difference(Map, Map)用来比较两个Map以获取所有不同点, 该方法返回MapDifference对象 224 | 225 | ```java 226 | Map left = ImmutableMap.of("a", 1, "b", 2, "c", 3); 227 | Map right = ImmutableMap.of("b", 2, "c", 4, "d", 5); 228 | MapDifference diff = Maps.difference(left, right); 229 | 230 | diff.entriesInCommon(); // {"b" => 2}, 两个Map中都有的映射项,包括键与值 231 | diff.entriesDiffering(); // {"c" => (3, 4)}, 键相同但是值不同的映射项。 232 | // 返回的Map的值类型为MapDifference.ValueDifference,以表示左右两个不同的值 233 | diff.entriesOnlyOnLeft(); // {"a" => 1}, 键只存在于左边Map的映射项 234 | diff.entriesOnlyOnRight(); // {"d" => 5}, 键只存在于右边Map的映射项 235 | 236 | ``` 237 | 238 |

Multisets

239 | 240 | ```java 241 | containsOccurrences(Multiset sup, Multiset sub); //对任意o,如果sub.count(o)<=super.count(o),返回true 242 | removeOccurrences(Multiset removeFrom, Multiset toRemove); //对toRemove中的重复元素,仅在removeFrom中删除相同个数 243 | retainOccurrences(Multiset removeFrom, Multiset toRetain); //修改removeFrom,以保证任意o都符合removeFrom.count(o)<=toRetain.count(o) 244 | intersection(Multiset, Multiset); //返回两个multiset的交集 245 | copyHighestCountFirst(Multiset); //返回Multiset的不可变拷贝,并将元素按重复出现的次数做降序排列 246 | unmodifiableMultiset(Multiset); //返回Multiset的只读视图 247 | unmodifiableSortedMultiset(SortedMultiset); //返回SortedMultiset的只读视图 248 | 249 | ``` 250 | 251 |

Multimaps

252 | 253 | #### index 254 | 作为Maps.uniqueIndex的兄弟方法,Multimaps.index(Iterable, Function)通常针对的场景是:有一组对象,它们有共同的特定属性,我们希望按照这个属性的值查询对象,但属性值不一定是独一无二的。 255 | 256 | #### invertFrom 257 | 鉴于Multimap可以把多个键映射到同一个值,也可以把一个键映射到多个值,反转Multimap也会很有用。Guava 提供了invertFrom(Multimap toInvert, Multimap dest)做这个操作,并且你可以自由选择反转后的Multimap实现。 258 | > TreeMultimap inverse = Multimaps.invertFrom(multimap, TreeMultimap.create()); 259 | 260 | #### forMap 261 | forMap方法把Map包装成SetMultimap, 与Multimaps.invertFrom结合使用,可以把多对一的Map反转为一对多的Multimap。 262 | ```java 263 | Map map = ImmutableMap.of("a", 1, "b", 1, "c", 2); 264 | SetMultimap multimap = Multimaps.forMap(map); 265 | // multimap maps ["a" => {1}, "b" => {1}, "c" => {2}] 266 | Multimap inverse = Multimaps.invertFrom(multimap, HashMultimap. create()); 267 | // inverse maps [1 => {"a", "b"}, 2 => {"c"}] 268 | 269 | ``` 270 | 271 |

Tables

272 | 273 | #### customTable 274 | 275 | Tables.newCustomTable(Map, Supplier)允许你指定Table用什么样的map实现行和列。 276 | ```java 277 | // use LinkedHashMaps instead of HashMaps 278 | Table table = Tables.newCustomTable( 279 | Maps.>newLinkedHashMap(), 280 | new Supplier> () { 281 | public Map get() { 282 | return Maps.newLinkedHashMap(); 283 | } 284 | } 285 | ); 286 | 287 | ``` 288 | 289 | #### transpose 290 | 291 | transpose(Table)方法允许你把Table转置成Table。例如,如果你在用Table构建加权有向图,这个方法就可以把有向图反转。 292 | 293 | ------ 294 | [返回目录](/README.md) -------------------------------------------------------------------------------- /doc/concurrency-listenablefuture.md: -------------------------------------------------------------------------------- 1 | 并发编程之ListenableFuture 2 | === 3 | 4 | ### 背景 5 | 在并发编程方面,JDK提供了Future, 但是使用起来不是很方便,guava提供了ListenableFuture以简化并发的编写. 6 | ListenableFuture继承自Future. 7 | 8 | ### 接口 9 | 10 | ```java 11 | interface ListenableFuture extends Future 12 | void addListener(Runnable listener, Executor executor) 13 | ``` 14 | 15 | 传统的Future: 通过异步的方式计算返回结果,Future是运行中的多线程的一个引用句柄. 16 | ListenableFuture: 允许注册回调方法, 在运算(多线程执行)完成的时候,使用指定的Executor执行指定的Runnable. 17 | 18 | ### 添加回调 19 | 20 | guava提供了以下几种方式添加回调 21 | 22 | * ListenableFuture接口上的addLister(Runnbale, Executor) 23 | * Futures.addCallback(ListenableFuture, FutureCallback, Executor) 24 | * Futures.addCallback(ListenableFuture, FutureCallback) // 这种情况默认使用MoreExecutors.sameThreadExecutor()线程池 25 | 26 | FutureCallback采用轻量级的设计, 只需要实现以下两个方法 27 | 28 | * onSuccess(V) // 在Future成功的时候执行 29 | * onFailure(Throwable) // 在Future失败的时候执行 30 | 31 | ### 创建ListenableFuture 32 | 33 | 传统JDK中创建Future的方式: 34 | 35 | ```java 36 | Executors.newFixedThreadPool(10).submit(Callable); 37 | ``` 38 | 39 | guava中创建ListenableFuture的方式: 40 | 41 | ```java 42 | MoreExecutors.listeningDecorator(ExecutorService).submit(Callable); 43 | ``` 44 | 45 | 完整的ListenableFuture使用示例: 46 | 47 | ```java 48 | // 创建ListeningExecutorService 49 | ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10)); 50 | 51 | // 添加执行操作 52 | ListenableFuture explosion = service.submit(new Callable() { 53 | public Explosion call() { 54 | return pushBigRedButton(); 55 | } 56 | }); 57 | 58 | // 添加回调 59 | Futures.addCallback(explosion, new FutureCallback() { 60 | // 操作执行完成后,执行onSuccess 61 | public void onSuccess(Explosion explosion) { 62 | walkAwayFrom(explosion); 63 | } 64 | public void onFailure(Throwable thrown) { 65 | battleArchNemesis(); // escaped the explosion! 66 | } 67 | }); 68 | ``` 69 | 70 | 当然,还有其他方式来创建,比如: 71 | 72 | ```java 73 | // 类似JDK的FutureTask模式 74 | ListenableFutureTask.create(Callable); 75 | 76 | // 将其他API提供的Future转换为ListenableFuture 77 | JdkFutureAdapters.listenInPoolThread(Future); 78 | ``` 79 | 80 | ### 使用 81 | 82 | guava还提供了一些支持链式操作的API 83 | 84 | ```java 85 | Futures.transform(ListenableFuture, AsyncFunction, Executor); 86 | Futures.transform(ListenableFuture, Function, Executor); 87 | Futures.allAsList(Iterable>); 88 | Futures.successfulAsList(Iterable>); 89 | ``` 90 | 91 | 92 | ------ 93 | [返回目录](/README.md) -------------------------------------------------------------------------------- /doc/concurrency-service.md: -------------------------------------------------------------------------------- 1 | 并发编程之Service框架 2 | === 3 | 4 | ------ 5 | [返回目录](/README.md) -------------------------------------------------------------------------------- /doc/eventbus.md: -------------------------------------------------------------------------------- 1 | 事件总线EventBus 2 | === 3 | 4 | ### 背景 5 | 6 | JDK中通过Observer接口和Observable类实现观察者模式, Observer对象是观察者,Observable对象是被观察者. 7 | 8 | 实现一个简单的观察者模式有以下几步: 9 | 10 | 1. 创建被观察者, 继承自java.util.Observable类 11 | 2. 创建观察者, 实现java.util.Observer接口 12 | 3. 在观察者中实现void update(java.util.Observable observable, java.lang.Object o)方法 13 | 4. 在被观察者对象上添加观察者Observable.addObserver(observer) 14 | 5. 当被观察事件发生时,执行以下代码 15 | 6. setChanged(); // 内部标志,注明数据发生了变化 16 | 7. notifyObservers(); // 调用观察者对象列表中所有的Observer的update()方法, 通知它们数据变化了 17 | 18 | 这种方式是通过发布者和订阅者之间的显式注册实现的. 19 | guava的EventBus就是为了取代这种显示注册方式,使组件间有更好的解耦. 20 | EventBus不适用于进程间通信。 21 | 22 | ### 示例 23 | 24 | 消息封装类: 任意的Java对象均可 25 | ```java 26 | public class LogEvent { 27 | 28 | private String log; 29 | 30 | // setter、getter 31 | } 32 | ``` 33 | 34 | 消息接收类: 任意的Java对象均可, 只需要在接收方法上添加注解@Subscribe即可 35 | ```java 36 | public class LogEventListener { 37 | 38 | @Subscribe 39 | public void listen(LogEvent log) { 40 | // handle log 41 | } 42 | 43 | } 44 | ``` 45 | 46 | 消息发布 47 | ```java 48 | // 事件总线 49 | EventBus eventBus = new EventBus(); 50 | // 事件监听者 51 | LogEventListener logEventListener = new LogEventListener(); 52 | // 注册监听 53 | eventBus.register(logEventListener) 54 | // 发布消息 55 | eventBus.post(new LogEvent("测试")); 56 | ``` 57 | 58 | ### 解析 59 | 60 | EventBus中主要包括以下几个角色: 61 | 62 | * 事件: 可以向事件总线发布的消息 63 | * 监听者: 提供一个处理方法, 通过参数声明希望接受和处理事件对象,实现自己的处理逻辑 64 | * 事件总线: 可以理解为消息传输的渠道,所有在当前事件总线上注册了的监听者都会收到来自于当前事件总线、与监听者所声明的期望类型一致(支持继承关系)的消息 65 | 66 | 同步发布事件: EventBus.post(Object) 67 | 异步发布事件: AsyncEventBus.post(Object) 68 | 69 | guava并未将EventBus设计为单例, 所以可以根据实际情况使用 70 | 71 | ### DeadEvent 72 | 73 | EventBus会将所有发布后,没有监听者处理的事件包装为DeadEvent, 可以通过监听该类型的消息来检测哪些消息未指明监听者 74 | 75 | ### 监听多个消息 76 | EventBus中,可以支持同一个监听者监听多个消息,只需要在每个订阅消息的方法上加上@Subscribe注解即可 77 | 78 | ```java 79 | @Subscribe 80 | public void listenInteger(Integer event) { 81 | lastInteger = event; 82 | System.out.println("event Integer:"+lastInteger); 83 | } 84 | 85 | @Subscribe 86 | public void listenLong(Long event) { 87 | lastLong = event; 88 | System.out.println("event Long:"+lastLong); 89 | } 90 | ``` 91 | 92 | ### 单例使用 93 | 94 | 在简单情况下,可以将EventBus声明为全局唯一的单例, 并可以通过Spring完成自动注册, 这样将进一步简化使用 95 | 96 | 示例: 97 | 98 | EventBus工厂 99 | ```java 100 | public class EventBusFactory { 101 | 102 | private static final EventBusFactory factory = new EventBusFactory(); 103 | 104 | private final EventBus eventBus; 105 | 106 | private EventBusFactory() { 107 | eventBus = new AsyncEventBus("AsyncEventBus", Executors.newFixedThreadPool(5)); 108 | } 109 | 110 | public static final EventBusFactory getDefault() { 111 | return factory; 112 | } 113 | 114 | public EventBus eventBus() { 115 | return eventBus; 116 | } 117 | 118 | } 119 | ``` 120 | 121 | 通过spring自动注册 122 | ```java 123 | @Service 124 | public class EventBusPostProcessor implements BeanPostProcessor { 125 | 126 | @Override 127 | public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 128 | return bean; 129 | } 130 | 131 | @Override 132 | public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 133 | // for each method in the bean 134 | Method[] methods = bean.getClass().getMethods(); 135 | for (Method method : methods) { 136 | // check the annotations on that method 137 | Annotation[] annotations = method.getAnnotations(); 138 | for (Annotation annotation : annotations) { 139 | // if it contains the Subscribe annotation 140 | if (annotation.annotationType().equals(Subscribe.class)) { 141 | // 检查到bean声明了Guava EventBus Subscribe注解, 则自动注册到全局的EventBus上 142 | EventBusFactory.getDefault().eventBus().register(bean); 143 | LOGGER.info("Bean " + beanName + " was subscribed to EventBus"); 144 | // we only need to register once 145 | return bean; 146 | } 147 | } 148 | } 149 | 150 | return bean; 151 | } 152 | 153 | } 154 | ``` 155 | 156 | 发布消息 157 | ```java 158 | EventBusFactory.getDefault().eventBus().post(new LogEvent(log)); 159 | ``` 160 | 161 | 通过这种方式,只需要编写监听者即可,无需关心注册 162 | 163 | ------ 164 | [返回目录](/README.md) -------------------------------------------------------------------------------- /doc/functional-idioms.md: -------------------------------------------------------------------------------- 1 | 函数式 2 | === 3 | 4 | ### 注意!注意!注意! 5 | 6 | Java中一切皆对象,唯函数不算! 7 | Java 7以前, Java中只能通过笨拙冗长的匿名类来达到近似函数式编程的效果。 (Java 8引入了Lambda表达式) 8 | 9 | > 过度使用Guava函数式编程会导致冗长、混乱、可读性差而且低效的代码。 10 | > 如果你想通过函数式风格达成一行代码,致使这行代码长到荒唐,Guava团队会泪流满面。 11 | > 请务必确保,当使用Guava函数式的时候,用传统的命令式做同样的事情不会更具可读性。 12 | > 总之,不要盲目使用函数式!!! 13 | 14 | 比较一下这种场景: 15 | 16 | 函数式 17 | ```java 18 | Function lengthFunction = new Function() { 19 | public Integer apply(String string) { 20 | return string.length(); 21 | } 22 | }; 23 | Predicate allCaps = new Predicate() { 24 | public boolean apply(String string) { 25 | return CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string); 26 | } 27 | }; 28 | Multiset lengths = HashMultiset.create( 29 | Iterables.transform(Iterables.filter(strings, allCaps), lengthFunction)); 30 | ``` 31 | 32 | 函数式2 33 | ```java 34 | Multiset lengths = HashMultiset.create( 35 | FluentIterable.from(strings) 36 | .filter(new Predicate() { 37 | public boolean apply(String string) { 38 | return CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string); 39 | } 40 | }) 41 | .transform(new Function() { 42 | public Integer apply(String string) { 43 | return string.length(); 44 | } 45 | })); 46 | ``` 47 | 48 | 命令式 49 | ```java 50 | Multiset lengths = HashMultiset.create(); 51 | for (String string : strings) { 52 | if (CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string)) { 53 | lengths.add(string.length()); 54 | } 55 | } 56 | ``` 57 | 58 | ### Functions[函数]和Predicates[断言] 59 | 60 | 61 | ------ 62 | [返回目录](/README.md) 63 | -------------------------------------------------------------------------------- /doc/hash.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiantiangao/guava-study/cba2511cf319c372f2a5cb942a2b4563a5f45aee/doc/hash.md -------------------------------------------------------------------------------- /doc/io.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiantiangao/guava-study/cba2511cf319c372f2a5cb942a2b4563a5f45aee/doc/io.md -------------------------------------------------------------------------------- /doc/math.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiantiangao/guava-study/cba2511cf319c372f2a5cb942a2b4563a5f45aee/doc/math.md -------------------------------------------------------------------------------- /doc/networking.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiantiangao/guava-study/cba2511cf319c372f2a5cb942a2b4563a5f45aee/doc/networking.md -------------------------------------------------------------------------------- /doc/primitives.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiantiangao/guava-study/cba2511cf319c372f2a5cb942a2b4563a5f45aee/doc/primitives.md -------------------------------------------------------------------------------- /doc/ranges.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiantiangao/guava-study/cba2511cf319c372f2a5cb942a2b4563a5f45aee/doc/ranges.md -------------------------------------------------------------------------------- /doc/reflection.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiantiangao/guava-study/cba2511cf319c372f2a5cb942a2b4563a5f45aee/doc/reflection.md -------------------------------------------------------------------------------- /doc/strings.md: -------------------------------------------------------------------------------- 1 | 字符串工具类 2 | === 3 | 4 | ### 连接器(Joiner) 5 | 6 | ### 拆分器(Splitter) 7 | 8 | ### 字符串匹配(CharMatcher) 9 | 10 | ### 字符集(Charsets) 11 | 12 | ### 大小写格式(CaseFormat) 13 | 14 | ------ 15 | [返回目录](/README.md) -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 4.0.0 5 | 6 | com.gtt 7 | guava-study 8 | 1.0.0 9 | jar 10 | 11 | 12 | 13 | com.google.guava 14 | guava 15 | 16.0.1 16 | 17 | 18 | junit 19 | junit 20 | 4.4 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/main/java/com/gtt/basicutilities/DefaultsTest.java: -------------------------------------------------------------------------------- 1 | package com.gtt.basicutilities; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertNull; 5 | 6 | import org.junit.Test; 7 | 8 | import com.google.common.base.Defaults; 9 | 10 | /** 11 | * @author tiantiangao 12 | */ 13 | public class DefaultsTest { 14 | 15 | @Test 16 | public void testGetDefaultValue() { 17 | assertEquals(false, Defaults.defaultValue(boolean.class).booleanValue()); 18 | assertEquals('\0', Defaults.defaultValue(char.class).charValue()); 19 | assertEquals(0, Defaults.defaultValue(byte.class).byteValue()); 20 | assertEquals(0, Defaults.defaultValue(short.class).shortValue()); 21 | assertEquals(0, Defaults.defaultValue(int.class).intValue()); 22 | assertEquals(0, Defaults.defaultValue(long.class).longValue()); 23 | assertEquals((Float) 0.0f, (Float) Defaults.defaultValue(float.class).floatValue()); 24 | assertEquals((Double) 0.0d, (Double) Defaults.defaultValue(double.class).doubleValue()); 25 | assertNull(Defaults.defaultValue(void.class)); 26 | assertNull(Defaults.defaultValue(String.class)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/gtt/basicutilities/ObjectsTest.java: -------------------------------------------------------------------------------- 1 | package com.gtt.basicutilities; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Test; 6 | 7 | import com.google.common.base.Objects; 8 | import com.google.common.collect.ComparisonChain; 9 | import com.google.common.primitives.Ints; 10 | 11 | /** 12 | * 13 | * @author tiantiangao 14 | */ 15 | public class ObjectsTest { 16 | 17 | @Test 18 | public void test() { 19 | testEquals(); 20 | testHashCode(); 21 | testToString(); 22 | testCompare(); 23 | } 24 | 25 | private void testEquals() { 26 | assertTrue(Objects.equal("a", "a")); 27 | assertFalse(Objects.equal("a", null)); 28 | assertFalse(Objects.equal(null, "a")); 29 | assertTrue(Objects.equal(null, null)); 30 | } 31 | 32 | private void testHashCode() { 33 | assertNotSame(Objects.hashCode("a", "b", "c"), Objects.hashCode("c", "b", "a")); 34 | } 35 | 36 | private void testToString() { 37 | assertEquals("Object{name=test, age=18}", 38 | Objects.toStringHelper(Object.class).add("name", "test").add("age", 18).toString()); 39 | } 40 | 41 | private void testCompare() { 42 | assertEquals(-1, Ints.compare(1, 2)); 43 | assertEquals(1, Ints.compare(2, 1)); 44 | assertEquals(0, Ints.compare(1, 1)); 45 | 46 | assertEquals(1, ComparisonChain.start().compare(1, 1).compare("aString", "aString").compare(true, false).result()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/gtt/basicutilities/OptionalTest.java: -------------------------------------------------------------------------------- 1 | package com.gtt.basicutilities; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Test; 6 | 7 | import com.google.common.base.Optional; 8 | 9 | /** 10 | * @author tiantiangao 11 | */ 12 | public class OptionalTest { 13 | 14 | @Test 15 | public void test() { 16 | testNotNullValue(); 17 | testNullValue(); 18 | } 19 | 20 | private void testNotNullValue() { 21 | Optional possible = Optional.fromNullable(6); 22 | assertTrue(possible.isPresent()); 23 | assertEquals(6, possible.get().intValue()); 24 | assertEquals(6, possible.or(1).intValue()); 25 | assertEquals(6, possible.orNull().intValue()); 26 | } 27 | 28 | private void testNullValue() { 29 | Optional absent = Optional.fromNullable(null); 30 | assertFalse(absent.isPresent()); 31 | try { 32 | absent.get(); 33 | fail(); 34 | } catch (IllegalStateException e) { 35 | assertTrue(true); 36 | } 37 | assertEquals(1, absent.or(1).intValue()); 38 | assertNull(absent.orNull()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/gtt/basicutilities/OrderingTest.java: -------------------------------------------------------------------------------- 1 | package com.gtt.basicutilities; 2 | 3 | import static org.junit.Assert.assertFalse; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import java.util.Collections; 7 | import java.util.Comparator; 8 | import java.util.List; 9 | 10 | import org.junit.Test; 11 | 12 | import com.google.common.base.Function; 13 | import com.google.common.collect.Lists; 14 | import com.google.common.collect.Ordering; 15 | 16 | /** 17 | * @author tiantiangao 18 | */ 19 | public class OrderingTest { 20 | 21 | @Test 22 | public void test() { 23 | testNatural(); 24 | testFrom(); 25 | testReverse(); 26 | testNullFirst(); 27 | testNullLast(); 28 | testCompound(); 29 | testOnResultOf(); 30 | testGreatestOf(); 31 | testLeastOf(); 32 | testIsOrdered(); 33 | testIsStrictlyOrdered(); 34 | testSortedCopy(); 35 | } 36 | 37 | private void testNatural() { 38 | // test int order 39 | List unorderedIntList = Lists.newArrayList(5, 3, 2, 4, 1); 40 | List orderedIntList = Lists.newArrayList(1, 2, 3, 4, 5); 41 | Collections.sort(unorderedIntList, Ordering.natural()); 42 | assertTrue(orderedIntList.equals(unorderedIntList)); 43 | 44 | // test string order 45 | List unorderedStringList = Lists.newArrayList("Test", "Jerry", "Rock", "Ohaha", "Yeah"); 46 | List orderedStringList = Lists.newArrayList("Jerry", "Ohaha", "Rock", "Test", "Yeah"); 47 | Collections.sort(unorderedStringList, Ordering.natural()); 48 | assertTrue(orderedStringList.equals(unorderedStringList)); 49 | } 50 | 51 | private void testFrom() { 52 | List unorderedIntList = Lists.newArrayList(5, 3, 2, 4, 1); 53 | List orderedIntList = Lists.newArrayList(1, 2, 3, 4, 5); 54 | Collections.sort(unorderedIntList, Ordering.from(new Comparator() { 55 | @Override 56 | public int compare(Integer i1, Integer i2) { 57 | return i1.compareTo(i2); 58 | } 59 | })); 60 | assertTrue(orderedIntList.equals(unorderedIntList)); 61 | } 62 | 63 | private void testReverse() { 64 | List unorderedIntList = Lists.newArrayList(5, 3, 2, 4, 1); 65 | List orderedIntList = Lists.newArrayList(5, 4, 3, 2, 1); 66 | Collections.sort(unorderedIntList, Ordering.natural().reverse()); 67 | assertTrue(orderedIntList.equals(unorderedIntList)); 68 | } 69 | 70 | private void testNullFirst() { 71 | List unorderedIntList = Lists.newArrayList(5, 3, null, 4, 1); 72 | List orderedIntList = Lists.newArrayList(null, 1, 3, 4, 5); 73 | Collections.sort(unorderedIntList, Ordering.natural().nullsFirst()); 74 | assertTrue(orderedIntList.equals(unorderedIntList)); 75 | } 76 | 77 | private void testNullLast() { 78 | List unorderedIntList = Lists.newArrayList(5, 3, null, 4, 1); 79 | List orderedIntList = Lists.newArrayList(1, 3, 4, 5, null); 80 | Collections.sort(unorderedIntList, Ordering.natural().nullsLast()); 81 | assertTrue(orderedIntList.equals(unorderedIntList)); 82 | } 83 | 84 | private void testCompound() { 85 | List unorderedStringList = Lists.newArrayList("Oest", "Jerry", "Jock", "Ohaha", "Yeah"); 86 | List orderedStringList = Lists.newArrayList("Jock", "Jerry", "Ohaha", "Oest", "Yeah"); 87 | 88 | Ordering firstLetterOrdering = Ordering.from(new Comparator() { 89 | @Override 90 | public int compare(String s1, String s2) { 91 | return s1.substring(0, 1).compareTo(s2.substring(0, 1)); 92 | } 93 | }); 94 | Collections.sort(unorderedStringList, firstLetterOrdering.compound(new Comparator() { 95 | @Override 96 | public int compare(String s1, String s2) { 97 | return s2.substring(1, s2.length()).compareTo(s1.substring(1, s1.length())); 98 | } 99 | })); 100 | assertTrue(orderedStringList.equals(unorderedStringList)); 101 | } 102 | 103 | private void testOnResultOf() { 104 | List unorderedStringList = Lists.newArrayList("Oest", "Jarry", "Jock", "Ohaha", "Ybah"); 105 | List orderedStringList = Lists.newArrayList("Jarry", "Ybah", "Oest", "Ohaha", "Jock"); 106 | 107 | Ordering secondLetterOrdering = Ordering.natural().onResultOf(new Function() { 108 | @Override 109 | public String apply(String input) { 110 | // 去除首字母 111 | return input.substring(1, input.length()); 112 | } 113 | }); 114 | 115 | Collections.sort(unorderedStringList, secondLetterOrdering); 116 | assertTrue(orderedStringList.equals(unorderedStringList)); 117 | } 118 | 119 | private void testGreatestOf() { 120 | List unorderList = Lists.newArrayList(5, 3, 2, 4, 1); 121 | List orderList = Lists.newArrayList(5, 4); 122 | assertTrue(orderList.equals(Ordering.natural().greatestOf(unorderList, 2))); 123 | 124 | orderList = Lists.newArrayList(5, 4, 3, 2, 1); 125 | assertTrue(orderList.equals(Ordering.natural().greatestOf(unorderList, 8))); 126 | } 127 | 128 | private void testLeastOf() { 129 | List unorderList = Lists.newArrayList(5, 3, 2, 4, 1); 130 | List orderList = Lists.newArrayList(1, 2); 131 | assertTrue(orderList.equals(Ordering.natural().leastOf(unorderList, 2))); 132 | 133 | orderList = Lists.newArrayList(1, 2, 3, 4, 5); 134 | assertTrue(orderList.equals(Ordering.natural().leastOf(unorderList, 8))); 135 | } 136 | 137 | private void testIsOrdered() { 138 | // 大于可通过 139 | List orderList = Lists.newArrayList(1, 2, 3, 4, 5); 140 | assertTrue(Ordering.natural().isOrdered(orderList)); 141 | 142 | // 大于或等于也可通过 143 | orderList = Lists.newArrayList(1, 2, 2, 4, 5); 144 | assertTrue(Ordering.natural().isOrdered(orderList)); 145 | } 146 | 147 | private void testIsStrictlyOrdered() { 148 | // 大于可通过 149 | List orderList = Lists.newArrayList(1, 2, 3, 4, 5); 150 | assertTrue(Ordering.natural().isStrictlyOrdered(orderList)); 151 | 152 | // 大于或等于不可通过 153 | orderList = Lists.newArrayList(1, 2, 2, 4, 5); 154 | assertFalse(Ordering.natural().isStrictlyOrdered(orderList)); 155 | } 156 | 157 | private void testSortedCopy() { 158 | List unorderList = Lists.newArrayList(5, 3, 2, 4, 1); 159 | List orderList = Lists.newArrayList(1, 2, 3, 4, 5); 160 | assertTrue(orderList.equals(Ordering.natural().sortedCopy(unorderList))); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/main/java/com/gtt/basicutilities/PreconditionsTest.java: -------------------------------------------------------------------------------- 1 | package com.gtt.basicutilities; 2 | 3 | import static com.google.common.base.Preconditions.*; 4 | import static org.junit.Assert.assertTrue; 5 | import static org.junit.Assert.fail; 6 | 7 | import java.util.ArrayList; 8 | 9 | import org.junit.Test; 10 | 11 | import com.google.common.collect.Lists; 12 | 13 | /** 14 | * 15 | * @author tiantiangao 16 | */ 17 | public class PreconditionsTest { 18 | 19 | @Test 20 | public void test() { 21 | testCheckArgument(); 22 | testCheckNotNull(); 23 | testCheckState(); 24 | testCheckElementIndex(); 25 | testCheckPositionIndex(); 26 | testCheckPositionIndexs(); 27 | } 28 | 29 | private void testCheckArgument() { 30 | int i = 1; 31 | checkArgument(i > 0, "参数是%s, 参数必须为正整数", i); 32 | 33 | try { 34 | i = -1; 35 | checkArgument(-1 > 0, "参数是%s, 参数必须为正整数", -1); 36 | fail(); 37 | } catch (IllegalArgumentException e) { 38 | assertTrue(true); 39 | } 40 | } 41 | 42 | private void testCheckNotNull() { 43 | Object value = new Object(); 44 | 45 | checkNotNull(value, "参数是null"); 46 | 47 | try { 48 | value = null; 49 | checkNotNull(value, "参数是null"); 50 | fail(); 51 | } catch (NullPointerException e) { 52 | assertTrue(true); 53 | } 54 | } 55 | 56 | private void testCheckState() { 57 | ArrayList list = Lists.newArrayList(1, 2, 3, 4, 5); 58 | checkState(list.size() < 6, "集体长度应该小于5"); 59 | 60 | list.add(6); 61 | try { 62 | checkState(list.size() < 6, "集体长度应该小于5"); 63 | fail(); 64 | } catch (Exception e) { 65 | assertTrue(true); 66 | } 67 | } 68 | 69 | private void testCheckElementIndex() { 70 | ArrayList list = Lists.newArrayList(1, 2, 3); 71 | // [0, size) 72 | checkElementIndex(list.size(), 4); 73 | 74 | try { 75 | checkElementIndex(list.size(), 3); 76 | fail(); 77 | } catch (Exception e) { 78 | assertTrue(true); 79 | } 80 | } 81 | 82 | private void testCheckPositionIndex() { 83 | ArrayList list = Lists.newArrayList(1, 2, 3); 84 | // [0, size] 85 | checkPositionIndex(list.size(), 3); 86 | 87 | try { 88 | checkPositionIndex(list.size(), 2); 89 | fail(); 90 | } catch (Exception e) { 91 | assertTrue(true); 92 | } 93 | } 94 | 95 | private void testCheckPositionIndexs() { 96 | ArrayList list = Lists.newArrayList(1, 2, 3, 4, 5); 97 | checkPositionIndexes(4, 5, list.size()); 98 | 99 | try { 100 | checkPositionIndexes(5, 6, list.size()); 101 | fail(); 102 | } catch (Exception e) { 103 | assertTrue(true); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/com/gtt/basicutilities/ThrowablesTest.java: -------------------------------------------------------------------------------- 1 | package com.gtt.basicutilities; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import java.io.FileNotFoundException; 6 | import java.io.InputStream; 7 | import java.net.URL; 8 | import java.util.Arrays; 9 | 10 | import org.junit.Test; 11 | 12 | import com.google.common.base.Throwables; 13 | 14 | /** 15 | * @author tiantiangao 16 | */ 17 | public class ThrowablesTest { 18 | 19 | @Test 20 | public void test() { 21 | testPropagate(); 22 | testPropagateIfInstanceOf(); 23 | testPropagateIfPossible(); 24 | testGetRootCause(); 25 | testGetStackTraceAsString(); 26 | testGetCausalChain(); 27 | } 28 | 29 | private void testPropagate() { 30 | try { 31 | URL url = new URL("http://www.dianping.com"); 32 | InputStream in = url.openStream(); 33 | in.close(); 34 | } catch (Exception e) { 35 | throw Throwables.propagate(e); 36 | } 37 | } 38 | 39 | private void testPropagateIfInstanceOf() { 40 | try { 41 | throw new NumberFormatException("a"); 42 | } catch (Throwable t) { 43 | try { 44 | Throwables.propagateIfInstanceOf(t, NumberFormatException.class); 45 | fail(); 46 | } catch (Throwable t2) { 47 | assertTrue(true); 48 | } 49 | } 50 | } 51 | 52 | private void testPropagateIfPossible() { 53 | try { 54 | throw new NumberFormatException(); 55 | } catch (Throwable t) { 56 | try { 57 | Throwables.propagateIfPossible(t, Exception.class); 58 | fail(); 59 | } catch (Throwable t1) { 60 | assertTrue(true); 61 | } 62 | } 63 | } 64 | 65 | private void testGetRootCause() { 66 | Exception e = new NumberFormatException("a"); 67 | assertEquals(e, Throwables.getRootCause(e)); 68 | 69 | IllegalArgumentException e2 = new IllegalArgumentException(e); 70 | assertEquals(e, Throwables.getRootCause(e2)); 71 | } 72 | 73 | private void testGetStackTraceAsString() { 74 | try { 75 | Integer.parseInt("a"); 76 | fail(); 77 | } catch (Exception e) { 78 | assertTrue(Throwables.getStackTraceAsString(e).startsWith( 79 | "java.lang.NumberFormatException: For input string: \"a\"")); 80 | } 81 | } 82 | 83 | private void testGetCausalChain() { 84 | FileNotFoundException fnfe = new FileNotFoundException(); 85 | IllegalArgumentException iae = new IllegalArgumentException(fnfe); 86 | RuntimeException re = new RuntimeException(iae); 87 | IllegalStateException ex = new IllegalStateException(re); 88 | 89 | assertEquals(Arrays.asList(ex, re, iae, fnfe), Throwables.getCausalChain(ex)); 90 | try { 91 | Throwables.getCausalChain(null); 92 | fail("Should have throw NPE"); 93 | } catch (NullPointerException expected) { 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/com/gtt/collections/CollectionUtilitiesTest.java: -------------------------------------------------------------------------------- 1 | package com.gtt.collections; 2 | 3 | import com.google.common.collect.Lists; 4 | import com.google.common.collect.Ordering; 5 | import com.google.common.collect.Sets; 6 | import org.junit.Test; 7 | 8 | /** 9 | * @author tiantiangao 10 | */ 11 | public class CollectionUtilitiesTest { 12 | 13 | @Test 14 | public void test() { 15 | testLists(); 16 | testSets(); 17 | } 18 | 19 | private void testLists() { 20 | Lists.newArrayList(); 21 | Lists.newArrayList(1, 2, 3); 22 | Lists.newArrayList(Sets.newHashSet(1, 2, 3)); 23 | Lists.newArrayListWithCapacity(10); 24 | Lists.newArrayListWithExpectedSize(10); 25 | 26 | Lists.newLinkedList(); 27 | Lists.newLinkedList(Sets.newHashSet(1, 2, 3)); 28 | 29 | Lists.partition(Lists.newArrayList(1, 2, 3, 4, 5), 2); 30 | Lists.reverse(Lists.newArrayList(1, 2, 3, 4, 5)); 31 | } 32 | 33 | private void testSets() { 34 | // 静态工厂方法 35 | Sets.newHashSet(); 36 | Sets.newHashSet(1, 2, 3); 37 | Sets.newHashSetWithExpectedSize(10); 38 | Sets.newHashSet(Lists.newArrayList(1, 2, 3)); 39 | 40 | Sets.newLinkedHashSet(); 41 | Sets.newLinkedHashSetWithExpectedSize(10); 42 | Sets.newLinkedHashSet(Lists.newArrayList(1, 2, 3)); 43 | 44 | Sets.newTreeSet(); 45 | Sets.newTreeSet(Lists.newArrayList(1, 2, 3)); 46 | Sets.newTreeSet(Ordering.natural()); 47 | 48 | // 集合运算(返回SetView) 49 | Sets.union(Sets.newHashSet(1, 2, 3), Sets.newHashSet(4, 5, 6)).toString(); // 取并集[1,2,3,4,5] 50 | Sets.intersection(Sets.newHashSet(1, 2, 3), Sets.newHashSet(3, 4, 5)); // 取交集[3] 51 | Sets.difference(Sets.newHashSet(1, 2, 3), Sets.newHashSet(3, 4, 5)); // 只在set1, 不在set2[1,2] 52 | Sets.symmetricDifference(Sets.newHashSet(1, 2, 3), Sets.newHashSet(3, 4, 5)); // 交集取反[1,2,4,5] 53 | 54 | // 其他工具方法 55 | Sets.cartesianProduct(Lists.newArrayList(Sets.newHashSet(1, 2), Sets.newHashSet(3, 4))); // 返回所有集合的笛卡尔积 56 | Sets.powerSet(Sets.newHashSet(1, 2, 3)); // 返回给定集合的所有子集 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/gtt/collections/ImmutableCollectionsTest.java: -------------------------------------------------------------------------------- 1 | package com.gtt.collections; 2 | 3 | import com.google.common.collect.*; 4 | import org.junit.Test; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | /** 13 | * @author tiantiangao 14 | */ 15 | public class ImmutableCollectionsTest { 16 | 17 | @Test 18 | public void test() { 19 | testJDKUnmodifiedList(); 20 | testCreate(); 21 | testAsList(); 22 | testImmutableXXX(); 23 | } 24 | 25 | private void testJDKUnmodifiedList() { 26 | List lists = Lists.newArrayList("aa", "bb", "cc"); 27 | 28 | List unmodifiedLists = Collections.unmodifiableList(lists); 29 | assertEquals(3, unmodifiedLists.size()); 30 | 31 | lists.add("dd"); 32 | assertEquals(4, unmodifiedLists.size()); 33 | } 34 | 35 | private void testCreate() { 36 | testCopyOf(); 37 | testOf(); 38 | testBuilder(); 39 | } 40 | 41 | private void testCopyOf() { 42 | ArrayList list = Lists.newArrayList(1, 2, 3); 43 | ImmutableList unmodifiedList = ImmutableList.copyOf(list); 44 | assertEquals(3, unmodifiedList.size()); 45 | 46 | list.add(4); 47 | assertEquals(3, unmodifiedList.size()); 48 | } 49 | 50 | private void testOf() { 51 | assertEquals(4, ImmutableList.of(1, 2, 3, 4).size()); 52 | assertEquals(4, ImmutableSet.of(1, 2, 3, 4).size()); 53 | assertEquals(4, ImmutableMap.of("aa", 1, "bb", 2, "cc", 3, "dd", 4).entrySet().size()); 54 | assertEquals(4, (Object) ImmutableMap.of("aa", 1, "bb", 2, "cc", 3, "dd", 4).get("dd")); 55 | } 56 | 57 | private void testBuilder() { 58 | ImmutableMap map = ImmutableMap.builder().put("aaa", 1).put("bbb", 2).put("ccc", 3).build(); 59 | assertEquals(3, map.size()); 60 | assertEquals(1, map.get("aaa")); 61 | assertEquals(2, map.get("bbb")); 62 | assertEquals(3, map.get("ccc")); 63 | } 64 | 65 | private void testAsList() { 66 | ImmutableSortedSet iset = ImmutableSortedSet.of(5, 2, 3, 4, 1); 67 | ImmutableList ilist = iset.asList(); 68 | 69 | List list = Lists.newArrayList(1, 2, 3, 4, 5); 70 | assertEquals(list, ilist); 71 | } 72 | 73 | private void testImmutableXXX() { 74 | assertEquals(5, ImmutableList.of(1, 2, 3, 4, 5).size()); 75 | assertEquals(5, ImmutableSet.of(1, 2, 3, 4, 5).size()); 76 | assertEquals(5, ImmutableSortedSet.of(1, 2, 3, 4, 5).size()); 77 | assertEquals(3, ImmutableMap.of(1, 2, 3, 4, 5, 6).size()); 78 | assertEquals(3, ImmutableSortedMap.of(1, 2, 3, 4, 5, 6).size()); 79 | assertEquals(9, ImmutableMultiset.of(1, 1, 2, 2, 3, 3, 4, 5, 6).size()); 80 | assertEquals(6, ImmutableMultiset.of(1, 1, 2, 2, 3, 3, 4, 5, 6).elementSet().size()); 81 | assertEquals(2, ImmutableMultiset.of(1, 1, 2, 2, 3, 3, 4, 5, 6).count(1)); 82 | } 83 | } 84 | --------------------------------------------------------------------------------