【数据结构Java版】ArrayList与顺序表

目录

一、线性表

二、顺序表

(1)顺序表概念

(2)接口的实现

三、ArrayList介绍

四、ArrayList使用

(1)ArrayList的构造

(2)ArrayList的常见方法

1.public int size()

2.public boolean add(Long e) 

3.public void add(int index, Long e)

4.public Long remove(int index)

5.public boolean remove(Long e)

6.public Long get(int index)

7.public Long set(int index, Long e)

8.public int indexOf(Long e)

9.public int lastIndexOf(Long e)

10.public boolean contains(Long e)

11.public void clear()

12.public boolean isEmpty()

(3)ArrayList的遍历

1.for循环下标

2.foreach

3.迭代器

(4)ArrayList的扩容机制

(5)自定义ArrayList的完整代码及测试过程

五、实例演示

(1)扑克牌

(2)杨辉三角


一、线性表

        线性表(Linear list)是n个具有相同特性的数据元素的有限序列,线性表是一种在实际生活中应用广泛的数据结构。

        常见的线性表:顺序表、链表、栈、队列。

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


二、顺序表

(1)顺序表概念

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

(2)接口的实现

  自定义的线性表(以接口的形式体现)
 1.元素类型     
Long
 1)int 下标      long 元素
 2)包装类:体现对象的形式而不是基本类型
 2.准备线性表的规定
 3.教学过程中体现(在Java真实的设计中没有这步要求):
 有元素的位置!=null

 没有元素的位置==null

注解小贴士: 

@return对方法返回值的说明,如果方法的返回值类型是 void ,则不能使用该注解。
@param

表示标签,可以归档方法或构造器的某个单一参数,或者归档类、接口以及泛型方法的类型参数。在使用@ param标签时,我们应该针对方法的每一个参数都使用一个该标签。每个段落的第一个词会被当作参数名,而余下的部分则会被当作是对它的描述。

@override当前的方法定义将覆盖超类中的方法, 限定必须重写父类方法
public interface MyList {
    /**
     * 返回线性表中的元素个数
     * @return
     */
    int size();

    /**
     * 将 e 尾插到线性表中,一定返回 true
     * @param e
     * @return
     */
    boolean add(Long e);

    /**
     * 将 e 插入到线性表的 index 位置,从 [index, size()) 向后移
     * index 的合法下标 [0, size()]
     * 如果下标不合法:抛出一个 ArrayIndexOutOfBoundsException
     * @param index
     * @param e
     */
    void add(int index, Long e);

    /**
     * 删除 index 位置的元素
     * index 的合法下标:[0, size())
     * 如果下标不合法:抛出一个 ArrayIndexOutOfBoundsException
     * @param index
     * @return 从线性表中删除掉的元素
     */
    Long remove(int index);

    /**
     * 从前到后,删除第一个遇到的 e( equals() == true)
     * @param e
     * @return 删除成功:true,没有该元素:false
     */
    boolean remove(Long e);

    /**
     * 直接返回 index 位置的元素
     * index: [0, size())
     * @param index
     * @return
     */
    Long get(int index);

    /**
     * 使用 e 替换 index 位置的元素
     * @param index [0, size())
     * @param e
     * @return 原来 index 位置的元素
     */
    Long set(int index, Long e);

    /**
     * 返回第一次遇到 e 的下标(equals() == true)
     * @param e
     * @return 如果没有找到,返回 -1
     */
    int indexOf(Long e);

    /**
     * 从后往前,返回第一次遇到 e 的下标(equals() == true)
     * @param e
     * @return 如果没有找到,返回 -1
     */
    int lastIndexOf(Long e);

    /**
     * 线性表中是否包含 e(equals)
     * @param e
     * @return
     */
    boolean contains(Long e);

    /**
     * 清空线性表
     */
    void clear();

    /**
     * 判断线性表是否是空的(empty) 等价于一个元素都没有
     * @return
     */
    boolean isEmpty();
}

 三、ArrayList介绍

        在集合框架中,ArrayList是一个普通的类,实现了List接口

​​​​​​​

 

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


四、ArrayList使用

(1)ArrayList的构造

 < ?>是泛型,泛型详情请见http://t.csdn.cn/p05n6

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

ps:对于初学者来说掌握一、三两种就可以了 

public class MyArrayList implements MyList {
    // 定义属性
    private Long[] array;   // array.length 就是容量(capacity)
    private int size;       // 保存元素的个数

    // 构造方法
    public MyArrayList() {
        // 容量没有规定,自行定义为 7 个
        this.array = new Long[7];
        // 这一步不是必须的,把数组中的每个位置都初始化成 null
        for (int i = 0; i < array.length; i++) {
            array[i] = null;
        }
        // 元素个数 = 0
        this.size = 0;
    }

    public MyArrayList(int initialCapacity) {
        this.array = new Long[initialCapacity];
        // 这一步不是必须的,把数组中的每个位置都初始化成 null
        for (int i = 0; i < array.length; i++) {
            array[i] = null;
        }
        // 元素个数 = 0
        this.size = 0;
    }
}

(2)ArrayList的常见方法

方法解释
boolean add()尾插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

常见方法的实现:

1.public int size()

@Override
    public int size() {
        return size;
    }

2.public boolean add(Long e) 


    // 时间复杂度是O(1) 
 @Override
    public boolean add(Long e) {
        // 为 size 赋予一个新的逻辑含义 —— 尾插时的元素的位置
        array[size] = e;
        // 让元素个数的记录 + 1
        size = size + 1;
        // 返回 true 表示插入成功
        return true;
    }

3.public void add(int index, Long e)

 // 时间复杂度:O(n)
    @Override
    public void add(int index, Long e) {     
        if (index < 0 || index > size) {
            // 下标不合法
            throw new ArrayIndexOutOfBoundsException("下标不合法");
        }
        // 此时下标一定是合法的
        // 将每个结点往后移位
        // i: 移位时的出发点
        for (int i = size - 1; i >= index; i--) {
            // array[i] 的元素搬到  array[i + 1]
            array[i + 1] = array[i];
        }
        // 将 e 放到 index 位置
        array[index] = e;
        // 元素个数增加了 1
        size = size + 1;
    }

4.public Long remove(int index)

  // 时间复杂度: O(n)
@Override
    public Long remove(int index) {
        // 下标合法性检查
        if (index < 0 || index >= size) {
            // 下标不合法
            throw new ArrayIndexOutOfBoundsException("下标不合法");
        }
        // 先把要删除的元素保存起来
        Long e = array[index];

        // i 代表搬元素时的起始位置 [index + 1, size)
        for (int i = index + 1; i < size; i++) {
            array[i - 1] = array[i];
        }
        // 把不存在的元素置为 null
        // 真实中是否要置 null,需要根据性能考虑
        array[size - 1] = null;
        // 元素个数减一
        size = size - 1;
        // 返回被删除的元素
        return e;
    }

5.public boolean remove(Long e)

// 时间复杂度是 O(n)
    @Override
    public boolean remove(Long e) {
        // 按照从前到后的顺序,找到第一个遇到的 e
        for (int i = 0; i < size; i++) {
            // 毕竟 array 的值 是否 和 e 相等(equals)
            if (array[i].equals(e)) {
                // 删除 [i] 位置的元素
                remove(i);  // O(n)
                return true;
            }
        }

        // 说明没有找到 e
        return false;
    }

6.public Long get(int index)

//时间复杂度是 O(1)
    @Override
    public Long get(int index) {// 下标合法性检查
        if (index < 0 || index >= size) {
            // 下标不合法
            throw new ArrayIndexOutOfBoundsException("下标不合法");
        }
        return array[index];
    }

7.public Long set(int index, Long e)

 // 时间复杂度是O(1)
    @Override
    public Long set(int index, Long e) {
        // 下标合法性检查
        if (index < 0 || index >= size) {
            // 下标不合法
            throw new ArrayIndexOutOfBoundsException("下标不合法");
        }
        Long tmp = array[index];
        array[index] = e;
        return tmp;
    }

8.public int indexOf(Long e)

  // 时间复杂度是O(n)
    @Override
public int indexOf(Long e) {
        for (int i = 0; i < size; i++) {
           if(array[i].equals(e)){
               return i;
           }
        }
        return -1;
    }

9.public int lastIndexOf(Long e)

// 时间复杂度是O(n)
    @Override
    public int lastIndexOf(Long e) {
        for (int i = size - 1; i >= 0; i--) {
            if (array[i].equals(e)) {
                return i;
            }
        }
        return -1;
    }

10.public boolean contains(Long e)

//时间复杂度是O(n)
 @Override
    public boolean contains(Long e) {
        for (int i = 0; i < size; i++) {
            if(array[i].equals(e)){
                return true;
            }
        }
        return false;
    }
    // 时间复杂度是O(n)
    @Override
    public boolean contains(Long e) {
        return indexOf(e) != -1;
    }

11.public void clear()

//时间复杂度是O(n)   
 @Override
    public void clear() {
        for (int i = 0; i <size ; i++) {
            array[i] = null;
        }
        this.size=0;
    }

12.public boolean isEmpty()

//时间复杂度是O(1)
@Override
    public boolean isEmpty() {
        return size == 0;
    }

(3)ArrayList的遍历

1.for循环下标

public class MyArrayList  {
    public static void main(String[] args) {
        List<String>list=new ArrayList<>();
        list.add("小");
        list.add("熊");
        list.add("努");
        list.add("力");
        list.add("写");
        list.add("代");
        list.add("码");
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i));
        }
    }
}

2.foreach

public class MyArrayList  {
    public static void main(String[] args) {
        List<String>list=new ArrayList<>();
        list.add("小");
        list.add("熊");
        list.add("努");
        list.add("力");
        list.add("写");
        list.add("代");
        list.add("码");
        for (String str:
             list) {
            System.out.print(str);
        }
    }
}

3.迭代器

<String>是泛型,泛型详情请见http://t.csdn.cn/p05n6

public class MyArrayList  {
    public static void main(String[] args) {
        List<String>list=new ArrayList<>();
        list.add("小");
        list.add("熊");
        list.add("努");
        list.add("力");
        list.add("写");
        list.add("代");
        list.add("码");
        Iterator<String>stringIterator=list.listIterator();
        while(stringIterator.hasNext()){
            System.out.print(stringIterator.next());
        }
    }
}

(4)ArrayList的扩容机制

        在Java中ArrayList是一个动态类型的顺序表,即在插入元素的过程中会自动扩容。

    // 时间复杂度:数据规模是 size,时间复杂度是 O(1)
    // 最坏情况,发生扩容的情况: O(n)
    // 我们认为扩容的发生是小概率事件
    // 平均事件复杂度:O(1)  
private void ensureCapacity() {
        // 此时不需要扩容
        if (this.size < this.array.length) {
            return;    
        }
        // 需要扩容
        // 1) 申请新数组,容量是原来的 2 倍,可以根据性能需要自定义
        int newLength = this.array.length * 2;
        Long[] newArray = new Long[newLength];
        // 2) 将数据搬到新数组中
        for (int i = 0; i < this.size; i++) {
            newArray[i] = this.array[i];
        }
        // 3) 让顺序表的 array 引用指向新数组
        this.array = newArray;
    }

相应的add方法也要稍加修改:

public boolean add(Long e) {
    ensureCapacity();  
    array[size] = e;
    size = size + 1;
    return true;
}


public void add(int index, Long e) {
    ensureCapacity();  
    if (index < 0 || index > size) {
         throw new ArrayIndexOutOfBoundsException("下标不合法");
    }
    for (int i = size - 1; i >= index; i--) {
        array[i + 1] = array[i];
    }
    array[index] = e;
    size = size + 1;
}

(5)自定义ArrayList的完整代码及测试过程

http://t.csdn.cn/XNIxK


五、实例演示

(1)扑克牌

http://t.csdn.cn/h9vZQ

(2)杨辉三角

http://t.csdn.cn/prEys


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小熊爱吃软糖吖

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

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

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

打赏作者

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

抵扣说明:

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

余额充值