ArrayList与顺序表

目录

1.线性表

2.顺序表

2.1 顺序表的实现

3. ArrayList简介 

4. ArrayList使用

4.1 ArrayList的构造

4.2 ArrayList常见操作 

4.3 ArrayList的遍历 

4.4 ArrayList的扩容机制

5. ArrayList的使用案例

5.1 简单的洗牌算法

5.2 杨辉三角 

6. ArrayList的问题 


1.线性表

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

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

2.顺序表

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

2.1 顺序表的实现

public class SeqList {
    private int[] elem;
    private int usedSize; // 记录当前使用元素个数
    private static final int DEFAULT_CAPACITY = 5;

    public SeqList() {
        this.elem = new int[DEFAULT_CAPACITY];
    }

    // 打印顺序表,注意:该方法并不是顺序表中的方法,为了方便看测试结果给出的
    public void display() {
        for (int i = 0; i < this.usedSize; i++) {
            System.out.print(this.elem[i] + " ");
        }
        System.out.println();
    }
    // 新增元素,默认在数据 最后新增
    public void add(int data) {
        if (isFull()) {
            this.elem = Arrays.copyOf(this.elem, this.elem.length * 2);
        }
        this.elem[usedSize] = data;
        this.usedSize++;
    }
    // 判断顺序满没满
    public boolean isFull() {
        return this.usedSize == this.elem.length;
    }
    // 判定是否包含某个元素
    public boolean contains(int data) {
        for (int i = 0; i < this.usedSize; i++) {
            if(this.elem[i] == data)
                return true;
        }
        return false;
    }
    // 查找某个元素对应的位置 下标
    public int indexof(int data){
        for (int i = 0; i < this.usedSize; i++) {
            if(this.elem[i] == data){
                return i;
            }
        }
        return -1;
    }
    // 获取 pos 位置的元素
    public int get (int pos) {
        if(!checkPos(pos)) {
            throw new PosBoundsOfException("获取pos位置时, pos不合法, 此时, pos: " + pos);
        }
        return this.elem[pos];
    }
    // 获取顺序表长度
    public int size() {
        return this.usedSize;
    }
    public boolean checkPos(int pos) {
        if(pos < 0 || pos > this.usedSize - 1) {
            return false;
        }
        return true;

    }
    // 给 pos 位置的元素设为 value【更新的意思 】
    public void set(int pos, int value) {
        if(!checkPos(pos)) {
            throw new PosBoundsOfException("获取pos位置时, pos不合法, 此时, pos: " + pos);
        }
        this.elem[pos] = value;
    }
    // 扩容
    public void resize() {
        this.elem = Arrays.copyOf(this.elem, this.elem.length * 2);
    }

    // 在 pos 位置新增元素
    public void add(int pos, int data) {
        if(pos < 0 || pos > this.usedSize) {
            throw new PosBoundsOfException("获取pos位置时, pos不合法, 此时, pos: " + pos);
        }
        if(isFull()) {
            resize();
        }
        // 挪数据
        for (int i = this.usedSize-1; i >= pos ; i--) {
            this.elem[i+1] = this.elem[i];

        }
        // 存数据
        this.elem[pos] = data;
        this.usedSize++;
    }
    // 删除第一次出现的关键字key
    public void remove(int key) {
        if (isEmpty()) {
            return;
        }
        int index = indexof(key);
        if (-1 == index) {
            return;// 没有你要删除的数据
        }
        // 删数据
        int i = index;
        for (; i < this.usedSize - 1; i++) {
            this.elem[i] = this.elem[i+1];
        }
        // 删除的是最后一个元素
        if(i == this.usedSize - 1) {
            this.elem[i] = 0;
        }
        this.usedSize--;
    }
    public boolean isEmpty() {
        return this.usedSize == 0;
    }
    // 清空顺序表
    public void clear() {
        this.usedSize = 0;
    }
}

3. ArrayList简介 

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

1. ArrayList是以泛型方式实现的,使用时必须要先实例化

2. ArrayList实现了RandomAccess接口,表明ArrayList支持随机访问 

3. ArrayList实现了Cloneable接口,表明ArrayList是可以clone的

4. ArrayList实现了Serializable接口,表明ArrayList是支持序列化的

5. 和Vector不同,ArrayList不是线程安全的,在单线程下可以使用,在多线程中可以选择Vector或者 CopyOnWriteArrayList

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

4. ArrayList使用

4.1 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);

}

4.2 ArrayList常见操作 

这些方法完全可以用到什么, 再去查什么

4.3 ArrayList的遍历 

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

public static void main(String[] args) {
        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();
}

1. ArrayList最长使用的遍历方式是:for循环+下标 以及 foreach

2. 迭代器是设计模式的一种,后序容器接触多了才会有较好的理解 

4.4 ArrayList的扩容机制

上述截图中new一个ArrayList对象, 采用的是无参数的构造方法, 但是无参的构造方法中的数组是一个空数组, 按理说数组空间不够, 不能够加东西进去. 那么"hello"是怎么加进去的呢? ArrayList实现了List接口, 所以我们要到ArrayList中去查看重写的add方法

这是一步一步在IDEA看ArrayList源码的步骤, 起初elemData这个数组的容量是0, 是不能够添加元素进去的, 但是ArrayList有一个扩容机制, 将容量扩充到了10. 如果10满了之后呢? 观察源码知道数组空间会扩充到15.

1. 检测是否真正需要扩容,如果是调用grow准备扩容

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

3. 使用copyOf进行扩容 

5. ArrayList的使用案例

5.1 简单的洗牌算法

Card类

public class Card {
    private String suit;
    private int rank;


    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 suit + rank;
    }
}

Test类 

public class Test {

    public static final String[] SUITS = {"♥", "♠", "♦", "♣"};
    
    // 生成牌
    public static List<Card> buyCard() {
        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 void shuffle(List<Card> cards) {
        Random random = new Random();
        for (int i = cards.size()-1; i > 0; i--) {
            int j = random.nextInt(i);
            // 交换牌
            Card tmp = cards.get(i);
            cards.set(i, cards.get(j));
            cards.set(j, tmp);
        }
    }
    public static void main(String[] args) {
        List<Card> cards = buyCard();
        System.out.println("洗牌前");
        System.out.println(cards);
        //洗牌后
        System.out.println("洗牌后");
        shuffle(cards);
        System.out.println(cards);
        // 摸牌
        // 每个人牌放在哪里
        List<Card> hand1 = new ArrayList<>();
        List<Card> hand2 = new ArrayList<>();
        List<Card> hand3 = new ArrayList<>();

        List<List<Card>> hand = new ArrayList<>();
        hand.add(hand1);
        hand.add(hand2);
        hand.add(hand3);
        // 3个人轮流拿5张牌
        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("第一个人的牌");
        System.out.println(hand1);
        System.out.println("第二个人的牌");
        System.out.println(hand2);
        System.out.println("第三个人的牌");
        System.out.println(hand3);
    }
}

5.2 杨辉三角 

public static List<List<Integer>> generate(int numRows) {
        List<List<Integer>> ret = new ArrayList<>();
        List<Integer> list = new ArrayList<>();
        list.add(1);
        // 先将第一行的1 加进去
        ret.add(list);
        // 之后逐渐加每一行
        for (int i = 1; i < numRows; i++) {
            // 一行一行来写 这代表当前行
            // 第0列的 1
            List<Integer> curRow = new ArrayList<>();
            curRow.add(1);
            // 处理中间数据
            List<Integer> preRow = ret.get(i-1);
            for (int j = 1; j < i; j++) {
                int val = preRow.get(j) + preRow.get(j-1);
                curRow.add(j, val);
            }
            //最后的1
            curRow.add(1);
            ret.add(curRow);
        }
        return ret;
}

6. ArrayList的问题 

1. ArrayList底层使用连续的空间,任意位置插入或删除元素时,需要将该位置后序元素整体往前或者往后搬 移,故时间复杂度为O(N)

2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。

3. 增容一般是呈1.5倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到150,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了45个数据空间。 

怎么解决,此时链表就出现了, 我会在下篇文章会详细介绍 

  • 22
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

早点睡觉1.0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值