目录
4、add(int pos, int data)在 pos 位置新增元素
5、contains(int toFind)判定顺序表是否包含某个元素
6、indexOf(int toFind)查找某个元素对应的位置
8、set(int pos, int value)给 pos 位置的元素设为 value
9、remove(int key)删除第一次出现的关键字key
10、isEmpty()/isFull()判断顺序表是否空/满
ArrayList构造
方法 | 解释 |
无参构造
| |
利用其他
Collection
构建
ArrayList
| |
指定顺序表初始容量
|
public static void main(String[] args) {
// ArrayList创建,推荐写法
// 构造一个空的列表
List<Integer> list1 = new ArrayList<>();
// 构造一个具有10个容量的列表
List<Integer> list2 = new ArrayList<>(10);
list2.add(1);
list2.add(2);
list2.add(3);
// list2.add("hello"); // 编译失败,List<Integer>已经限定了,list2中只能存储整形元素
// list3构造好之后,与list中的元素一致
ArrayList<Integer> list3 = new ArrayList<>(list2);
// 避免省略类型,否则:任意类型的元素都可以存放,使用时将是一场灾难
List list4 = new ArrayList();
list4.add("111");
list4.add(100);
}
ArrayList动态扩容机制
ArrayList的主要属性
如果不指定容量(空构造器),则在添加数据时的空构造器默认初始容量最小为 10
private static final int DEFAULT_CAPACITY = 10;
出现在需要用到空数组的地方,其中一处是使用自定义初始容量构造方法时候如果你指定初始容量为0的时候,那么elementData指向该数组
另一处是使用包含指定collection集合元素的列表的构造方法时,如果被包含的列表中没有数据,那么elementData指向该数组
private static final Object[] EMPTY_ELEMENTDATA = {};
如果使用默认构造方法,那么elementData指向该数组
在添加元素时会判断是否是使用默认构造器第一次添加,如果是数组就会扩容至10个容量
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
默认未初始化的储存 ArrayList集合元素的底层数组,其长度就是 ArrayList的容量
transient Object[] elementData;
私有的elementData数组中具体的元素对象的数量,可通过size方法获得。默认初始值为0,在add、remove等方法时size会改变
private int size;
-
可以看到
ArrayList
底层核心是一个Object[] elementData
的数组
ArrayList的构造器
// 默认的构造器
public ArrayList() {
// Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {} 空数组
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 自定义容量大小的构造器
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);
}
}
从上面的构造器中,可以得出以下结论
- 如果使用
ArrayList
的默认构造器时,它的初始容量就是0
- 如果使用
ArrayList
的有参构造器时,它的初始容量就是你传入的参数initialCapacity
的值
ArrayList的动态扩容
根据上面的两个结论,不管是使用默认或有参构造器时,我们可以使其初始容量为 0
,那么它的动态扩容发生在哪里?ArrayList源码中扩容方式
public boolean add(E e) {
// ensureCapacityInternal() 如下
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
// 第一次 add 时,size肯定是0,所以参数 minCapacity = 1
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// calculateCapacity() 方法
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 如果是第一次 add 元素
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// minCapacity 设置为 DEFAULT_CAPACITY 与 minCapacity 的最大值
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
// ensureExplicitCapacity() 方法
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// 原容量
int oldCapacity = elementData.length;
// 扩容后的容量,扩大到原容量的 1.5 倍左右,右移一位相当于原数值除以 2 的商
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果新容量减去最小容量的值小于 0
if (newCapacity - minCapacity < 0)
// 新容量等于最小容量
newCapacity = minCapacity;
// 如果新容量减去建议最大容量的值大于 0
if (newCapacity - MAX_ARRAY_SIZE > 0)
// 调整新容量上限或者抛出 OutOfMemoryError
newCapacity = hugeCapacity(minCapacity);
// 最终进行新数组的构建和重新赋值,此后原数组被摒弃
// elementData:原数组; newCapacity:新数组容量
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
// 如果minCapacity小于0,抛出OutOfMemoryError异常
if (minCapacity < 0)
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
elementData.length 是数组长度,它是随着数组扩容而动态变化的
如果是第一次 add 元素时,它为 0,之后它是随着 oldCapacity + (oldCapacity >> 1) 而动态变化
calculateCapacity() 方法
如果是第一次 add 元素,那么 minCapacity 设置为 DEFAULT_CAPACITY 与 minCapacity 的最大值,即 10 与 1 取最大值,这里返回最大值 10,否则,直接返回 minCapacity
ensureExplicitCapacity() 方法
如果是第一次 add 元素,由上面方法可知 minCapacity = 10,即 10 - 0 > 0 返回 true,再调用grow() 方法只要 minCapacity - elementData.length > 0 为 true,就会再调用 grow() 方法
如果是第一次 add 元素,由上面的方法可知参数 minCapacity = 10,第一次 add 元素 oldCapacity = 0,可得知 newCapacity = 0 + 0,由于 newCapacity - minCapacity < 0,所以 newCapacity = minCapacity = 10 重新赋值,最终进行新数组的构建和copyOf()赋值
总结:1. 检测是否真正需要扩容,如果是调用grow准备扩容
2. 预估需要库容的大小
初步预估按照1.5倍大小扩容
如果用户所需大小超过预估1.5倍大小,则按照用户所需大小扩容
真正扩容之前检测是否能扩容成功,防止太大导致扩容失败
3. 使用copyOf进行扩容
ArrayList常见操作
方法 | 解释 |
boolean add(E e) | 尾插e |
将
e
插入到
index
位置
| |
尾插
c
中的元素
| |
删除
index
位置元素
| |
删除遇到的第一个
o
| |
获取下标
index
位置元素
| |
将下标
index
位置元素设置为
element
| |
清空
| |
判断
o
是否在线性表中
| |
返回第一个
o
所在下标
| |
返回最后一个
o
的下标
| |
截取部分
list
|
ArrayList模拟实现
1、先写一个MyArrayList类,并写出它的构造方法
import java.util.*;
public class MyArrayList {
public int[] elem;//null
public int usedSize;//0
public MyArrayList() {
this.elem = new int[5];
}
2、add(int data)新增元素
public void add(int data) {
// 1、判断是不是满的
int newLength = this.elem.length + (this.elem.length >> 1);//扩容1.5倍
if(isFull()){
this.elem = Arrays.copyOf(this.elem,newLength);
}
this.elem[this.usedSize] = data;
this.usedSize++;
}
3、MyToString()打印顺序表
// 打印顺序表
public void myToString() {
for (int i = 0; i < this.usedSize; i++) {
System.out.print(this.elem[i]+" ");
}
System.out.println();
}
4、add(int pos, int data)在 pos 位置新增元素
public void add(int pos, int data) {
//0、pos位置不合法
if(pos < 0||pos > this.usedSize){
System.out.println("pos位置不合法!");
return;
}
//1、不能是满的
if(isFull()){
this.elem = Arrays.copyOf(this.elem,2*this.elem.length);
}
//2、正常的插入
for(int i = usedSize -1;i >= pos;i--){
this.elem[i+1]=this.elem[i];
}
this.elem[pos]=data;
this.usedSize++;
}
5、contains(int toFind)判定顺序表是否包含某个元素
public boolean contains(int toFind) {
for(int i = 0;i < this.usedSize;i++){
if(this.elem[i] == toFind){
return true;
}
}
return false;
}
6、indexOf(int toFind)查找某个元素对应的位置
public int indexOf(int toFind) {
for(int i = 0;i < this.usedSize;i++){
if(this.elem[i] == toFind){
return i;
}
}
return -1;
}
7、get(int pos)获取 pos 位置的元素
public int get(int pos) {
if(pos < 0||pos >= this.usedSize){
System.out.println("pos位置不合法!");
throw new RuntimeException("pos位置不合法!");
}
return this.elem[pos];
}
8、set(int pos, int value)给 pos 位置的元素设为 value
public void set(int pos, int value) {
if(pos < 0||pos > this.usedSize) {
System.out.println("pos位置不合法!");
throw new RuntimeException("pos位置不合法!");
}
this.elem[pos] = value;
}
9、remove(int key)删除第一次出现的关键字key
public void remove(int key) {
if(isEmpty()){
System.out.println("不能删除空的顺序表!");
return;
}
int index = indexOf(key);
for(int i = index ;i < this.usedSize-1;i++){
this.elem[i]=this.elem[i+1];
}
this.usedSize--;
// this.elem[this.usedSize] = null; 如果引用数据类型,删除的是地址,应该置位null
}
10、isEmpty()/isFull()判断顺序表是否空/满
public boolean isEmpty(){
if(this.usedSize == 0){
return true;
}
return false;
}
public boolean isFull() {
if(this.usedSize == this.elem.length) {
return true;//满的
}
return false;
}
11、size()获取顺序表长度
public int size() {
return this.usedSize;
}
12、clear()清空顺序表
public void clear() {
this.usedSize = 0;
}
扑克牌
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
class Card {
private String suit;//花色
private int rank;//数字
public Card(String suit, int rank) {
this.suit = suit;
this.rank = rank;
}
@Override
public String toString() {
return "花色:"+suit+" 数字:"+rank;
}
}
public class CardDemo {
private static final String[] SUITS = {"♥","♠","♦","♣"};
public static List<Card> buyCard() {
List<Card> cards = new ArrayList<>();
for (int i = 0; i < 4; i++) {
for (int j = 1; j <= 13; j++) {
/* String suit = SUITS[i];
int rank = j;
arraylistdemo.Card card = new arraylistdemo.Card(suit,j);
cards.add(card);*/
cards.add(new Card(SUITS[i],j));
}
}
return cards;
}
public static void shuffle(List<Card> cards) {
Random random = new Random();
for (int i = cards.size()-1; i > 0 ; i--) {
int index = random.nextInt(i);//[0,100)
//arraylistdemo.Card tmp = cards[i];
Card tmp = cards.get(i);
//cards[i] = cards[index];
cards.set(i,cards.get(index));
//cards[index] = tmp;
cards.set(index,tmp);
}
}
public static void main(String[] args) {
List<Card> cardList = buyCard();
System.out.println("买牌:"+cardList);
shuffle(cardList);
System.out.println("洗牌:"+cardList);
System.out.println("揭牌:");
List<List<Card>> hands = new ArrayList<>();
List<Card> hand1 = new ArrayList<>();
List<Card> hand2 = new ArrayList<>();
List<Card> hand3 = new ArrayList<>();
hands.add(hand1);
hands.add(hand2);
hands.add(hand3);
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 3; j++) {
Card card = cardList.remove(0);//每次都去删除0下标的牌
hands.get(j).add(card);
}
}
System.out.println("第1个人的牌:"+hand1);
System.out.println("第2个人的牌:"+hand2);
System.out.println("第3个人的牌:"+hand3);
System.out.println("剩下的牌:"+cardList);
}
}