记录java 在遍历中删除元素 以及 mysql5.6版本添加unique失败

遍历中删除

List或Queue等数据结构中,如何一边遍历一遍删除?

1. 常犯错误

ArrayList

可能没遇到坑过的人会用增强for循环这么写:

public static void main(String[] args) {
1 List<Integer> list = new ArrayList<>();
2    list.add(1);
3    list.add(2);
4    list.add(3);
5
6    for (Integer value : list) {
7        if (value.equals(2)) {
8            list.remove(value);
9        }
10    }
11    System.out.println(list);
}

但是一运行,结果却抛 java.util.ConcurrentModificationException 异常
即并发修改异常

image.png

简单看看异常是怎么产生的:

首先,从抛出异常的位置入手,发现原因是因为: modCount 的值不等于 expectedModCount

image.png

modCount值是什么呢?

The number of times this list has been structurally modified. Structural modifications are those that change the size of the list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.

从源码注释中可以看出来,表示的是 修改该表结构的次数, 包括修改列表大小等。

所以原因很简单了: 刚开始循环时 modCount 与期待值相等

image.png

但是在循环中进行了删除操作后,modeCount 进行了自增。
image.png

最后由于 modCount 的值不等于 expectedModCount,抛出异常。

HashMap:

HashMap中也同理, 如果我们简单使用forEach,然后remove。同样报
java.util.ConcurrentModificationException

1    HashMap<Integer,String> hashMap = new HashMap<>();
2    hashMap.put(1, "张三");
3    hashMap.put(2, "李四");
4    hashMap.put(3, "王五");
5    hashMap.forEach((key, value) -> {
6        logger.info("当前值: key" + key +  " value " + value) ;
7        if ((hashMap.get(key) ==  "李四")) {
8            hashMap.remove(key);
9        }
10    });
11    logger.info(hashMap.toString());

image.png

原因也一样: 在 modCount 与期待值不一样, 发生了并发修改异常

image.png

Queue

我们再来看看队列

1    Queue<Integer> queue = new LinkedList<>();
2    queue.offer(1);
3    queue.offer(2);
4    queue.offer(3);
5
6    logger.info("删除前" + queue.toString());
7    for(Integer value : queue) {
8        logger.info("当前值" + value);
9        if (value.equals(2)) {
11           queue.remove(value);
12      }
13    }
14    logger.info("删除后" + queue.toString());

如果我们用 LinkList 结构作为队列,可以成功删除。

但是我们按理来说循环3次, 实际上只循环2次。如下图。 所以可能会导致漏判断。(有空在进行研究)

image.png

如果我们把 LinkList 换成 一些功能性比较好的队列,就不会有这种情况。 比如 带有ReentrantLock 锁的LinkedBlockingQueue。

 Queue<Integer> queue = new LinkedBlockingQueue<>();

就能正常循环3次, 并正常删除。
image.png

迭代器遍历

那么应该使用哪些遍历呢?

比较推荐使用Iterator迭代器进行遍历:
使用它的 iterator.remove()方法不会产生并发修改错误。

1 List<Integer> list = new ArrayList<>();
2        list.add(1);
3        list.add(2);
4        list.add(3);
5        Iterator<Integer> iterator = list.iterator();
6        while (iterator.hasNext()) {
7            Integer integer = iterator.next();
8            if (integer == 2) {
9                logger.info("删除" + integer);
10                iterator.remove();
11            }
12        }
13        System.out.println(list);

原因可以从源码中找到: 每次删除一个元素,都会将 modCount 的值重新赋值给expectedModCount,这样2个变量就相等了,不会触发异常。

image.png

同时好处还有一个,无论是什么结构的数据,一般都可以用迭代器访问。

从JDK1.8开始,可以使用removeIf()方法来代替 Iterator的remove()方法实现一边遍历一边删除,IDEA中也会提示:
其原理也是使用迭代器的remove。
image.png

mysql5.6版本添加unique失败

项目使用jpa自动生成数据库, 添加unique字段的时候,发现报错:

image.png

Specified key was too long; max key length is 767 bytes

反复测试后,发现,Long对象的 unique 可以添加成功,String 对象的不行,然后我把 String 对象加上length限制

@Column(unique = true, nullable = false, length = 20)
private String acctName;

结果就成功了。

原因:

数据库查看:

image.png

原来是因为指定字段长度过长。未指定长度的字符串,默认为255 varchar,utf8mb4字符集每个 varchar 为4bytes,即为总长255x4=1020bytes,大于了767bytes。

因此,unique 字段的最大长度为767/4 = 191 varchar。(注:utf8mb4字符集)

MySQL 5.6 版(及之前的版本)中的 767 字节是 InnoDB 表的规定前缀限制。在 MySQL 5.7 版(及更高版本)中,此限制已增加到3072 字节。

可以选择升级版本,或者设置长度
image.png

583 声望
100 粉丝
0 条评论
推荐阅读
浅谈前端的状态管理,以及anguar的状态管理库
在前端技术中, 状态管理可以帮助你管理“全局”状态 - 那些应用程序的许多部分都需要的状态。比如组件之间共享的数据、页面需要及时更新的数据等等。

weiweiyi阅读 253

使用springboot+angular实现web端微信扫码登陆
现在微信的使用用户越来越多,如果网站添加上微信登录,就能节省很多用户注册时间,极大缩小了注册流程。会让用户觉得特别方便。接下来我们就说一下怎么来实现Web端微信扫码登录。

郝泽龙_HZ6阅读 910

利用Docker部署管理LDAP及其初次使用
前言:本周主要写了gitlabWebhook转github的项目,总体上没有遇到什么大问题,这周接触到了LDAP,于是就花时间实际操作了解了一下。

李明5阅读 1.2k

记录本周问题
项目里两个地方都用到了hashmap。但是感觉自己用的时候并没有感觉非常的清晰。同时发现hashmap有线程不安全问题,而自己用的时候就是多线程来使用。于是在这里介绍一下。

weiweiyi5阅读 815

Spring Aop 动态代理
为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施...

KerryWu5阅读 8.2k评论 1

记录多项目共用一个公众号逻辑修改
前言微信扫码登陆,前段时间写完微信扫码登录后,由于有多个项目都需要微信登录,而公众号的数量有限。 所以需要研究一下多个项目使用同一个公众号登录。思路原来的思路: 每个后台与微信服务器之间进行通信, 需要...

weiweiyi4阅读 784评论 1

583 声望
100 粉丝
宣传栏