【数据结构二】手撕顺序表与ArrayList源码详解

2023-12-28 22:30:42

目录

顺序表与ArrayList

1. 手撕顺序表

2.ArrayList的使用

3.ArrayList的源码分析(扩容机制)

4.力扣题练习


顺序表与ArrayList

? ? 线性表是在逻辑上具备线性结构的一种有序序列,包括顺序表和链表。其中顺序表的物理地址也连续,一般采用数组储存,在数组上完成对数据的增删改查。链表的物理地址不连续,通过记录下一个节点的地址来实现逻辑上的连续,通过对记录地址变量的修改来实现增删改查。

1. 手撕顺序表

对于任意一个继承list接口的数据结构我们都应该实现增删改查获取长度清空等方法,以及相应类的构造方法,我们知道Java中为了提高代码的复用,都是通过类继承接口的方式来进行代码试现,下面让我们写这样一个接口。

下面我们简单通过数组来实现下这个顺序表:

注意:我们自己的顺序表没有实现泛型机制,而java提供的ArrayList是实现了泛型的。

import java.util.Arrays;

public class MyArrayList implements MyList{
    private int[] array;
    private int size;
    MyArrayList(){
     array=new int[10];
     size=0;
    }
    //指定容量的构造器
    MyArrayList(int initcapacity){
       array=new int[initcapacity];
       size=0;
    }
    @Override
    public void add(int data) {
       if(size==array.length){
           Arrays.copyOf(array,array.length*2);
       }
       array[size]=data;
       size++;
    }

    @Override
    public void add(int pos, int data) {
    if (pos>size||pos<0){
        throw new posException();
    }
    if(size==array.length){
        Arrays.copyOf(array,array.length*2);
    }
    for(int i=size-1;i>=pos;i--){
        array[i+1]=array[i];
    }
    array[pos]=data;
    size++;
    }

    @Override
    public boolean contains(int toFind) {
        for(int i=0;i<size;i++){
            if(array[i]==toFind){
                return true;
            }
        }
        return false;
    }

    @Override
    public int indexOf(int toFind) {
        for (int i=0;i<size;i++){
            if(array[i]==toFind){
                return i;
            }
        }
        return -1;
    }

    @Override
    public int get(int pos) {
        if (pos>=size||pos<0){
            throw new posException();
        }
        return array[pos];
    }

    @Override
    public void set(int pos, int value) {
        if (pos>=size||pos<0){
            throw new posException();
        }
        array[pos]=value;
    }

    @Override
    public void remove(int toRemove) {
        int num=indexOf(toRemove);
       if(size==0){
           throw new emptyException();
       }
       for(int i=num;i<size-1;i++){
           array[i]=array[i+1];
       }
       size--;
    }

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

    @Override
    public void clear() {
      for (int i=0;i<size;i++){
          array[i]=0;
      }
      size=0;
    }
}

大家可以自己动手写一写这个代码实现。?

2.ArrayList的使用

ArrayList基本的增删改查操作与上述几乎一致,这里重点讲述ArrayList的遍历方法:

最常见的遍历方式有四种,分别是:

1.for循环+下标:

for(int i=0;i<list.length;i++){
System.out.print(list[i]+" ");
}

2.for-each遍历:

ArrayList<String> list = new ArrayList();
        for (String str:list) {
            System.out.print(str+" ");
        }

3.迭代器遍历:

ArrayList<String> list = new ArrayList();
        Iterator iterator=list.iterator();
        while (iterator.hasNext()){
            System.out.print(iterator.next()+" ");
}

4.集合对象的for-each方法(结合lambda表达式)

ArrayList<String> list = new ArrayList();
        list.add("awda");
        list.add("asdasd");
        list.forEach(str->{
            System.out.print(str+" ");
        });

?

3.ArrayList的源码分析(扩容机制)

ArrayList 是一个动态类型的顺序表,即:在插入元素的过程中会自动扩容。以下是 ArrayList 源码中扩容方式:
Object[] elementData; // 存放元素的空间
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 默认空间
private static final int DEFAULT_CAPACITY = 10; // 默认容量大小
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// 获取旧空间大小
int oldCapacity = elementData.length;
// 预计按照1.5倍方式扩容
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果用户需要扩容大小 超过 原空间1.5倍,按照用户所需大小扩容
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果需要扩容大小超过MAX_ARRAY_SIZE,重新计算容量大小
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 调用copyOf扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
// 如果minCapacity小于0,抛出OutOfMemoryError异常
if (minCapacity < 0)
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}

总结:

  1. ArrayList顺序表在无参构造时初始容量大小是10。
  2. 在添加元素时,检查是否需要扩容,如果是调用grow方法扩容。
  3. 初步预估按照 1.5 倍大小扩容
    如果用户所需大小超过预估 1.5 倍大小,则按照用户所需大小扩容
    真正扩容之前检测是否能扩容成功,防止太大导致扩容失败
  4. 用Arrays.copyOf()方法进行扩容。

4.力扣题练习

问题链接:杨辉三角

问题:?

思路:?通过泛型内在放一个顺序表类的方法实现二维列表,然后再利用循环先将每一层的数值填入numRows个顺序表中,再把这numRows个顺序表填入另一个顺序表中,最后这个顺序表就包含了我们想要的结果。

解题答案:?

文章来源:https://blog.csdn.net/2203_75565612/article/details/135185277
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。