数据结构---关于ArrayList(上)

前言

关于ArrayList呢,首先应该就是对他的家族有一个大概的轮廓图。
我们在IDEA中打开并且进行查看:
在这里插入图片描述
那么我们在这里对ArrayList所实现的这些接口进行说明:

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

一丶关于构造ArrayList

我们在上一篇博客讲过,ArrayList可以接收一切类型的参数,那这意味着什么?泛型!它需要使用泛型的构造方法。具体如下:

 public static void main(String[] args) {
        //构造一个空的列表ls1,然后让它指向子类对象ArrayList
        List<Integer> ls1 = new ArrayList<>();
        //构造一个空的列表ls2,然后让它指向子类对象ArrayList
        List<Integer> ls2 = new ArrayList<>();
        ls2.add(1);
        ls2.add(2);
        ls2.add(3);
        //ls2.add("hello");  编译失败,只能是Integer类

        //甚至还可以直接传顺序表.但是要注意接收的list要和传入的顺序表数据类型一致
        List<Integer> list = new ArrayList<>(ls2);

        //不要省略数据类型,否则泛型使用的毫无意义。
        List list1 = new ArrayList();
        list1.add("hello");
        list1.add(12);
        list1.add(12.36);
    }

二丶关于ArrayList的使用

还是老规矩,我们打开IDEA,具体有哪些方法让编辑器告诉我们(Alt + 7)。
在这里插入图片描述

在这里插入图片描述
感觉好像非常的多,但是刚开始,这么多方法我们只需要掌握其中的一部分便可,不要求全部掌握。

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

那么接下来,我们对这些方法进行一一验证:

public static void main(String[] args) {
        List<String> list = new ArrayList<>(10);

        //尾插元素
        list.add("位置1");
        list.add("位置2");
        list.add("位置3");
        list.add("位置4");
        list.add("位置5");

        //将 e 插入到 index 位置,后续所有元素往后移一位
        list.add(2,"此处位置被重新插入");
        System.out.println(list);

        //删除 index 位置元素,后续所有元素往前移一位
        list.remove(2);
        System.out.println(list);

        //删除指定对象
        list.remove("位置1");
        System.out.println(list);

        //获取下标 index 位置元素
        System.out.println(list.get(0));

        //将下标 index 位置元素设置为 element
        list.set(1,"这里重新设置");
        System.out.println(list);

        //判断 o 是否在线性表中
        System.out.println(list.contains("嘤嘤嘤"));

        //从前往后返回第一个 o 所在下标
        list.add(3,"位置2");
        System.out.println(list.indexOf("位置2"));
        //从后往前找
        System.out.println(list.lastIndexOf("位置2"));

        System.out.println(list);

    }

具体运行结果如下:

在这里插入图片描述

关于ArrayList的遍历

关于ArrayList的遍历这里,总共有三种遍历方式:

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

        //遍历方式一:for循环
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i)+ " " );
        }
        System.out.println();
        //方式二:for -each循环
        for(Integer i : list){
            System.out.print(i + " ");
        }
        System.out.println();
        //方式三:迭代器
        Iterator<Integer> it = list.listIterator();
        while (it.hasNext()){
            System.out.print(it.next() + " ");
        }
        System.out.println();
    }

然后结果如下:
在这里插入图片描述

关于ArrayList的扩容

在这让一段代码引出问题。

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

可以发现这段代码问题在哪里?
很 明 显 , 刚 开 始 没 有 给 出 A r r a y L i s t 的 容 量 \color{red}{很明显,刚开始没有给出ArrayList的容量} ArrayList

但是这段代码是可以成功运行的,意味着什么?
要 么 不 设 置 容 量 时 , 其 容 量 足 够 大 或 者 它 中 途 会 进 行 动 态 扩 容 \color{red}{要么不设置容量时,其容量足够大或者它中途会进行动态扩容}

那么我们查看对应的源码:

Object[] elementData; // 存放元素的空间
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 默认空间
private static final int DEFAULT_CAPACITY = 10; // 默认容量大小
public void ensureCapacity(int minCapacity) {
 // 计算最小的扩容大小
 int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)? 0 : 
DEFAULT_CAPACITY;
 
 // 检测是否需要扩容
 if (minCapacity > minExpand) {
 ensureExplicitCapacity(minCapacity);
 }
}
private void ensureExplicitCapacity(int minCapacity) {
 modCount++;
 // 如果minCapacity比数组的容量大,就调用grow进行扩容
 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)
 newCapacity = hugeCapacity(minCapacity);
 
 // 调用copyOf扩容
 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;
}

从这里可以看出扩容分为以下几步:

  1. 检测是否真正需要扩容(默认容量10),如果是调用grow准备扩容
  2. 预估需要库容的大小
    初步预估按照1.5倍大小扩容
    如果用户所需大小超过预估1.5倍大小,则按照用户所需大小扩容
    真正扩容之前检测是否能扩容成功,防止太大导致扩容失败
  3. 使用copyOf进行扩容

三丶关于ArrayList的实际使用

这里关于ArrayList的实际使用,还是使用一道例题。

public class CardDemo {
    public static final String[] SUITS = {"♥","♠","♣","♦"};

    //定义一副牌
    public static List<Card> buyDeck(){
        List<Card> deck = new ArrayList<>(52);
        for(int i = 0;i < 4;i++){
            for(int j = 0;j < 13;j++){
                String suit = SUITS[i];
                int rank = j;
                Card card = new Card();
                card.rank = rank;
                card.suit = suit;
                deck.add(card);
            }
        }
        return deck;
    }

    //交换牌序
    public static void swap(List<Card> deck,int i,int j){
        Card c = deck.get(i);
        deck.set(i,deck.get(j));
        deck.set(j,c);
    }

    //打乱牌序----洗牌
    public static void shuffle(List<Card> deck){
        Random random = new Random();
        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);

        //接着定义三个人,然后进行抓牌
        //这里的这个定义格式就很耐人寻味了
        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++) {
                //这里里面是3,外面是5,就是一共进行五轮,每个人每次拿一张牌
                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));

    }
}

class Card{
    int rank;//定义牌面值
    String suit;//定义花色

    @Override
    public String toString() {
        String s = "[" + suit + "," + rank + "]";
        return s;
    }
}

四丶总结

还是多练练OJ题吧。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值