Map采坑与 ConHashMap

1. Key == null ? Value == null?
记忆:线程安全的Map:HashTable,concurrentHashMap,键值都不允许为null,hashMap不安全,键值可以为null。
在这里插入图片描述

2. 如果需要使用自定义对象做为 Map 集合的 key,那么一定记得重写hashCode 与 equals 方法
所以尽量避免使用自定义对象作为 Map 集合 key,如果一定要使用,记得重写 hashCode 与 equals 方法。另外还要保证这是一个不可变对象,即对象创建之后,无法再修改里面字段值。

1.7的ConcurrentHashMap

  • 使用分段锁的方式,是Segment+HashEntry的组合,HashEntry又是一个链表数组,所以1.7的conHashMap相当于是多个HashTable的组合,使用多个HashTable来提升并发性。

  • 在高并发下的情况下如何保证取得的元素是最新的:用于存储键值对数据的HashEntry,在设计上它的成员变量value跟next都是volatile类型的,这样就保证别的线程对value值的修改,get方法可以马上看到。

  • 这个size方法比较有趣,他是先无锁的统计下所有的数据量看下前后两次是否数据一样,如果一样则返回数据,如果不一样则要把全部的segment进行加锁,统计,解锁。并且size方法只是返回一个统计性的数字,因此size谨慎使用

1.8的ConcurrentHashMap

  • 取消了segment数组,直接用Node数组保存数据,锁的粒度更小,减少并发冲突的概率。采用table数组元素作为锁,从而实现了对每一行数据进行加锁,进一步减少并发冲突的概率,并发控制使用Synchronized和CAS来操作。

  • 存储数据时采用了数组+ 链表+红黑树的形式

  • 链表转为树的两个条件 : 64 + 8 , 树转为链表 6

假设table已经初始化完成,put操作采用 CAS + synchronized 实现并发插入或更新操作,具体实现如下。

  • 做一些边界处理,然后获得hash值。(key ,value 不能为null)
if (key  null || value  null) throw new NullPointerException(); //边界处理
  • 没初始化就初始化,初始化后看下对应的桶是否为空,为空就原子性的尝试插入。
if (tab  null || (n = tab.length)  0)
           tab = initTable(); // 初始化表 如果为空,懒汉式
           
if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null))) 
  • 如果当前节点正在扩容还要去帮忙扩容,骚操作。
else if ((fh = f.hash)  MOVED) 
       // 如果当前节点正在扩容。还要帮着去扩容。
           tab = helpTransfer(tab, f);
  • 用syn来加锁当前节点,然后操作几乎跟就跟hashmap一样了。
synchronized (f)  //  桶存在数据 加锁操作进行处理

// 链表尾插
for (Node<K,V> e = f;; ++binCount) {
                            K ek;
                            // 遍历链表去查找,如果找到key一样则选择性
                            if (e.hash  hash &&
                                ((ek = e.key)  key ||
                                 (ek != null && key.equals(ek)))) {
                                oldVal = e.val;
                                if (!onlyIfAbsent)
                                    e.val = value;
                                break;
                            }
                            Node<K,V> pred = e;
                            if ((e = e.next)  null) {// 找到尾部插入
                                pred.next = new Node<K,V>(hash, key,
                                                          value, null);
                                break;
                            }
                        }
                    }

// 红黑树插入
else if (f instanceof TreeBin) {// 如果桶节点类型为TreeBin
                        Node<K,V> p;
                        binCount = 2;
                        if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                       value)) != null) { 
                             // 尝试红黑树插入,同时也要防止节点本来就有,选择性覆盖
                            oldVal = p.val;
                            if (!onlyIfAbsent)
                                p.val = value;
                        }
                    }
  • 最后插完判断是否需要转成树和扩容
if (binCount != 0) { // 如果链表数量
                if (binCount >= TREEIFY_THRESHOLD)
                    treeifyBin(tab, i); //  链表转红黑树哦!
                if (oldVal != null)
                    return oldVal;
                break;
            }
        }
    }
    addCount(1L, binCount); // 统计大小 并且检查是否要扩容。
    return null;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MapStruct 是一个开源的 Java 对象映射框架,用于在不同的 Java 对象之间进行映射转换。虽然它在简化对象映射的过程中提供了很多便利,但在使用过程中也可能会遇到一些,以下是一些常见的问题和解决方法: 1. 循环引用:在对象之间存在循环引用时,MapStruct 可能会导致栈溢出或无限递归的问题。解决方法是使用 `@Context` 注解标记循环引用的属性,并使用 `@BeforeMapping` 或 `@AfterMapping` 注解提供自定义的处理逻辑。 2. 枚举映射:MapStruct 默认使用枚举的名称进行映射,但在某些情况下,可能需要根据枚举的其他属性进行映射。可以使用 `@ValueMapping` 注解和自定义的转换方法来实现。 3. 集合映射:当映射集合类型时,MapStruct 默认会创建一个新的集合对象,并将源集合中的元素逐个映射到目标集合中。但如果目标集合已经存在元素,则默认情况下会清空目标集合并重新添加元素。如果需要保留目标集合中的元素,可以在映射方法上使用 `@MappingTarget` 注解。 4. 嵌套映射:当对象之间存在嵌套关系时,MapStruct 默认会自动进行嵌套映射。但在某些情况下,可能需要手动控制嵌套映射的过程。可以使用 `@Mapping` 注解的 `target` 属性指定嵌套属性的映射目标。 5. 转换器生成:MapStruct 使用注解处理器生成转换器代码,但在某些情况下,可能由于编译顺序或其他原因导致转换器类未生成。可以检查编译配置和依赖项,确保 MapStruct 注解处理器正确配置。 这些是一些常见的 MapStruct 的及其解决方法,希望对你有所帮助。如果有其他具体问题,请告诉我。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值