18_Java集合ArrayList

2022/6/8 1:20:09

本文主要是介绍18_Java集合ArrayList,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

ArrayList 集合可以理解为 存储引用类型元素 的数据容器,元素类型不限,但可以通过 指定类型参数 限制可以存储的数据类型。

常用方法

  • 增添元素
// 在末尾添加元素
public boolean add(E e)
// 在指定位置插入元素
public void add(int index, E element)
  • 删除元素
// 删除指定元素,只删除第一个相同的元素
public boolean remove(Object o)
// 删除指定索引的元素
public E remove(int index)
// 删除所有元素
public void clear()
  • 修改元素
// 修改指定索引的元素
public E set(int index, E e)
  • 查找元素
// 顺序查找,返回元素索引位置
public int indexOf(Object o)
// 倒序查找,返回元素索引位置
public int lastIndexOf(Object o)
// 是否包含指定元素
public boolean contains(Object o)
  • 访问元素
// 访问指定索引处的元素
public E get(int index)
  • 获取元素个数
// 获取容器中的元素个数
public int size()

基本原理

transient Object[] elementData;  // 存放元素
private int size; // 记录存放的元素个数

protected transient int modCount = 0; // 父类 AbstractList 中,记录修改次数

ArrayList 是基于数组实现的,Object 数组 elementData 存放元素,变量 size 记录实际存放的元素个数,modCount 记录修改次数,每次发生结构性变化的时候 modCount 都会增加。

  • add 方法
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

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

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

private static int calculateCapacity(Object[] elementData, int minCapacity) {
  if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    return Math.max(DEFAULT_CAPACITY, minCapacity);
  }
  return minCapacity;
}

private void ensureExplicitCapacity(int minCapacity) {
  modCount++; // modCount 表示内部的修改次数
  
  // overflow-conscious code
  if (minCapacity - elementData.length > 0)
    grow(minCapacity);
}

private void grow(int minCapacity) {
  // overflow-conscious code
  int oldCapacity = elementData.length;
  int newCapacity = oldCapacity + (oldCapacity >> 1);
  if (newCapacity - minCapacity < 0)
    newCapacity = minCapacity;
  if (newCapacity - MAX_ARRAY_SIZE > 0)
    newCapacity = hugeCapacity(minCapacity);
  // minCapacity is usually close to size, so this is a win:
  elementData = Arrays.copyOf(elementData, newCapacity);
}

private static int hugeCapacity(int minCapacity) {
  if (minCapacity < 0) // overflow
    throw new OutOfMemoryError();
  return (minCapacity > MAX_ARRAY_SIZE) ?
    Integer.MAX_VALUE :
  MAX_ARRAY_SIZE;
}

迭代遍历

  • 普通循环遍历

可以使用 while、for 循环对 ArrayList 集合进行遍历。

  • foreach 迭代遍历
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
for (Integer ele: arrayList) {
  System.out.println(ele);
}

编译器会对 foreach 语法进行转换。

while(arrayList.hasNext()){
  System.out.println(arrayList.next());
}

ArrayList 实现了 Iterable 接口,Iterable 表示可迭代,只要实现了 Iterable 接口,就可以使用 foreach 语法。

public interface Iterable<T> {
    Iterator<T> iterator();
}

iterator 方法返回一个实现了 Iterator 接口的对象。

public interface Iterator<E> {
    boolean hasNext(); // 判断是否还有元素可访问
  
    E next(); // 返回下一个元素
  
    default void remove() { // 删除最后返回的元素
        throw new UnsupportedOperationException("remove");
    }
}

来看 ArrayList 中的 iterator:

public Iterator<E> iterator() {
  return new Itr();
}
// Itr 是内部类
private class Itr implements Iterator<E> {
  int cursor;       // index of next element to return
  int lastRet = -1; // index of last element returned; -1 if no such
  int expectedModCount = modCount; // 每次发生结构性变化的时候 modCount 都会增加

  Itr() {}

  public boolean hasNext() {
    return cursor != size;
  }

  @SuppressWarnings("unchecked")
  public E next() {
    checkForComodification();
    int i = cursor;
    if (i >= size)
      throw new NoSuchElementException();
    Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length)
      throw new ConcurrentModificationException();
    cursor = i + 1;
    return (E) elementData[lastRet = i];
  }

  public void remove() {
    if (lastRet < 0)
      throw new IllegalStateException();
    checkForComodification();

    try {
      ArrayList.this.remove(lastRet);
      cursor = lastRet;
      lastRet = -1;
      expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
      throw new ConcurrentModificationException();
    }
  }

  final void checkForComodification() {
    if (modCount != expectedModCount)
      throw new ConcurrentModificationException();
  }
}

注意迭代中的删除:

/**
 * foreach 遍历时不能直接调用 remove 方法进行删除
 * 因为 foreach 实际是调用 Iterator 的方法,迭代器遍历时会维护索引位置相关的数据
 * 直接 remove 会导致结构变化,导致索引数据失效
 */
for (Integer ele: arrayList) {
  if(ele == 2) arrayList.remove(ele); // 执行报错
  System.out.println(ele);
}

使用迭代器删除:

Iterator<Integer> it = arrayList.iterator();
while (it.hasNext()){
  if(it.next() == 2) it.remove();
}

实现接口

  • Collection

Collection 表示一个数据集合,数据没有位置或顺序的概念。

  • List

List 表示有顺序或位置的数据集合,主要方法都与位置有关。

  • RandomAccess

标记接口,RandomAccess 表示可以随机访问,数据在内存是连续存放的,根据索引值可以直接定位到具体的元素,访问效率很高。

主要特点

  • 只能存放引用类型数据
  • 底层结构是数组,增删慢,查询相对较快,数据在内存连续存放,可以根据索引随机快速访问
  • 线程不安全


这篇关于18_Java集合ArrayList的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程