线性表&顺序表&ArrayList

一.线性表

1.1 线性表的定义

线性表 linear list n 个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结 构,常见的线性表:顺序表、链表、栈、队列...
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。

1.2 List接口

站在数据结构的角度来看,List就是一个线性表,即n个具有相同类型元素的有限序列,在该序列上可以执行增删查改等操作

List是一个接口,其框架如下

 在Java的集合结构中,List是个接口,不能用来实例化,如果要去使用,只能实例化List的实现类

我们下文的ArrayList就实现类List接口

二.顺序表

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

2.1 顺序表的模拟实现

下面简单实现一下int类型的顺序表

public class SeqList {

    private int[] array;//定义整型数组
    private int size;//size表示数组的有效元素个数
    private final int DEFAULT_CAPACITY=10;//定义数组的初始容量为10

    SeqList() {
        array=new int[DEFAULT_CAPACITY];
    }

    //扩展数组容量
    private void inCapacity(int capacity) {
        this.array= Arrays.copyOf(this.array,capacity);
    }
    //添加数组元素
    public void add(int data) {
        if(this.size==this.array.length) {
            inCapacity(this.array.length*2);
        }
        this.array[this.size++]=data;
    }
    //判断下标pos是否有效
    private boolean isValid(int pos) {
        if(pos<0||pos>this.size)
            return false;
        return true;
    }
    //指定添加下标为pos的元素
    public void add(int pos,int data) {
        if(!isValid(pos)) {
            throw new OutOfRangeException("添加元素的下标越界");
        }
        for(int i=this.size;i>pos;i--) {
            this.array[i]=this.array[i-1];
        }
        this.array[pos]=data;
        this.size++;
    }
    //查找toFind元素的下标
    public int indexOf(int toFind) {
        for(int i=0;i<this.size;i++) {
            if(this.array[i]==toFind)
                return i;
        }
        return -1;
    }

    //查找数组是否包含toFind元素
    public boolean contains(int toFind) {
        if(indexOf(toFind)==-1) return false;
        return true;
    }
   //获取下标为pos的元素
    public int get(int pos) {
        if(!isValid(pos)) {
            throw new OutOfRangeException("获取元素的下标越界");
        }
        return array[pos];
    }
    /设置下标为pos的元素是value
    public void set(int pos,int value){
        if(pos<0||pos>=this.size) {
            throw new OutOfRangeException("设置元素的下标越界");
        }
        this.array[pos]=value;
    }
    //移除元素toRemove
    public void remove(int toRemove) {
        if(this.size==0) return;
        int index=indexOf(toRemove);
        if(index==-1) return;
        for(int i=index;i<this.size-1;i++) {
            this.array[i]=this.array[i+1];
        }
        this.size--;
    }
    //获取数组有效长度
    public int size(){
        return this.size;
    }
    //清空数组
    public void clear() {
        this.size=0;
    }
    //显示数组全部元素(是为了方便调试使用的)
    public void display() {
        for(int i=0;i<size;i++) {
            System.out.print(array[i]+" ");
        }
        System.out.println();
    }

}
//下面是下标不合法时抛的异常
public class OutOfRangeException extends RuntimeException{

    public OutOfRangeException() {
    }

    public OutOfRangeException(String message) {
        super(message);
    }
}

2.2 ArrayList介绍

ArrayList的底层结构就是前面说的顺序表,在集合框架中, ArrayList 是一个普通的类

根据上面的图片可以知道

  1. ArrayList是泛型类,在实例化的时候必须传入泛型参数
  2. ArrayList实现了RandomAccess接口,表示ArrayList支持随机访问
  3. ArrayList实现了Cloneable接口,表示ArrayList可以克隆
  4. ArrayList实现了Serializable接口,表示ArrayList可以序列化
  5. ArrayList支持单线程,在多线程的情况下不安全,尽量使用Vector或CopyOnWriteArrayList
  6. ArrayList底层是一个连续的空间,并且支持动态扩容

2.3 ArrayList的使用

2.3.1 ArrayList的构造

ArrayList提供了三种构造方法

 1.无参构造

​​​​​​

 

 

其中,elementData就是ArrayList的底层数组,可以看到,如果使用无参构造,elementData会指向一个空数组

既然无参构造并没有给ArrayList的底层数组分配内存空间,那么如果往ArrayList对象里面增添一个元素,是不是会抛异常?

public class Test {

    public static void main(String[] args) {
        ArrayList<Integer> a=new ArrayList<>();//调用无参构造
        a.add(1);//增添元素
    }
}

结果并没有抛出异常,在debug下我们可以看到数组的有效长度变成了1

这就涉及到add方法的实现了,一起剖析一下源码吧

1.如果add元素时原数组是空数组,就会将数组扩容为10,并增添新的元素

2.如果使用add增添元素,当数组容量不够时,就会自动扩容为原来容量的1.5倍,这是为了减少数组的扩容次数,也就意味着减少了数组的拷贝

2.初始化时指定容量

 如果传入的initialCapacity>0,就让elementData数组初始化为该容量

如果initialCapacity==0,就让elementData指向一个空数组

如果initialCapacity<0,就抛异常

3. 使用其他Collection初始化ArrayList

 举例

public class Test {
    public static void main(String[] args) {
        ArrayList<Integer> a=new ArrayList<>();
        a.add(1);
        a.add(2);
        a.add(3);
        a.add(4);
        ArrayList<Integer> b=new ArrayList<>(a);//使用a初始化顺序表b
        ArrayList<String>  c=new ArrayList<>(a);//报错,传入的类型参数是Integer,不是String的子类
    }
}

通过Debug调试一下

可以看到,顺序表b的元素和a完全一致 

2.3.2 ArrayList的常见操作

下面提供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)
截取[fromIndex,toIndex)的部分list

注:

1.使用remove方法时,如果传入的是int数据类型,编译器优先使用remove(int Index)方法 

如果目的就是删除这个元素,解决办法是进行装包

2. subList方法返回的是对象的部分引用,更改子顺序表会影响原顺序表

 public static void main(String[] args) {

        ArrayList<String> list=new ArrayList<>();
        list.add("hello");
        list.add("welcome");
        list.add("abcd");
        List<String> sublist=list.subList(0,2);//获取下标为0,1的部分列表
        sublist.set(0,"iodiobo");//将字列表的第一个元素改为idiobo
        System.out.println(list);//输出原顺序表
    }

 

 2.3.3 ArrayList的遍历

和数组一样,ArrayList也有for+下标和增强for循环的遍历方式

1.for+下标

public static void main(String[] args) {

        ArrayList<Integer> a=new ArrayList<>();
        a.add(1);
        a.add(2);
        a.add(3);
        a.add(4);
        for (int i = 0; i < a.size(); i++) {
            System.out.print(a.get(i)+" ");
        }
        
    }

2.for-each

public static void main(String[] args) {

        ArrayList<Integer> a=new ArrayList<>();
        a.add(1);
        a.add(2);
        a.add(3);
        a.add(4);
        for(int e:a) {
            System.out.print(e+" ");
        }

    }

第三种方法就是使用迭代器,使用示例如下

public static void main(String[] args) {

        ArrayList<Integer> a=new ArrayList<>();
        a.add(1);
        a.add(2);
        a.add(3);
        a.add(4);

        Iterator<Integer> it=a.iterator();//也可以a.listIterator或者
                                       //a.listIterator(int Index)表示从Index开始
        while(it.hasNext()) {
            System.out.print(it.next()+" ");

        }
    }

 2.4 ArrayList练习

使用ArrayList实现洗牌操作

//定义Card类
public class Card {
    private String color;
    private int number;

    public Card(String color, int number) {
        this.color = color;
        this.number = number;
    }

    @Override
    public String toString() {
        return "color=" + color  +
                " number=" + number+" ";
    }
}
public class Test {
    private static String[] colors={"♥","♠","♣","♦"};
    //获取52张牌
    public static ArrayList<Card> getCards() {
        ArrayList<Card> cards=new ArrayList<>(52);
       for(int i=1;i<=13;i++) {
           for(int j=0;j<4;j++) {
               Card card=new Card(colors[j],i);
               cards.add(card);
           }
       }
        return cards;
    }
    //进行洗牌操作
    public static  void shuffle(ArrayList<Card> cards) {
      for(int i=cards.size()-1;i>0;i--) {
          Random random=new Random();
          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) {
    ArrayList<Card> cards=getCards();
    shuffle(cards);
    //定义一张三人桌
    ArrayList<ArrayList<Card>> persons=new ArrayList<>();
    for(int i=0;i<3;i++) {
        persons.add(new ArrayList<Card>());
    }
    //共十五张牌,每五张牌让三个人轮流摸
    for(int i=0;i<5;i++) {
        for(int j=0;j<3;j++) {
            persons.get(j).add(cards.remove(0));
        }
    }
    //输出每个人的牌
    for(int i=0;i<3;i++) {
        System.out.println(persons.get(i));
    }
    //输出剩下的牌
        System.out.println(cards);
    }
}

2.5 ArrayList小结

ArrayList的好处就是它的物理空间是连续的,可以使用下标进行修改和查找,时间复杂度均为O(n),相对地,用ArrayList删除和增添元素时都要移动其他元素,尤其是当该元素的下标为0时,时间复杂度为O(n)

因此,顺序表适用于经常查找或更新元素的操作

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不 会敲代码

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

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

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

打赏作者

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

抵扣说明:

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

余额充值