作为不是在逗比,就是在逗比路上的,拆家霸主
本汪今天就拆了Collectors.toConcurrentMap
看看他里面是怎么搞的
额,不了解函数式编程的小伙伴,推荐看下《java实战第二版》
看懂这篇博客,需要一定的函数式编程基础,起码得懂什么是行为参数化
不了解泛型的子类型规则,无限制的通配符类型的小伙伴,推荐以下博客
https://www.cnblogs.com/jian0110/p/10690483.html
一、开篇有益
行为参数化就是可以帮助你处理频繁变更的需求的一种软件开发模式。
本汪简单举个好理解的例子
1.我们定义一个对象,地球 -》作为一个参数对象; 执行 -》自转
2.我们定义一个对象,太阳 -》作为一个参数对象; 执行 -》发光发热
3.我们自定义一个对象,BloomFilter-》作为一个参数对象;执行 -》限流操作
4.我们定义另一个对象,MyLock -》 作为一个参数对象; 执行 -》锁升级,自旋 - 成功则升级/失败则重试
5.我们定义一个对象,字符串数组 -》 作为一个参数对象; 返回数组中非空的字符串
有没有一个方法,能被以上所有的案例使用呢?
JDK1.8以前做不到,JKD1.8以后可以了
想一下,我们可以写一个方法,把对象+行为
作为参数传入,eg:
public void used( 我是一个对象,什么类型的都可以Object object,我是一个行为,什么行为都行Active<T> active ) {
那么这里: 1.地球 + 自旋2.太阳 + 发光发热3.BloomFilter + 限流4.MyLock + 锁升级,自旋 - 成功则升级/失败则重试5.字符串数组 + 返回数组中非空的字符串}
这就是所谓的行为参数化了
现在写个简单的例子,定义一个used方法,使得她可以对同一个字符串数组,进行不同的操作,带大家看一下,她具体的代码实现
一下代码中,我们把“对象 和 具体行为操作”作为参数,传入方法used(对象,行为)
中
1、我们先定义一个函数式接口,ReturnBoolean<T>, 她可以接受一个对象,返回执行筛选操作后的对象,定义她的目的是为了对应Lambda表达式的解析
这儿本汪插一句,如果java.util.function下有相同功能的函数接口,大家就不要自己另定义了,这儿只是为了举例子(其实相当与Predicate <T )
@FunctionalInterface
public interface ReturnBoolean<T> {
boolean active(T t);
}
此处相当与上面的used()方法,List -》对象 ,ReturnBoolean<T> 行为(执行的操作)
public <T> List<T> filter(List<T> list ,ReturnBoolean<T> r) {
List<T> results = new ArrayList<>();for (T t : list) {
if (r.active(t)) {
results.add(t);}}return results;}
使用时,传入他的具体行为:
ReturnBoolean<String> r = (String i) -> !i.isEmpty();// 字符串数组为对象 行为:筛选数组中的非空字符串ReturnBoolean<String> r1 = (String i) -> i.contains("a");//字符串数组为对象 行为:筛选数组中包含“a”的字符串ReturnBoolean<Integer> i1 = (Integer i) -> i > 0;//Integer数组中,行为:筛选大于0 的数据ReturnBoolean<Car> u1 = (Car car) -> car.canRun();//List<Car>,行为:筛选还能跑的汽车List<String> strings = Arrays.asList("ss","sdfka","","abc");List<String> str = used(strings,r);
二、Collectors.toConcurrentMap源码解析
这儿本汪多句嘴
ConcurrentHashMap在JDK1.8做了大的变动,底层结构变为了数组+链表+红黑树,
内部也由Entry节点,变为了Node节点,
与HashTable对所有方法加Synchornized锁不同,她只会锁相关的node节点,这点不清楚,后面可能看不懂,
由于根据泊松分布,当默认负载因子为0.75时,单个Hash曹内存在8个元素的概率小于百万分之一,所以当元素数>=8时,链表->红黑树;<=6时,红黑树->链表; 7的时候不变动
嗯,这下差不多了,进入正题
public ConcurrentHashMap<String,Long> getList()
{
return LongStream.rangeClosed(1,10).boxed().collect(Collectors.toConcurrentMap(i -> "a", identity(),(o1,o2) -> o1,ConcurrentHashMap::new));}
我们进入Collectors.java,找到她
public static <T, K, U, M extends ConcurrentMap<K, U>>Collector<T, ?, M> toConcurrentMap(Function<? super T, ? extends K> keyMapper,Function<? super T, ? extends U> valueMapper,BinaryOperator<U> mergeFunction,Supplier<M> mapSupplier) {
BiConsumer<M, T> accumulator= (map, element) -> map.merge(keyMapper.apply(element),valueMapper.apply(element), mergeFunction);return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_CONCURRENT_ID);}
其实很简单:
Function<? super T, ? extends K> keyMapper
进行key的映射
Function<? super T, ? extends U> valueMapper
进行value的映射
BinaryOperator<U> mergeFunction
是一个双重操作,(o1,o2)-> o1,
本汪不放心:
所以还是说下吧,不然有的小伙伴真的在听天书
public int getNewObject(int o1,int o2){
return o1 > o2 ? o1 : o2;
}
->
传入两个相同类型的参数,返回一个返回值(也是相同的类型)
Supplier<M> mapSupplier
提供实现ConcurrentMap的具体实现类
进入map.merge(keyMapper.apply(element), valueMapper.apply(element), mergeFunction)继续查看
@Overridedefault V merge(K key, V value,BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);Objects.requireNonNull(value);V oldValue = get(key);//通过传入的key获取旧的value值for (;;) {
//如果可以从原Map中拿到value,把她看做是一个旧的value,if (oldValue != null) {
//有旧的value时,把她和我们新传进来的value,作比较//如果不相等,返回新的valueV newValue = remappingFunction.apply(oldValue, value);//如果返回了新的value,就调用ConcurrentHashMap中的replace方法进行置换,看到没,置换的是node节点哦!/*public boolean replace(K key, V oldValue, V newValue) {if (key == null || oldValue == null || newValue == null)throw new NullPointerException();return replaceNode(key, newValue, oldValue) != null;}*/if (newValue != null) {
if (replace(key, oldValue, newValue))return newValue;//如果无新值,或新值为null,返回null} else if (remove(key, oldValue)) {
return null;}oldValue = get(key);} else {
if ((oldValue = putIfAbsent(key, value)) == null) {
return value;}}}}
今天到这儿,先稍微了解一下她的实现,后面有时间,我们继续拆。。。。。。