Java集合(list)
本文最后更新于223 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com

Arraylist 与 LinkedList 区别?

数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。

随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。

增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据的下标。

综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。

ArrayList 与 Vector 区别呢?为什么要用Arraylist取代Vector呢?

线程安全:Vector 使用了 Synchronized 来实现线程同步,是线程安全的,而 ArrayList 是非线程安全的。

性能:ArrayList 在性能方面要优于 Vector。

扩容:ArrayList 和 Vector 都会根据实际的需要动态的调整容量,只不过在 Vector 扩容每次会增加 1 倍,而 ArrayList 只会增加 50%。

说一说 ArrayList 的扩容机制吧?

ArrayList是List接口的实现类,它是支持根据需要而动态增长的数组。java中标准数组是定长的,在数组被创建之后,它们不能被加长或缩短。这就意味着在创建数组时需要知道数组的所需长度,但有时我们需要动态程序中获取数组长度。ArrayList就是为此而生的。

因此,了解它的扩容机制对使用它尤为重要。

ArrayList扩容发生在add()方法调用的时候,下面是add()方法的源码:

public boolean add(E e) {
  //扩容
   ensureCapacityInternal(size + 1);  // Increments modCount!!
   elementData[size++] = e;
   return true;
}

根据意思可以看出ensureCapacityInternal()是用来扩容的,形参为最小扩容量,进入此方法后:

private void ensureCapacityInternal(int minCapacity) {
   ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

通过方法calculateCapacity(elementData, minCapacity)获取:

private static int calculateCapacity(Object[] elementData, int minCapacity) {
   //如果传入的是个空数组则最小容量取默认容量与minCapacity之间的最大值
   if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
       return Math.max(DEFAULT_CAPACITY, minCapacity);
  }
   return minCapacity;
}

ensureExplicitCapacity方法可以判断是否需要扩容:

private void ensureExplicitCapacity(int minCapacity) {
   modCount++;

   // 如果最小需要空间比elementData的内存空间要大,则需要扩容
   if (minCapacity - elementData.length > 0)
       //扩容
       grow(minCapacity);
}

接下来重点来了,ArrayList扩容的关键方法grow():

private void grow(int minCapacity) {
   // 获取到ArrayList中elementData数组的内存空间长度
   int oldCapacity = elementData.length;
  // 扩容至原来的1.5倍
  int newCapacity = oldCapacity + (oldCapacity >> 1);
  // 再判断一下新数组的容量够不够,够了就直接使用这个长度创建新数组,
   // 不够就将数组长度设置为需要的长度
  if (newCapacity - minCapacity < 0)
      newCapacity = minCapacity;
  //若预设值大于默认的最大值检查是否溢出
  if (newCapacity - MAX_ARRAY_SIZE > 0)
      newCapacity = hugeCapacity(minCapacity);
  // 调用Arrays.copyOf方法将elementData数组指向新的内存空间时newCapacity的连续空间
  // 并将elementData的数据复制到新的内存空间
  elementData = Arrays.copyOf(elementData, newCapacity);//其实调用了System.arraycopy()
}

从此方法中我们可以清晰的看出其实ArrayList扩容的本质就是计算出新的扩容数组的size后实例化,并将原有数组内容复制到新数组中去。

1:ArrayList 无参数构造器构造,现在 add 一个值进去,此时数组的大小是多少,下一次扩容前最大可用大小是多少?

此处数组的大小是 1,下一次扩容前最大可用大小是 10,因为 ArrayList 第一次扩容时,是有默认值的,默认值是 10,在第一次 add 一个值进去时,数组的可用大小被扩容到 10 了。

2:如果我连续往 list 里面新增值,增加到第 11 个的时候,数组的大小是多少?

这里的考查点就是扩容的公式,当增加到 11 的时候,此时我们希望数组的大小为 11,但实际上数组的最大容量只有 10,不够了就需要扩容,扩容的公式是:oldCapacity + (oldCapacity>> 1),oldCapacity 表示数组现有大小,目前场景计算公式是:10 + 10 /2 = 15,然后我们发现 15 已经够用了,所以数组的大小会被扩容到 15。

3:数组初始化,被加入一个值后,如果我使用 addAll 方法,一下子加入 15 个值,那么最终数组的大小是多少?

第一题中我们已经计算出来数组在加入一个值后,实际大小是 1,最大可用大小是 10 ,现在需要一下子加入 15 个值,那我们期望数组的大小值就是 16,此时数组最大可用大小只有 10,明显不够,需要扩容,扩容后的大小是:10 + 10 /2 = 15,这时候发现扩容后的大小仍然不到我们期望的值 16,这时候源码中有一种策略如下:

// newCapacity 本次扩容的大小,minCapacity 我们期望的数组最小大小
// 如果扩容后的值 < 我们的期望值,我们的期望值就等于本次扩容的大小
if (newCapacity - minCapacity < 0)
   newCapacity = minCapacity;

所以最终数组扩容后的大小为 16。

请你说一说vector和list的区别

ArrayList

1、实现原理:采用动态对象数组实现,默认构造方法创建了一个空数组

2、第一次添加元素,扩展容量为10,之后的扩充算法:原来数组大小+原来数组的一半

3、当插入、删除位置比较靠前时,与链表比较,不适合进行删除或插入操作

4、为了防止数组动态扩充次数过多,建议创建ArrayList时,给定初始容量

5、多线程中使用不安全,适合在单线程访问时使用,效率较高

Vector

1、实现原理:采用动态数组对象实现,默认构造方法创建了一个大小为10的对象数组

2、扩充的算法:当增量为0时,扩充为原来的2倍,当增量大于0时,扩充为原来大小+增量

3、当插入、删除位置比较靠前时,与链表比较,不适合删除或插入操作

4、为了防止数组动态扩充次数过多,建议创建Vector时,给定初始容量

5、线程安全,适合在多线程访问时使用,效率较低

集合的使用注意: 若使用集合来存储多个不同类型的元素(对象),那么在处理时会比较麻烦 在实际开发中不建议这样使用,我们应该在一个集合中存储相同的类型对象

Array 和 ArrayList 有何区别?

Array 可以存储基本数据类型和对象,ArrayList 只能存储对象。

Array 是指定固定大小的,而 ArrayList 大小是自动扩展的。

Array 内置方法没有 ArrayList 多,比如 addAll、removeAll、iteration 等方法只有 ArrayList 有。

Iterator 和 ListIterator 有什么区别?

Iterator 可以遍历 Set 和 List 集合,而 ListIterator 只能遍历 List。

Iterator 只能单向遍历,而 ListIterator 可以双向遍历(向前/后遍历)。

ListIterator 从 Iterator 接口继承,然后添加了一些额外的功能,比如添加一个元素、替换一个元素、获取前面或后面元素的索引位置。

阐述ArrayList、Vector、LinkedList的存储性能和特性

1、ArrayList采用的是数组形式来保存对象的,这种方式将对象放在连续的位置中,所以最大的缺点就是插入删除时非常麻烦

2、LinkedList采用的将对象存放在独立的空间中,而且在每个空间中保存下一个链接的索引,但是缺点就是查找非常麻烦,要从第一个索引开始

3、ArrayList和Vector都是用数组方式存储数据,此数组元素数要大于实际的存储空间以便进行元素增加和插入操作,他们都允许直接用序号索引元素,但是插入数据元素涉及到元素移动等内存操作,所以索引数据快而插入数据慢

4、Vector使用了synchronized方法(线程安全),所以在性能上比ArrayList要差些

5、LinkedList使用双向链方式存储数据,按序号索引数据需要向前或向后遍历数据,索引索引数据慢,插入数据时只需要记录前后项即可,所以插入的速度快

LinkedList的实现原理总结如下:

①数据存储是基于双向链表实现的。

②插入数据很快。先是在双向链表中找到要插入节点的位置index,找到之后,再插入一个新节点。 双向链表查找index位置的节点时,有一个加速动作:若index < 双向链表长度的1/2,则从前向后查找; 否则,从后向前查找。

③删除数据很快。先是在双向链表中找到要插入节点的位置index,找到之后,进行如下操作:node.previous.next = node.next;node.next.previous = node.previous;node = null 。查找节点过程和插入一样。

④获取数据很慢,需要从Head节点进行查找。

⑤遍历数据很慢,每次获取数据都需要从头开始。

如果觉得本文对您有所帮助,那这将是对我最大的鼓励,期待您留下您宝贵的评论。
暂无评论

发送评论 编辑评论


				
上一篇