List接口、动态数组的增删查改

List接口

什么是接口?

在集合框架中,List是一个接口,继承自Collection。

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

特点:

1、它是一个元素存取有序的集合。例如,存元素的顺序是11、22、33。那么集合中,元素的存储就是按照11、22、33的顺序完成的)。
2、它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)。
3、集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。

常见方法

方法解释
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 subList(int fromIndex, int toIndex)截取部分 list

List的使用

注意:List是个接口,并不能直接用来实例化。
如果要使用,必须去实例化List的实现类。在集合框架中,ArrayList和LinkedList都实现了List接口。
例如

public class Test {
    public static void main(String[] args) {
        List<String> list = new LinkedList<>();
        list.add("我");
        list.add("爱");
        list.add("学");
        list.add("习");
        //在索引为1的位置添加”不“
        list.add(1,"不");
        System.out.println(list);
        System.out.println(list.contains("不"));
        System.out.println(list.contains("嘻嘻"));
        System.out.println(list.get(2));
        String index  = String.valueOf(list.indexOf("不"));
        list.remove(index);
        System.out.println(list);
        //将索引为1的数替换为
        list.set(1,"超级");
        System.out.println(list);
    }

细节:
当List接口保存的是整型,方法默认按照索引删除,若要按值删除,先调用indexOf方法获取该值对应的索引,再调用remove方法删除。

动态数组

动态数组 :对于用户来说,使用动态数组不需要关心数组长度,解决原生语言中数组定长的问是本质上,所谓的动态数组只是在基本数组的基础上封装到了类中,至于数组的长度由类的内部来进行扩容操作。

数组的扩容处理:

 public void add(E element) {
        this.elementDate[size] = element;
        size++;
        if (size == elementDate.length) {
            //当前数组已满,需要扩容
            grow();
        }
    }
  private void grow() {
        int oldLength = elementDate.length;
        // 默认扩容为原来的一倍
        int newLength = oldLength << 1;
        // 将原数组中的所有内容拷贝到新数组中,超出的部分使用默认值来填充
        Object[] newArray = Arrays.copyOf(elementDate,newLength);
        elementDate= newArray;
    }
public class MyArrayTest {
    public static void main(String[] args) {
        Seqlist<Integer> list = new MyArray<>(4);
        list.add(1);
        list.add(3);
        list.add(5);
        list.add(7);
        list.add(9);
        System.out.println(list);
    }
}

在这里插入图片描述
在这里插入图片描述
在Debug模式下进行调试观察数组在存入7的时候,长度变为原来的两倍。

增删查改

public class MyArray<E> implements Seqlist<E> {
    //保存当前线性表中真正保存的有效元素个数
private Object[] elementDate;
private int size;

public MyArray(){
this(10);
}
//从外部传入一个数组长度进行初始化操作
public MyArray(int size){
    this.elementDate = new Object[size];
}
//默认尾插
    @Override
    public void add(E element) {
        this.elementDate[size] = element;
        size++;
        if (size == elementDate.length) {
            //当前数组已满,需要扩容
            grow();

        }
    }
		//对内部的数组进行扩容处理
    private void grow() {
        int oldLength = elementDate.length;
        // 默认扩容为原来的一倍
        int newLength = oldLength << 1;
        // 将原数组中的所有内容拷贝到新数组中,超出的部分使用默认值来填充
        Object[] newArray = Arrays.copyOf(elementDate,newLength);
        elementDate= newArray;
    }
//从中间插入元素
    @Override
    public void add(int index, E element) {
		 //判断用户传入的index是否合法
        if(index < 0 || index >size){
            throw new IllegalArgumentException("add index illegal!");
        }
    //index == size
    if(index == size) {
        add(element);
        return;
    }
    //0 <=index <size
        for (int i = size - 1; i>= index; i--) {
            elementDate[i + 1] = elementDate[i];
        }
	//此时index位置待插入
            elementDate[index] = element;
            size++;
            if(size == elementDate.length){
                grow();
            }
        }
//删除指定索引位置的数
    @Override
    public E removeByIndex(int index) {
        // 1.index合法性校验
        if(!rangeCheck(index)) {
            throw new IllegalArgumentException("remove index illegal!");
        }
        E oldVal = (E) elementDate[index];
        // 从待删除元素开始,将当前位置的值覆盖为下一个元素的值
        // 由于删除场景下索引不能取到size
        // 因此要保证代码中所有位置均不越界,i + 1 < size
        for (int i = index; i < size - 1; i++) {
            elementDate[i] = elementDate[i + 1];
        }
        // 维护一下size属性,最后多的那个元素不影响我们的使用~下次添加元素时就会自动覆盖掉
        size --;
        return oldVal;
    }
//删除数组中某个元素
    @Override
    public void removeByValue(E element) {
        // 从前向后遍历,碰到第一个值为待删除的元素时,删除该元素并返回 ~
        for (int i = 0; i < size; i++) {
            if (elementDate[i].equals(element)) {
                // 碰到第一个待删除的元素
                removeByIndex(i);
                return;
            }
        }
        System.out.println("没有待删除的元素!");
    }
//删除数组中值为element的所有元素
    @Override
    public void removeAllValue(E element) {
        // 从前向后遍历,碰到索引对应的元素为待删除的元素,删除即可
        for (int i = 0; i < size;) {
            if (elementDate[i].equals(element)) {
                // 当i指向的元素是待删除的元素,不能移动i
                removeByIndex(i);
            }else {
                // 此时i指向的不是待删除元素,此时移动i的指向,继续判断下一个元素是否是待删除的元素
                i ++;
            }
        }
    }
//修改索引为index的元素为element
    @Override
    public E set(int index, E element) {
        if(!rangeCheck(index)){
            throw new IllegalArgumentException("set index illegal!");
        }
        E oldVal = (E) elementDate[index];
        elementDate[index] = element;
        return oldVal;
    }

//查找数组中某个元素
    @Override
    public E get(int index) {
    if(!rangeCheck(index)){
        throw new IllegalArgumentException("get index illegal!");
    }
        return (E) elementDate[index];
    }
    private boolean rangeCheck(int index) {
        if(index < 0 || index >= size){
        return false;
    }
        return true;
    }
    @Override
    public boolean contains(E element) {
            for (int i = 0; i < size; i++) {
                if (elementDate[i].equals(element)) {
                    return true;
                }
            }
            return false;
        }
    @Override
    public int indexOf(E element) {
        for (int i = 0; i < size; i++) {
            if (elementDate.equals(element)) {
                return i;
            }
        }
        throw new NoSuchElementException("indexOf error!" +
                "Don't has this element");
    }
public String toString(){
    StringBuilder sb = new StringBuilder("[");
    for (int i = 0; i < size; i++) {
        sb.append(elementDate[i]);
        if( i != size - 1){
            sb.append(",");
        }
    }
    sb.append("]");
    return sb.toString();
        }
}

总结:

①动态数组在中间位置进行插入和删除,操作比较耗时。因为此时需要来回搬移元素。

②动态数组由于底层仍然使用的是数组,因此按索引查询值是比较快的。立即可以取得值。

千万不要武断的说,动态数组的插入一定就慢,若调用默认的add方法,尾插,也是很快的,不需要元素搬移~
除了增删查之外,动态数组一般需要连续的空间,当数据量较大时,有很大一部分空间的浪费~

假设此时数组长度为100,元素存满了,要保存下一个元素,就需要进行扩容,扩容之后数组长度变为了200
为了多存一个元素,浪费了99个空间
有没有一种数据结构,既能不关心元素的长度,又能比较快速的进行插入和删除,也不太浪费空间,由此我们开始进入链表的学习。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小杨爱喝橙汁

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

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

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

打赏作者

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

抵扣说明:

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

余额充值