ArrayList与顺序表

线性表linear listn个具有相同特性的数据元素的有限序列。  线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列...

线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物  理上存储时,通常以数组和链式结构的形式存储。

ArrayList与顺序表_第1张图片

  1. 顺序表

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成   数据的增删查改。

    1. 接口的实现

ArrayList与顺序表_第2张图片

ArrayList与顺序表_第3张图片

  1. ArrayList简介

在集合框架中,ArrayList是一个普通的类,实现了List接口,具体框架图如下:

ArrayList与顺序表_第4张图片

说明

  1. ArrayList实现了RandomAccess接口,表明ArrayList支持随机访问
  2. ArrayList实现了Cloneable接口,表明ArrayList是可以clone
  3. ArrayList实现了Serializable接口,表明ArrayList是支持序列化的
  4. Vector不同,ArrayList不是线程安全的,在单线程下可以使用,在多线程中可以选择Vector或者

CopyOnWriteArrayList

  1. ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表
  1. ArrayList使用
    1. ArrayList的构造

方法

解释

ArrayList()

无参构造

ArrayList(Collection c)

利用其他 Collection 构建 ArrayList

ArrayList(int initialCapacity)

指定顺序表初始容量

ArrayList与顺序表_第5张图片

    1. ArrayList常见操作

ArrayList虽然提供的方法比较多,但是常用方法如下所示,需要用到其他方法时,同学们自行查看ArrayList的帮助    文档。

方法

解释

boolean add(E e)

尾插 e

void add(int index, E element)

e 插入到 index 位置

boolean addAll(Collection c)

尾插 c 中的元素

E remove(int index)

删除 index 位置元素

boolean remove(Object o)

删除遇到的第一个 o

E get(int index)

获取下标 index 位置元素

E set(int index, E element)

将下标 index 位置元素设置为 element

void clear()

清空

boolean contains(Object o)

判断 o 是否在线性表中

int indexOf(Object o)

返回第一个 o 所在下标

int lastIndexOf(Object o)

返回最后一个 o 的下标

List subList(int fromIndex, int toIndex)

截取部分 list

ArrayList与顺序表_第6张图片

ArrayList与顺序表_第7张图片

    1. ArrayList的遍历

ArrayList 可以使用三方方式遍历:for循环+下标、foreach、使用迭代器

ArrayList与顺序表_第8张图片

    1. ArrayList的扩容机制

下面代码有缺陷吗?为什么?

ArrayList与顺序表_第9张图片

ArrayList是一个动态类型的顺序表,即:在插入元素的过程中会自动扩容:以下是ArrayList源码中扩容方式

ArrayList与顺序表_第10张图片

Object[] elementData; // 存放元素的空间

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 默认空间

private static final int DEFAULT_CAPACITY = 10; // 默认容量大小

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++;

// overflow-conscious code

if (minCapacity - elementData.length > 0) grow(minCapacity);

}

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private void grow(int minCapacity) {

// 获取旧空间大小

int oldCapacity = elementData.length;

// 预计按照1.5倍方式扩容

int newCapacity = oldCapacity + (oldCapacity >> 1);

// 如果用户需要扩容大小 超过 原空间1.5倍,按照用户所需大小扩容

if (newCapacity - minCapacity < 0) newCapacity = minCapacity;

// 如果需要扩容大小超过MAX_ARRAY_SIZE,重新计算容量大小

if (newCapacity - MAX_ARRAY_SIZE > 0)

ArrayList与顺序表_第11张图片

总结

      1. 检测是否真正需要扩容,如果是调用grow准备扩容
      2. 预估需要库容的大小

ArrayList与顺序表_第12张图片

初步预估按照1.5倍大小扩容

ArrayList与顺序表_第13张图片

ArrayList与顺序表_第14张图片

如果用户所需大小超过预估1.5倍大小,则按照用户所需大小扩容真正扩容之前检测是否能扩容成功,防止太大导致扩容失败

      1. 使用copyOf进行扩容
  1. 使用示例
    1. 扑克牌

ArrayList与顺序表_第15张图片

ArrayList与顺序表_第16张图片

ArrayList与顺序表_第17张图片

Card card = new Card(); card.rank = rank; card.suit = suit;

deck.add(card);

}

}

return deck;

}

private static void swap(List<Card> deck, int i, int j) { Card t = deck.get(i);

deck.set(i, deck.get(j)); deck.set(j, t);

}

private static void shuffle(List<Card> deck) { Random random = new Random(20190905); for (int i = deck.size() - 1; i > 0; i--) {

int r = random.nextInt(i); swap(deck, i, r);

}

}

public static void main(String[] args) { List<Card> deck = buyDeck(); System.out.println("刚买回来的牌:"); System.out.println(deck); shuffle(deck);

System.out.println("洗过的牌:");

System.out.println(deck);

// 三个人,每个人轮流抓 5 张牌List<List<Card>> hands = new ArrayList<>(); hands.add(new ArrayList<>()); hands.add(new ArrayList<>()); hands.add(new ArrayList<>());

for (int i = 0; i < 5; i++) { for (int j = 0; j < 3; j++) {

hands.get(j).add(deck.remove(0));

}

}

System.out.println("剩余的牌:"); System.out.println(deck); System.out.println("A 手中的牌:"); System.out.println(hands.get(0)); System.out.println("B 手中的牌:"); System.out.println(hands.get(1)); System.out.println("C 手中的牌:"); System.out.println(hands.get(2));

}

ArrayList与顺序表_第18张图片

运行结果

ArrayList与顺序表_第19张图片

    1. 杨辉三角

杨辉三角

  1. 顺序表的问题及思考
  1. 顺序表中间/头部的插入删除,时间复杂度为O(N)
  2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
  3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继  续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

思考: 如何解决以上问题呢?

你可能感兴趣的:(java,intellij-idea)