前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【数据结构二】手撕顺序表与ArrayList源码详解

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

作者头像
小皮侠
发布2024-04-08 20:40:21
880
发布2024-04-08 20:40:21
举报

顺序表与ArrayList

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

1. 手撕顺序表

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

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

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

代码语言:javascript
复制
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循环+下标:

代码语言:javascript
复制
for(int i=0;i<list.length;i++){
System.out.print(list[i]+" ");
}

2.for-each遍历:

代码语言:javascript
复制
ArrayList<String> list = new ArrayList();
        for (String str:list) {
            System.out.print(str+" ");
        }

3.迭代器遍历:

代码语言:javascript
复制
ArrayList<String> list = new ArrayList();
        Iterator iterator=list.iterator();
        while (iterator.hasNext()){
            System.out.print(iterator.next()+" ");
}

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

代码语言:javascript
复制
ArrayList<String> list = new ArrayList();
        list.add("awda");
        list.add("asdasd");
        list.forEach(str->{
            System.out.print(str+" ");
        });

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

ArrayList 是一个动态类型的顺序表,即:在插入元素的过程中会自动扩容。以下是 ArrayList 源码中扩容方式:

代码语言:javascript
复制
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个顺序表填入另一个顺序表中,最后这个顺序表就包含了我们想要的结果。

解题答案:

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-04-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 顺序表与ArrayList
    • 1. 手撕顺序表
      • 2.ArrayList的使用
        • 3.ArrayList的源码分析(扩容机制)
          • 4.力扣题练习
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档