【JavaSE】 ArrayList与顺序表

1 ArrayList介绍

在集合框架中,ArrayList是一个普通的类,实现了List接口,具体框架图如下:
在这里插入图片描述

【说明】

  1. ArrayList实现了RandomAccess接口,表明ArrayList支持随机访问
  2. ArrayList实现了Cloneable接口,表明ArrayList是可以clone的
  3. ArrayList实现了Serializable接口,表明ArrayList是支持序列化的
  4. 和Vector不同,ArrayList不是线程安全的,在单线程下可以使用,在多线程中可以选择Vector或者CopyOnWriteArrayList
  5. ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表

2 ArrayList的使用

2.1 ArrayList的构造

方法解释
ArrayList()无参构造
ArrayList(Collection<? extends E> c)利用其他 Collection 构建 ArrayList
ArrayList(int initialCapacity)指定顺序表初始容量

💡几个例子:

public class Test {
    public static void main(String[] args) {
        // ArrayList创建,推荐写法
        // 构造一个能存放十个元素的列表
        List<Integer> list1 = new ArrayList<>(10);
        //注意:List已经被指定为Integer类型的列表,所以只能存放Integer类型的数据
        list1.add(21);
        list1.add(12);
        list1.add(43);
        List<Integer> list2 = new ArrayList<>(list1);
        int ret = list2.get(2);
        System.out.println(ret);
    }
}

2.2 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 subList(int fromIndex, int toIndex)截取部分 list

🔥使用范例:

public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("JavaSE");
        list.add("JavaWeb");
        list.add("JavaEE");
        list.add("JVM");
        list.add("测试课程");
        System.out.println(list);
        // 获取list中有效元素个数
        System.out.println(list.size());
        // 获取和设置index位置上的元素,注意index必须介于[0, size)间
        System.out.println(list.get(1));
        list.set(1, "JavaWEB");
        System.out.println(list.get(1));
        // 在list的index位置插入指定元素,index及后续的元素统一往后搬移一个位置
        list.add(1, "Java数据结构");
        System.out.println(list);
        // 删除指定元素,找到了就删除,该元素之后的元素统一往前搬移一个位置
        list.remove("JVM"); System.out.println(list);
        // 删除list中index位置上的元素,注意index不要超过list中有效元素个数,否则会抛出下标越界异常
        list.remove(list.size()-1); 
        System.out.println(list);
        // 检测list中是否包含指定元素,包含返回true,否则返回false
        if(list.contains("测试课程")){
            list.add("测试课程");
        }
        // 查找指定元素第一次出现的位置:indexOf从前往后找,lastIndexOf从后往前找
        list.add("JavaSE");
        System.out.println(list.indexOf("JavaSE"));
        System.out.println(list.lastIndexOf("JavaSE"));
        // 使用list中[0, 4)之间的元素构成一个新的ArrayList返回
        List<String> ret = list.subList(0, 4);
        System.out.println(ret);
        list.clear(); 
        System.out.println(list.size());
    }

在这里插入图片描述

2.3 ArrayList的遍历

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

List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        // 使用下标+for遍历 
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i) + " ");
        }
        System.out.println();
        // 借助foreach遍历
        for (Integer integer : list) {
            System.out.print(integer + " ");
        }
        System.out.println();
        Iterator<Integer> it = list.listIterator();
        while(it.hasNext()){
            System.out.print(it.next() + " ");
        }
        System.out.println();

在这里插入图片描述

2.4 ArrayList的扩容机制

我们先来看一段代码:

public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(i);
        }
        System.out.println(list);
    }

由于我们没有指定列表大小,所以一般情况会觉得这段代码会报错,但是由于ArrayList的自动扩容机制,当列表空间不够存放元素时,列表会先进行扩容再将元素放入列表中。所以这段代码能正常运行。

在这里插入图片描述
下面我们就来看看ArrayList源码中是怎么进行扩容的:

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

    /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // 获取旧空间大小
        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);
        // 扩容
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0){
            //空间大小出错,抛出错误。
            throw new OutOfMemoryError();
        }
        return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
    }

总结:
首先判断加一个元素后(即size+1)列表是否越界,若越界则进行扩容,否则可直接进行元素赋值,注意当初始空间为0时会自动扩容为10,这样就会减少扩容次数,优化效率.

4 用ArrayList实现扑克牌

💡代码示范:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

/**
 * Created with IntelliJ IDEA. 
 * Description:
 * User: admin
 * Date: 2021-11-29
 * Time: 22:08
 */
class Card{
    private int rank;
    private String suit;

    public Card(int rank, String suit) {
        this.rank = rank;
        this.suit = suit;
    }

    public int getRank() {
        return rank;
    }

    public void setRank(int rank) {
        this.rank = rank;
    }

    public String getSuit() {
        return suit;
    }

    public void setSuit(String suit) {
        this.suit = suit;
    }

    @Override
    public String toString() {
        return "["+this.suit+":"+this.rank+"]";
    }
}
public class Test {
    public static final String[] suits = {"♠","♦","♣","♥"};
    //买牌,即生成存放牌的列表
    public static List<Card> buyCards(){
        List<Card> cards = new ArrayList<>();
        for (int i = 0; i < suits.length; i++) {
            for (int j = 1; j <= 13; j++) {
                Card card = new Card(j,suits[i]);
                cards.add(card);
            }
        }
        return cards;
    }
    //洗牌
    public static List<Card> swapCards(List<Card> cards){
        for (int i = cards.size()-1; i>0; i--){
            Random random = new Random();
            //生成随机数范围为[0, i),每次循环让最后一个元素与前面随机一个元素进行交换从而达到洗牌的效果
            int rand = random.nextInt(i);
            Card tep = cards.get(rand);
            cards.set(rand,cards.get(i));
            cards.set(i,tep);
        }
        return cards;
    }
    public static void main(String[] args) {
        List<Card> cards = buyCards();
        List<Card> ret = swapCards(cards);
        System.out.println(ret);
        //建立手牌列表,每个元素都是一个List<Card>,代表的某个人手上的牌
        List<List<Card>> hand = new ArrayList<>();
        List<Card> hand1 = new ArrayList<>();
        List<Card> hand2 = new ArrayList<>();
        List<Card> hand3 = new ArrayList<>();
        hand.add(hand1);
        hand.add(hand2);
        hand.add(hand3);
        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < 3; j++) {
                Card card = cards.remove(0);
                hand.get(j).add(card);
            }

        }
        System.out.println("第一个人牌是:"+hand.get(0));
        System.out.println("第二个人牌是:"+hand.get(1));
        System.out.println("第三个人牌是:"+hand.get(2));
        System.out.println("剩下的牌:"+cards);
    }
}

在这里插入图片描述

5 ArrayList的模拟实现

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: admin
 * Date: 2021-11-30
 * Time: 23:09
 */
//避免在将Object类型强制类型转换时出现警告
@SuppressWarnings("all")
class MyArrayList<E> {
    private Object[] array;//数组
    private int size;//数组实际存放的元素个数
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    public MyArrayList() {
        this.array = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    public MyArrayList(int len) {
        if (len > 0) {
            this.array = new Object[len];
        } else if (len == 0) {
            this.array = new Object[0];
        } else {
            throw new IllegalArgumentException("下标错误");
        }
    }
    public int size(){
        return this.size;
    }
    public boolean isEmpty(){
        return 0==this.size;
    }
    public boolean add(E e){
        //保证添加元素后数组能放的下,若放不下则自动扩容
        ensureCapacityInternal(size + 1);
        this.array[this.size] = e;
        this.size++;
        return true;
    }
    public void ensureCapacityInternal(int minCapacity){
        //1、先计算
        int capacity = calculateCapacity(array, minCapacity);
        //2、确保该容量是否可以分配
        ensureExplicitCapacity(capacity);
    }
    private static final int DEFAULT_CAPACITY = 10;
    public int calculateCapacity(Object[] array, int minCapacity){
        if(array == DEFAULTCAPACITY_EMPTY_ELEMENTDATA){
            return Math.max(DEFAULT_CAPACITY,minCapacity);
        }
        return minCapacity;
    }
    public void ensureExplicitCapacity(int capacity){
        if(capacity-this.array.length>0){
            grow(capacity);
        }
    }
    //扩容
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    public void grow(int capacity){
        int oldCapacity = this.array.length;
        int newCapacity = oldCapacity + (oldCapacity>>1);
        if(newCapacity<capacity){
            newCapacity = capacity;
        }
        if(newCapacity>MAX_ARRAY_SIZE){
            newCapacity = MAX_ARRAY_SIZE;
        }
        this.array = new Object[newCapacity];
    }
    //在某个下标添加元素,注意添加删除某个下标元素都要有限判断下标有效性。
    public void add(E e, int index){
        check(index);
        ensureCapacityInternal(this.size+1);
        for (int i = size-1; i >= index ; i--) {
            array[i+1] = array[i];
        }
        //如果e为Object类型那么就没有类型检查功能,就可能导致数组不是放的同一类元素。
        array[index] = e;
        size++;

    }
    //检查下标有效性
    public void check(int index){
        if(index<0 || index>=this.size){
            throw new IndexOutOfBoundsException("下标越界");
        }
        return;
    }
    //删除元素
    public void delete(int index){
        check(index);
        for (int i = index+1; i < size; i++) {
            array[i-1] = array[i];
        }
        array[size-1] = null;
        size--;
    }
    // 获取num第一次出现的位置
    public int getPos(Object num){
        if(num == null){
            for (int i = 0; i < size; i++) {
                if(array[i] == null){
                    return i;
                }
            }
        }else{
            for (int i = 0; i < size; i++) {
                if(array[i].equals(num)){
                    return i;
                }
            }
        }
        System.out.println("没有该下标,请重新输入!");
        return -1;
    }

    public boolean remove(Object num){
        int index = getPos(num);
        if(index == -1){
            System.out.println("没有该元素!");
            return false;
        }
        delete(index);
        return true;
    }

    public E getNum(int index){
        check(index);
        return (E) array[index];
    }

    public E setNum(int index,E e){
        check(index);
        array[index] = e;
        return e;
    }

    public void clear(){
        for (int i = 0; i < size; i++) {
            array[i] = null;
        }
        size = 0;
    }

    @Override
    public String toString() {
        String str = "[";
        if(size>0){
            //最后一个数不用加分号,放到循环外链接。
            for (int i = 0; i < size-1; i++) {
                str += array[i];
                str += ";";
            }
            str += array[size-1];
        }
        str += "]";
        return str;
    }
}
public class Test {
    public static void main(String[] args) {
        MyArrayList<String> list = new MyArrayList<>();
        list.add("hello");
        list.add("world");
        int pos = list.getPos("world");
        System.out.println(list);
        System.out.println(pos);
        list.remove("hello");
        System.out.println(list);
        String RET = list.getNum(0);
        System.out.println(RET);
    }
}

在这里插入图片描述

作者水平有限,若文章有任何问题欢迎私聊或留言,希望和大家一起学习进步!!!
创作不易,再次希望大家👍支持下,谢谢大家🙏

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值