加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

JDK1.8、JDK1.7、JDK1.6区别看这里

发布时间:2020-05-23 13:31:03 所属栏目:Java 来源:互联网
导读:这一篇开始说ArrayList参考代码为jdk1.6_45jdk1.7_80jdk1.8_111中的源码,对比阅读,发现修改的问题以及改进点。

这一篇开始说ArrayList

参考代码为jdk1.6_45 jdk1.7_80 jdk1.8_111中的源码,对比阅读,发现修改的问题以及改进点。

public class ArrayList<E> extends AbstractList<E> implements List<E>,RandomAccess,Cloneable,java.io.Serializable

一、基本性质

1、底层使用原生数组实现,实现RandomAccess接口,可以随机访问,随机访问指的是下标索引操作index(i)的时间复杂度是O(1)。

size、isEmpty、get、set、iterator和listIterator操作在O(1)内完成,add(e)操作平均在O(1)内完成,即添加n个元素需要O(n)时间(这个是Collection.add,是在尾部添加注意区分下List.add(index,e))。其他操作基本都是O(n)内完成。ArrayList与LinkedList实现相比,O(n)的各个方法的时间复杂度的常数因子更小。

2、因为底层数组 elementData 的容量是不能改变的,所以容量不够时,需要把 elementData 换成一个更大的数组,这个过程叫作扩容。实际的元素的数量size,总是不会超过底层数组的容量 elementData.length,因为扩容需要申请更大的内存,并且需要原来数组的进行一次复制,所以扩容是个耗时的操作。在添加大量元素之前,使用者最好是预估一个大致的数量,手动调用ensureCapacity进行一次扩容操作,避免一个个添加导致频繁扩容影响性能。

3、ArrayList是未同步的,多线程并发读写时需要外部同步,如果不外部同步,那么可以使用Collections.synchronizedList方法对ArrayList的实例进行一次封装,或者使用Vector。

4、对存储的元素无限制,允许null元素。

5、ArrayList的iterator和listIterator方法返回的迭代器是快速失败的,也就是如果在创建迭代器之后的任何时间被结构性修改,除了通过迭代器自己的remove或add方法之外,迭代器将直接抛出一个ConcurrentModificationException,从而达到快速失败fail-fast的目的,尽量避免不确定的行为。

ArrayList的迭代器的快速失败行为不能被严格保证,并发修改时它会尽量但不100%保证抛出ConcurrentModificationException。因此,依赖于此异常的代码的正确性是没有保障的,迭代器的快速失败行为应该仅用于检测bug。

6、实现clone接口,可以调用其clone方法(虽然clone()是Object中的方法,但是它是protected,使用子类的clone()必须在子类中覆盖此方法)。clone方法复制一个ArrayList,底层数组elementData不共享,但是实际的元素还是共享的。
不过clone是ArrayList中覆盖的,不属于List中的方法,因此常见的声明形式
     List<String> strs = new ArrayList<>();
声明出来的变量不能直接使用clone方法,本身也用得极少。

7、实现Serializable接口,可以被序列化。ArrayList"实现"了自定义序列化方法,这么做主要是为了节省空间 。对于占用空间的大头――元素list,仅仅序列化实际size大小的元素,同时不序列化对于新对象无用属性的――来自父类AbstractList的modCount。ArrayList的实际size不会超过底层数组的length,大多数情况下比底层数组length小,使用默认序列化的话,会直接序列化整个底层数组,序列化后字节流会变大,浪费空间。

二、构造方法

1、默认构造方法,ArrayList()

关于默认构造方法,你可能在别的地方看见过这种话:无参构造方法(默认构造方法)构造的ArrayList的底层数组elementData大小(容量)默认为10。这里告诉你,这不一定是对的。这句话在1.6版本中是对的(更之前的版本我没看),从1.7开始这句话就有问题了。下面我贴出了三个版本的代码:
jdk1.6的,初始化成10个容量。

// jdk1.6的 
/** Constructs an empty list with an initial capacity of ten. */ 
 public ArrayList() { 
 this(10); 
} 

jdk1.7的,相对1.6版本,引入了一个新的常量EMPTY_ELEMENTDATA,它是一个空数组,因此容量为0。

// jdk1.7的 
/** Shared empty array instance used for empty instances. */ 
private static final Object[] EMPTY_ELEMENTDATA = {}; 
 
... 
 
/** Constructs an empty list with an initial capacity of ten. */ 
public ArrayList() { 
 super(); 
 this.elementData = EMPTY_ELEMENTDATA; 
} 

jdk1.8的,相对1.7版本,又引入了一个新的常量DEFAULTCAPACITY_EMPTY_ELEMENTDATA ,它也是一个空数组,因此容量也为0。至于两个空数组有什么区别,看下面一点说的。

/** Shared empty array instance used for empty instances. */ 
private static final Object[] EMPTY_ELEMENTDATA = {}; 
 
/** 
 * Shared empty array instance used for default sized empty instances. We 
 * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when 
 * first element is added. 
 */ 
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; 
 
... 
 
/** Constructs an empty list with an initial capacity of ten. */ 
public ArrayList() { 
 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; 
} 

对比下可以看出:jdk1.6的无参构造方法(默认构造方法)构造的ArrayList的底层数组elementData大小(容量)默认为10;从1.7开始,无参构造方法构造的ArrayList的底层数组elementData大小默认为0。
java集合类在jdk1.7版本基本上都有一种改动:懒初始化。懒初始化指的是默认构造方法构造的集合类,占据尽可能少的内存空间(对于ArrayList来说,使用空数组来占据尽量少的空间,不使用null是为了避免null判断),在第一次进行包含有添加语义的操作时,才进行真正的初始化工作。

1.7开始的ArrayList,默认构造方法构造的实例,底层数组是空数组,容量为0,在进行第一次add/addAll等操作时才会真正给底层数组赋非empty的值。如果add/addAll添加的元素小于10,则把elementData数组扩容为10个元素大小,否则使用刚好合适的大小(例如,第一次addAll添加6个,那么扩容为10个,第一次添加大于10个的,比如24个,扩容为24个,刚好合适);1.8版本,默认构造的实例这个行为没有改变,只是用的数组名字变了。

顺便吐槽下:jdk这个类维护者,你能不能改下默认构造方法上的注释啊,默认构造方法的行为都改变了,你注释还是用之前的!!!

2、带初始容量的构造方法,public ArrayList(int initialCapacity)

// 1.6 
public ArrayList(int initialCapacity) { 
 super(); 
 if (initialCapacity < 0) 
  throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); 
 this.elementData = new Object[initialCapacity]; 
} 
 
// 1.7 跟1.6的一样 
public ArrayList(int initialCapacity) { 
 super(); 
 if (initialCapacity < 0) 
  throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); 
 this.elementData = new Object[initialCapacity]; 
} 
 
// 1.8 
public ArrayList(int initialCapacity) { 
 if (initialCapacity > 0) { 
  this.elementData = new Object[initialCapacity]; 
 } else if (initialCapacity == 0) { 
  this.elementData = EMPTY_ELEMENTDATA; // 重用空数组,一个小小的优化 
 } else { 
  throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); 
 } 
} 

678三个版本的这个构造方法的实际行为基本一致:如果initialCapacity >= 0,把底层数组elementData赋值为一个大小为initialCapacity的数组,数组的所有元素都是默认值null。1.8稍微进行了一点优化,也是赋值为空数组,但是重用了常量对象。
下面写个简单的例子看一个细微的区别。

// jdk1.6,两个构造的区别很明显 
public class TestArrayList { 
 public static void main(String[] args) { 
  List<String> la = new ArrayList<String>(0); // la.elementData = new Object[0],la.elementData.length = 0 
  la.add("111"); // la.elementDate.length = 1,这里一次性扩容了1个,后续再按照通用扩容策略执行扩容操作 
 
  List<String> lb = new ArrayList<String>(); // lb.elementData = new Object[10],lb.elementData.length = 10 
  lb.add("111"); // lb.elementDate.length = 10,这里没有进行扩容,后续再按照通用扩容策略执行扩容操作 
 } 
} 
 
// jdk1.7,两个构造在第一次进行添加时才看得出区别 
public class TestArrayList { 
 public static void main(String[] args) { 
  List<String> la = new ArrayList<>(0); // la.elementData = new Object[0],la.elementData.length = 0 
  la.add("111"); // la.elementDate.length = 1,这里一次性扩容了1个,后续再按照通用扩容策略执行扩容操作 
 
  List<String> lb = new ArrayList<>(); // lb.elementData = EMPTY_ELEMENTDATA,lb.elementData.length = 0 
  lb.add("111"); // lb.elementDate.length = 10,这里一次性扩容了10个,后续再按照通用扩容策略执行扩容操作 
 } 
} 
 
// jdk1.8,同1.7 
public class TestArrayList { 
 public static void main(String[] args) { 
  List<String> la = new ArrayList<>(0); // la.elementData = EMPTY_ELEMENTDATA,la.elementData.length = 0 
  la.add("111"); // la.elementDate.length = 1,这里一次性扩容了1个,后续再按照通用扩容策略执行扩容操作 
 
  List<String> lb = new ArrayList<>(); // lb.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA,lb.elementData.length = 0 
  lb.add("111"); // lb.elementDate.length = 10,这里一次性扩容了10个,后续再按照通用扩容策略执行扩容操作 
 } 
} 

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读