引言:
对于这段代码,我们肯定是觉得有问题的,那么它的问题在那儿呢?如果换成:
又会有什么问题?
这段代码,java是如何操作的?初始化时,初始容量是多少? 本章内容就来聊聊这些事儿!
1、初始化方法:
第一种,使用int类型入参,构造一个capacity大小的数组。
第二种,使用无参的构造方法,设置了一个空的数组
private static final Object[]DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
同时初始化容量为10 private static final int DEFAULT_CAPACITY =10;
第三种,则是通过传入一个类型是当前类型派生类的集合。
这里先引申讲一下泛型。
1.1、泛型
1.1.1、这里先摆出几个事实:
①虚拟机中没有泛型,只有普通的类和方法。
(泛型擦除:无论何时定义一个泛型类型,都自动提供一个相应的原始类型。即擦除类型变量,替换为限定类型
如 Pair<T> 会替换为Pair<Object> ,Pair<T extends Comparable>会替换成Pair<Comparable>
特例Pair<T extends Serializable & Comparable> 会替换成Pair<Serializable>,为了提高效率,应该将标签接口(无方法接口)放在列表末尾)
②所有的类型参数都用它们的限定类型替换。
(当程序调用泛型方法,如果擦除返回类型,编译器自动插入强制类型转换)
③桥方法被合成来保持多态。
(泛型擦除与多态发生矛盾,即会产生两个方法:Object get(); Basic get();<直接这样编码,编译器会报错>但是在虚拟机中,用参数类型和返回类型确定一个方法,编译器可能产生两个返回类型不同的方法字节码,虚拟机能够正确处理。
一个方法覆盖另一个方法时,可以指定一个更严格的返回类型。合成的桥方法调用了新定义的方法,即
public void setSecond(Object second){setSecond((Date)second)})
④为保持类型安全性,必要时插入强制类型转换。
下面以一张图讲述区别
1.1.2、约束和局限性
①不能用基本数据类型实例化类型参数,例如List<int>。
②运行时类型查询只适用于原始类型。
③不能创建参数化类型数组,例如 List<Integer>[]。
④Varages警告 例如:@SafeVerargs <T> void addAll(Collection<T> col, T... ts) 仅限于最常见事例。
⑤不能实例化类型变量 ClassCastException 。
⑥不能构造泛型数组
⑦泛型类的静态上下文中类型变量无效
⑧不能捕获或抛出泛型类的实例
2、集合增长
2.1、集合增长首先看add方法。
所有的add方法都会调用一个private void ensureCapacityInternal(int minCapacity) 方法,minCapacity 是当前size+addnum。
如代码所示:如elementData是一个空数组Object[],比较默认容量(10)和minCapacity大小,此分支只会在第一次添加元素时进入。再modCount++(这是个隐含点),若minCapacity大于数组容量,则增长。
注意:int类型构造方法是this.elementData =new Object[initialCapacity]; 操作,直接设置数组大小。设置合适的容量,可避免频繁扩容。
如代码所示:获取数组长度(默认10),第一次扩容约1.5倍,如果不够,直接扩容到实际大小,如果还是不够。
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE -8;
@Native public static final int MAX_VALUE =0x7fffffff;
return (minCapacity >MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
只能扩到最大值-8这个区间,约21亿
3、移除元素
分两种情况,for循环移除和迭代器移除(即增强for循环)
3.1、for(int i = 0;i<arrayList.size(); i++) {arrayList.remove(i); },简称锯齿状,每次remove,数组都会重新构造。例如,删除index值为0的,删除后,index为1的值到了第一位,index值自增为2只想数据3。正确清空可while(arrayList.size()>0){arrayList.remove(0);} 或者arrayList.clear();
3.2、for (Integer a: arrayList) {arrayList.remove(a);} 与 for (int a: arrayList) {arrayList.remove(a);} 其实并没有区别。前一篇文章集合遍历有提到,使用增强for循环编译器会编译成迭代器方式。
它的方法都会调用一下checkForComodification(),
而所有的增、删操作都会自增modCount,导致抛出传说中的ConcurrentModificationException异常。而迭代器的remove操作会及时更新expectedModCount值,所以可迭代删除,即Iterator<Integer> iterable = arrayList.iterator();while(iterable.hasNext()) {iterable.next();iterable.remove();}。
总结:这篇文章主要总结了一下集合操作这块内容,顺带捋了一下泛型相关内容,很遗憾自己在整理这篇文章之前,就在面试中犯过错。
参考:
1、Java核心技术 卷一
2、Java API Docs
网友评论