Java7和Java8中集合类差异
Java8在Java7的基础上对集合类进行了改进和优化,但是使用过程与Java7并没有什么不同,这一点是值的学习借鉴的。
1. 通用区别
Map、Set和List在Java8中均添加了forEach方法,该方法的入参是Consumer,一个函数式接口,可以简单理解为允许一个入参,但没有返回值的函数式接口。函数式接口会在以后的lambda表达式时候讲解。
@override
public void forEach(Consumer<? super E> action) {
// 判断非空
Objects.requireNonNull(action);
// modCount的原始值被拷贝,用于判断在方法执行过程中数组是否被修改
final int expectedModCount = modCount;
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
// 每次循环都会判断数组有没有被修改,一旦被修改,停止循环
for (int i=0; modCount == expectedModCount && i < size; i++) {
// 执行循环内容,action 代表要干的事情
action.accept(elementData[i]);
}
// 数组如果被修改了,抛异常
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
- action.accept的含义
action.accept就是在for循环中要做的事情,可以是任何事情,如
public void testForEach(){
List<Integer> list = new ArrayList<Integer>(){{
add(1);
add(3);
add(2);
add(4);
}};
// value 是每次循环的入参,就是 list 中的每个元素
list.forEach( value->log.info("当前值为:{}",value));
}
此时,action.accept就是括号中要打印的语句,执行结果为,
当前值为:1
当前值为:3
当前值为:2
当前值为:4
log.info(“当前值为:{}”,value)就是action。
- forEach方法上有@override标记,说明该方法是对父类方法的复写,该方法被定义在Iterable接口上。Java7和8中ArrayList都实现了该接口,但是在Java7中并没有实现该方法。源码中,该方法使用default关键字进行修饰,这个关键字值会出现在接口类中,被该关键字修饰的方法不会强制子类对其进行实现。
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
2.List区别
Java7在初始化List时,直接创建容量为10的数组。而Java8中,初始化时是空数组,在第一次调用add方法后才会对数组进行初始化。
其余地方没有修改。
3.Map区别
HashMap的区别
- 与List一样,在初始化时先不对底层数组进行初始化
- Java8的hash算法更加简单
- Java8在HashMap中引入了红黑树。如果使用Java的API,如String、Integer等作为key时,这些API的hashCode实现十分完美,很少出现链表转为红黑树的情况。只有当key是自定义的类且复写的hashCode方法十分糟糕时,大量的hash冲突会迫使链表转为红黑树,用于提升效率。
所以Java8的相当于重写了Java7的HashMap。 - 新增getOrDefault、putIfAbsent、compute和computeIfPresent方法。
public void compute(){
HashMap<Integer,Integer> map = Maps.newHashMap();
map.put(10,10);
log.info("compute 之前值为:{}",map.get(10));
map.compute(10,(key,value) -> key * value);
log.info("compute 之后值为:{}",map.get(10));
// 还原测试值
map.put(10,10);
// 如果为 11 的 key 不存在的话,需要注意 value 为空的情况,下面这行代码就会报空指针
map.compute(11,(key,value) -> key * value);
// 为了防止 key 不存在时导致的未知异常,我们一般有两种办法
// 1:自己判断空指针
map.compute(11,(key,value) -> null == value ? null : key * value);
// 2:computeIfPresent 方法里面判断
map.computeIfPresent(11,(key,value) -> key * value);
log.info("computeIfPresent 之后值为:{}",map.get(11));
}
这些新增的方法能够很好的解决空指针的问题。
LinkedHashMap
由于HashMap被重写,导致LinkedHashMap调用HashMap方法时的代码也进行了修改。