Arrays.asList使用和lambda表达式复习 [改]
实际开发中遇到的问题,仅此记录
map中对Arrays.asList的结果集合进行操作无效。 知识点太久没复习,一不小心就掉咧。完整测试代码
一、出现问题
// 需求要删掉map中key值跟数组元素相同的数据。
Integer[] idsInt = {2, 3};
Map<Integer, String> map = HashMap<>();
map.put(1,"hello");
map.put(2,"world");
map.put(3,"go");
map.put(4,"ahead");
map.keySet().removeIf(v -> Arrays.asList(idsInt).contains(v));
// 最后结果应该只有key为1、4的数据。
// 然而结果是所有map原始数据(key1~key4)
二、定位问题
removeIf
这个方法是java8中新增。直接写死参数看结果:map.keySet().removeIf(v -> v==2);
确实key为2的数据被删了,确实是可以删除数据的,pass继续。
Arrays.asList
- 因为用到了lambda表达式,分析的时候有点卡-->lambda表达式复习
v -> Arrays.asList(idsInt).contains(v)
先来分析
->
,这是一个Predicate函数,执行test方法,返回boolean类型。所以完整的v -> Arrays.asList(idsInt).contains(v)
执行流程是:map.keySet().removeIf(new Predicate<Integer>() { @Override public boolean test(Integer v) { return Arrays.asList(idsInt).contains(v); } });
来看看
Arrays.asList
代码@SafeVarargs @SuppressWarnings("varargs") public static <T> List<T> asList(T... a) { return new ArrayList<>(a); }
返回了一个ArrayList数组集合,没毛病。
结合代码,如果key存在于该数组中,就返回true,返回true就执行删除操作。为什么没有删掉呢?
再退回来看map.keySet().removeIf
代码
default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) { // 在此处调用了lambda表达式,如果包含则返回true
each.remove();
removed = true;
}
}
return removed;
}
最终map.keySet().removeIf(v -> Arrays.asList(idsInt).contains(v));
的代码执行流程如下:
removeIf(Predicate<Integer> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<Integer> each = iterator();
while (each.hasNext()) {
// if(filter.test(each.next())) 等于
if (Arrays.asList(idsInt).contains(each.next())) {
// 这里调用了迭代器的remove方法对Arrays.asList的结果集进行了remove操作
// 迭代器使用的注意事项:不能调用集合的remove方法改变集合,否则会ConcurrentModificationException
each.remove();
removed = true;
}
}
return removed;
}
map.keySet().removeIf(Predicate..);
发现问题
- removeIf方法的if语句中
Arrays.asList
返回了一个数组集合,数组集合增删查改应该没问题才对。然鹅,当按住ctrl键点击该ArrayList
类时,并不是跳转到java.util.ArrayList
类!!! 而是跳转到
Arrays
私有的内部类(这个内部类紧接着asList方法,滑稽),其路径为:java.util.Arrays
中的ArrayList
内部类private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable { xxxx //并没有增删方法 }
- 因为该内部类继承了
List
的实现类AbstractList
,所以该内部类ArrayList有List所有定义的方法
可以调用,List中存在remove方法的定义,但是在ArrayList内部类中该定义没有被实现。所以就发生了删不了数据的情况!!
三、解决方法
原来的代码:map.keySet().removeIf(v -> Arrays.asList(idsInt).contains(v));
可修改为下面代码使用:
第一种方法:
map.keySet().removeIf(v -> {
for(int j=0;j<idsInt;j++)
if(j == v) return true;
return false;
});
- 把集合相关的操作都用for循环解决,避免使用
Arrays.asList
第二种方法
map.keySet().removeIf(v -> new ArrayList<Integer>(Arrays.asList(idsInt)).contains(v));
- 还是使用
Arrays.asList
,但是转换成了java.util.ArrayList
类来用
四、效率
long startTime = System.nanoTime();
// 代码(上面两种方法)
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println("use: " + duration / 1000000);
设置2000000条map数据,idsInt = {2000,13000,57000,1000000},每种方法都跑三遍,发现:
- for循环的最高时间为:270毫秒 且三次时间都在200~300毫秒内;
- 集合
Arrays.asList
最高为369秒,300~400毫秒内。
所以建议使用for循环来做。OVER~~~~,搞完睡觉