ArrayList顺序表的模拟实现

目录

ArrayList构造 

ArrayList动态扩容机制 

ArrayList的主要属性 

ArrayList的构造器 

ArrayList的动态扩容

ArrayList常见操作

ArrayList模拟实现 

1、先写一个MyArrayList类,并写出它的构造方法

2、add(int data)新增元素 

3、MyToString()打印顺序表

4、add(int pos, int data)在 pos 位置新增元素

 5、contains(int toFind)判定顺序表是否包含某个元素

6、indexOf(int toFind)查找某个元素对应的位置

7、get(int pos)获取 pos 位置的元素

8、set(int pos, int value)给 pos 位置的元素设为 value

9、remove(int key)删除第一次出现的关键字key

10、isEmpty()/isFull()判断顺序表是否空/满

11、size()获取顺序表长度

12、clear()清空顺序表

扑克牌 


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
void add (int index, E element)
e 插入到 index 位置
boolean addAll (Collection<? extends E> 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<E> subList (int fromIndex, int toIndex)
截取部分 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);
    }
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值