ArrayList与顺序表

集合和它背后的数据结构:

集合 —— ArrayList

数据结构 —— 顺序表 

一. ArrayList

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

(1) 实现RandomAccess接口:表示支持随机访问

(2)实现Cloneable接口:表示可以克隆

(3)实现java.io.Serializable接口:表示支持序列化

我们通过结合源码来学习ArrayList中的方法。

1. 调用构造方法创建ArrayList对象

有三种创建方法:

查看构造方法之前,我们先来了解一下ArrayList中的这些成员:

下面我们来看ArrayList的构造方法。 

(1)首先,我们来看第一种无参构造方法:

DEFAULTCAPACITY_EMPTY_ELEMENTDATA是空数组,所以elementData引用了一个空数组。

我们之前可能听说过,调用无参构造方法,默认数组容量是10,其实是错的。通过上述分析,我们知道,调用无参构造方法,数组容量为0。(空数组,也会在堆上分配内存,但数组的长度为0)

咦?空数组,数组长度为0,那之后数据改如何放进去的呢?

那就需要说道说道add方法啦,因为其实是它起了作用。

我们来看一下add方法:

我们看一下 ensureCapacityInternal 方法:

ensureCapacityInternal 方法中调用了 ensureExplicitCapacity 方法,并将 calculateCapacity 方法的返回值作为参数。

grow方法,就是用来扩容的方法

直到这时,数组的容量才被扩容到10。 elementData.length = 10。

那么当数组放满了,我们又是怎么扩容的呢?

这时,size = 10,size+1 = 11

还是经过上述步骤, 只是minCapacity是11了。调用grow方法,我们发现是1.5倍扩容。

总结:

调用无参构造方法时,只有在第一次add的时候,数组的容量(elementData.length)才会被扩容成10。

接着放数据,数组放满了,会进行 1.5 倍的扩容。

(2) 接下来,我们来看有参构造方法。

构造具有指定容量的空列表。

直接对数组进行初始化,elementData 引用了 长度为 initialCapacity 的数组。非常完美,调用它,我们就构造了一个容量固定的数组。

当然,如果你给的容量是0,elementData引用的是 EMPTY_ELEMENTDATA 这个空数组。数组长度为0,之后数据如何放进去,也是当第一次add的时候,会调用grow方法,但它是扩容成长度为1的数组。继续放数据,是怎么扩容的参考grow源码,这里不做继续分析。但都大差不差。

这里我们就知道了DEFAULTCAPACITY_EMPTY_ELEMENTDATA 和 EMPTY_ELEMENTDATA 这两个空数组的区别:前面是调用无参构造方法的时候会用到,后面是调用有参构造方法,传入容量的值为0使会用到。用到的地方不一样。

下一个:

ArrayList(Collection<? extends E> c) 

需要满足两个条件:

(1)实现Collection接口的都可以传过来,如下红色框框里的都可以。

例子:

LinkedList类实现了Collection接口,所以可以传。

(2)通配符?可以接收的类型是E的子类或者E本身

例子:

报错原因:实参arrayList3的类型是ArrayList<String>,arrayList4的类型是ArrayList<Integer>,通配符?只能接收E的子类或E本身,而String不是Integer的本类或Integer本身。

2. ArrayList中的方法

(1)增

boolean add(E e) —— 尾插e
void add(int index, E element) —— 将element插入到index位置
boolean addAll(Collection<? extends E> c) —— 尾插c中的元素
boolean addAll(int index, Collection<? extends E> c) —— 将c中的元素插入到index位置

不需要用到返回值时,可以不用接收。 

(2)删

boolean remove(Object o) —— 删除遇到的第一个o


E remove(int index) —— 删除index位置的元素,返回:index位置的元素
boolean removeAll(Collection<?> c) —— 删除在ArrayList实例中出现的c中的元素

 

(3)查

boolean contains(Object o) —— 是否包含元素o
int indexOf(Object o) —— 返回第一个元素o的下标
int lastIndexOf(Object o) —— 返回最后一个元素o的下标
E get(int index) —— 获取index位置的元素

(4)改

E set(int index, E element) —— 将index位置的元素改为element

(5)其他方法:截取和清空

List<E> subList(int fromIndex, int toIndex) —— 在原ArrayList实例上截取,不会产生新的一份,左闭右开

void clear() —— 清空

3. ArrayList的遍历

1. for循环

2. foreach

3. 迭代器(Iterator)与 ListIterator

详解:见上一篇博客 迭代器(Iterator)与 ListIterator 的使用

boolean hasNext(); —— 判断是否有下一个元素
E next(); —— 返回下一个元素,并往后走一步

4. 题目 

(1)给定两个字符串s1和s2,请删除s1中出现的s2中的字符,请使用集合来完成。

法一:不是s2的字符放到集合中

法二:都放集合,调用 removeAll 方法 

 

5. ArrayList的具体使用

1. 简单的洗牌算法

三个同学想玩扑克牌。首先,他们得买一副扑克牌。然后,洗牌。接着,揭牌,每个人轮流揭5张牌。请用方法实现这三个过程。

Poker:

/*一张扑克牌*/
public class Poker {
    private String suit;
    private int rank;

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

    @Override
    public String toString() {
        return suit + rank;
    }
}

Pokers:

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

/*一副扑克牌*/
public class Pokers {
    public static final String[] suits = {"红桃","黑桃","梅花","方片"};

    //买牌
    public static List<Poker> buyPorkers(){
        List<Poker> pokerList = new ArrayList<>();//放扑克的集合
        for (int i = 0; i < 4; i++) {
            String suit = suits[i];
            for (int j = 1; j <= 13; j++) {
                Poker poker = new Poker(suit,j);//new一张扑克牌
                pokerList.add(poker);//把new的每一张扑克牌放进集合
            }
        }
        return pokerList;//返回放了一副扑克牌的集合
    }
    //洗牌
    public static void swap(List<Poker> pokerList,int i,int j){
        Poker poker1 = pokerList.get(i);
        Poker poker2 = pokerList.get(j);
        pokerList.set(i,poker2);
        pokerList.set(j,poker1);
    }
    public static void shuffle(List<Poker> pokerList){
        //生成随机数
        Random random = new Random();
        int length = pokerList.size();
        for (int i = length-1; i > 0 ; i--) {//从后往前取所有扑克牌(除第一张)
            int index = random.nextInt(i);// 生成0-i之间的任意随机数 [0,i)
            swap(pokerList,i,index);//交换
        }
    }
}

 Test:

import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        //买牌
        List<Poker> pokerList = Pokers.buyPorkers();
        System.out.println(pokerList);
        //洗牌
        Pokers.shuffle(pokerList);
        System.out.println(pokerList);
        //揭牌
        //有三个同学,也就是有三只手需要揭牌,然后放到手中,每一只手就是一个放扑克牌的集合
        List<Poker> hand1 = new ArrayList<>();
        List<Poker> hand2 = new ArrayList<>();
        List<Poker> hand3 = new ArrayList<>();
        //放 手 的集合,集合的每个元素都是一只手
        List<List<Poker>> hand = new ArrayList<>();
        hand.add(hand1);
        hand.add(hand2);
        hand.add(hand3);
        //揭 5 张牌
        for (int i = 0; i < 5; i++) {
            int size = hand.size();//手的个数,即玩牌的人数
            for (int j = 0; j < size; j++) {
                List<Poker> handTmp = hand.get(j);//来确定是哪个人的手
                //pokerList.remove(0) 返回的是最上面那张牌
                handTmp.add(pokerList.remove(0));//往这个人手里放牌
            }
        }

        for (int i = 0; i < hand.size(); i++) {
            System.out.println("第"+(i+1)+"的人手里的牌:"+hand.get(i));
        }
        System.out.println("剩余的牌:"+pokerList);

    }
}

2. 杨辉三角

List<List<Integer>> —— List集合里面的元素类型是List<Integer>

List<Integer> —— List集合里面的元素类型是Integer

 

public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> list = new ArrayList<>();

        List<Integer> firstRow = new ArrayList<>();
        firstRow.add(1);
        list.add(firstRow);

        for (int i = 1; i < numRows; i++) {
            List<Integer> row = new ArrayList<>();
            row.add(1);

            List<Integer> prevRow = list.get(i-1);
            for (int j = 1; j < i; j++) {
                int ret = prevRow.get(j)+prevRow.get(j-1);
                row.add(ret);
            }
            row.add(1);
            list.add(row);
        }
        return list;
}

6. ArrayList的优缺点:

优点:

适合查找给定下标对应的元素,时间复杂度可以达到O(1)

缺点:

(1)扩容可能会造成空间的浪费

(2)每次插入或删除元素,都要先向后或向前移动元素。最坏情况下,当插入或删除的是第一个元素时,时间复杂度可以达到O(n)

所以,ArrayList不适合频繁的插入和删除,适合给定下标查找。

二. Collections —— 用来操作集合的工具类

用的最多的是里面的 sort 方法(排序)

public static <T extends Comparable<? super T>> void sort(List<T> list) 
这是一个静态的泛型方法 —— 用来对集合进行排序

参数 List<T> list ,即 要实现List接口 
<T extends Comparable<? super T>> T是Comparable<? super T>的子类或本身,
? super T,?是T的父类或本身

例如:
ArrayList<Student> arrayList = new ArrayList<>();
Collections.sort(arrayList); 

ArrayList实现了List接口
Student必须是Comparable<? super Student>的子类或本身,即Student必须实现Comparable<Student>接口

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值