java小白翻身-如何手写山寨版ArrayList

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

要是有一种数据结构,我只管往里头添加东西,要是容量不够,它就能自动给我扩容该有多好?

大家回忆一下,当初写LinkedList的时候,是不是没遇到需要扩容的情况?

为什么?那是因为LinkedList内部维护了一个链表,链表的话只要你愿意,内存足够,你就可以无限制地加下去。

但是数组就不行,因为数组的长度是固定的,超过这个长度就会报错。所以,我们需要在内部做一个判断,如果快超限了,就给数组扩容。这也是ArrayList内部的基本原理。

内部的数组

========================================================================

和LinkedList的套路一样,LinkedList实际上真正起作用的是内部的Node,而ArrayList内部就是维护一个数组。这其实就是数组的强化,ArrayList是强化版的数组。

步骤 1 从零开始敲代码

image

老规矩,从零开始,创建一个Class。

内部不是要维护一个数组吗,就设置一个Object类型的数组,能存放任何的类型。

public class TuziArrayList {

private Object[] array;

}

到时候遍历啥的,是不是得有一个下标,于是就弄个index。

public class TuziArrayList {

private Object[] array;

private int index;

}

数组的总长度是多少啊?为了方便,再来个size属性。现在是我们自己来开发,所以不需要去看JDK里面的ArrayList到底怎么设计的,先按照我们自己的思路来。

public class TuziArrayList {

private Object[] array;

private int index;

private int size;

}

步骤 2 构造方法

接下来设计构造方法,直接new的话,就给数组一个默认的长度,所以还得来个默认的长度变量。

public class TuziArrayList {

private Object[] array;

private int index;

private int size;

private int capacity = 10; // 默认长度为10

public TuziArrayList(){

array = new Object[capacity];

}

}

当然,也可以指定内部数组的长度,传一个容量进去。

public TuziArrayList(int capacity){

this.capacity = capacity;

array = new Object[capacity];

}

为什么这么设计?因为假如你知道这个数组需要存放很多很多的数据,那么就可以一次性把数组长度设置大一点,免得到时候一次次地扩容影响效率。

add方法

========================================================================

给ArrayList添加元素,其实就是给内部的数组添加元素。

public void add(Object o){

array[index++] = o;//给数组赋值

size++;//注意,这个是数组内实际元素的个数,而不是数组的总长度

}

代码很清晰,下标要++,size也要++。

测试:

public static void main(String[] args) {

TuziArrayList list = new TuziArrayList();

for (int i = 0; i < 12; i++) {

list.add(i);

}

}

报错:

Exception in thread “main” java.lang.ArrayIndexOutOfBoundsException: 10

这是因为数组总长度是10,只能存放下标为0-9的数据,所以溢出了。

为了解决上面的问题,还是得数组扩容。我们可以定义一个加载引子,factor,当超过这个百分比时,就让数组自动扩容。

private float factor = 0.75f;

还是老规矩,先写设计占坑。

public void add(Object o){

if(index >= capacity * factor){

enlarge();

}

array[index++] = o;//给数组赋值

size++;//注意,这个是数组内实际元素的个数,而不是数组的总长度

}

反正我就是要enlarge,怎么实现待会再说。这个enlarge方法,就是让数组自动扩容一倍,然后原来的数据要拷贝过新的数组。

private void enlarge() {

//数组容量扩大一倍

capacity *= 2;

//创建新数组

Object[] arrNew = new Object[capacity];

}

写到这里完全没有难度啊,有木有!

但是,如何拷贝数组呢?有了,java本身提供一个好的方法:

System.arraycopy(src, srcPos, dest, destPos, length);

参数的含义(看了api,我反复琢磨以后,感觉这样翻译比较好)

src : 需要拷贝的数组。

srcPos : 从哪里开始 拷贝?

dest : 目标数组

destPos : 从哪里开始 粘贴?

length : 拷贝的元素个数

private void enlarge() {

//数组容量扩大一倍

capacity *= 2;

//创建新数组

Object[] arrNew = new Object[capacity];

//拷贝数组

System.arraycopy(array, 0, arrNew, 0, size);

//把新的数组赋值给原数组

array = arrNew;

}

嗯,应该挺对的,赶紧来试试。

果然,原来的方法不报错了。为了看起来方便,我们下一节来设计toString方法。

toString方法

=============================================================================

toString方法可以方便我们观察ArrayList里面的数据。

public String toString(){

StringBuffer sb = new StringBuffer();

for (int i = 0; i < size; i++) {

if(i == size - 1)

sb.append(array[i]);

else

sb.append(array[i]+“,”);

}

return sb.toString();

}

public static void main(String[] args) {

TuziArrayList list = new TuziArrayList();

for (int i = 0; i < 12; i++) {

list.add(i);

}

System.out.println(list);

}

image

remove方法

===========================================================================

remove方法用来删除某一个元素。

步骤 1 两种删除方法

首先,我们看下ArrayList的remove方法是怎么做的。

第一种,是通过下标删除。

public E remove(int index)

第二种,是根据对象删除。

public boolean remove(Object o)

步骤 2 根据下标删除

这个下标其实就是内部数组的下标,众所周知,数组删除元素一向是个令人头疼的问题,下面演示一种推荐的做法,没错,还是用arrayCopy方法。

假如数组是这样的:

image

我想要删除元素2,也就是index = 1的位置。

image

2拿掉,后面的3和4左移一个单位不就好了吗?

再复习一下之前arrayCopy的用法:

System.arraycopy(src, srcPos, dest, destPos, length);

参数的含义(看了api,我反复琢磨以后,感觉这样翻译比较好)

src : 需要拷贝的数组。

srcPos : 从哪里开始 拷贝

dest : 目标数组

destPos : 从哪里开始 粘贴

length : 拷贝的元素个数

憋口气,一下子就写出下面的代码:

System.arraycopy(array, index+1, array, index, size - index -1);

size是4,index是1,所以size-index-1就是2, 什么 是快乐星球?

哦不好意思,跑错片场了。

2是什么,2不就是后面需要移动的元素个数嘛!所以,最后一个参数就是拷贝的元素个数。

但是,这样一来就有个问题:

image

如图,因为数组的长度还是4,remove掉一个元素后,最后一个位置就空了。我们需要手动把它置为空,这里如果不置空,它将会保存着一个引用,那么垃圾收集器将无法回收它,可能会造成内存泄漏。

最终源码:

public Object remove(int index){

//边界检测方法

rangeCheck(index);

Object oldValue = array[index];

System.arraycopy(array, index+1, array, index, size - index -1);

array[–size] = null;

return oldValue;

}

private void rangeCheck(int index) {

if(index < 0 || index >= size){

throw new ArrayIndexOutOfBoundsException(index);

}

}

步骤 3 根据元素删除

如果上面的方法理解了,那么这个方法简直就是soEasy,不就是遍历一遍数组的所有元素,然后重新调用上面的方法么?

说干就干。

public Object remove(Object e){

for (int i = 0; i < size; i++) {

if(array[i].equals(e)){

remove(i);

}

}

return true;

}

步骤 4 问题1

第一个问题,假如我给ArrayList添加null咋办?

image

如果元素是null,那么这个地方直接.equals肯定会报空指针的。所以,代码需要优化一下。

public Object remove(Object e){

for (int i = 0; i < size; i++) {

if(array[i] != null && array[i].equals(e)){

remove(i);

}

}

return true;

}

这边用了短路与,必须左边的表达式为true,右边的才会执行。

步骤 5 问题1–如何元素值为null怎么办?

第一个问题,假如我给ArrayList添加null咋办?

image

如果元素是null,那么这个地方直接.equals肯定会报空指针的。所以,代码需要优化一下。

public Object remove(Object e){

for (int i = 0; i < size; i++) {

if(array[i] != null && array[i].equals(e)){

remove(i);

}

}

return true;

}

这边用了短路与,必须左边的表达式为true,右边的才会执行。

步骤 6 问题2–怎么总是返回true

这个问题非常明显,就是return的地方很明显错了,改一下:

public Object remove(Object e){

for (int i = 0; i < size; i++) {

if(array[i] != null && array[i].equals(e)){

remove(i);

return true;

}

}

return false;

}

步骤 7 问题3–如果remove传进来的值是null?

如果remove传进来的值是null?还真有这种可能,虽然在当前的逻辑下,最多就是循环一圈找不到匹配的,返回false。但是,这样还是很影响效率,不如一开始就把这个情况判断掉。

public Object remove(Object e){

if(e == null)

return false;

for (int i = 0; i < size; i++) {

if(array[i] != null && array[i].equals(e)){

remove(i);

最后

本人也收藏了一份Java面试核心知识点来应付面试,借着这次机会可以送给我的读者朋友们

目录:

全靠这套面试题,才让我有惊无险美团二面拿offer  (面经解析)

Java面试核心知识点

一共有30个专题,足够读者朋友们应付面试啦,也节省朋友们去到处搜刮资料自己整理的时间!

全靠这套面试题,才让我有惊无险美团二面拿offer  (面经解析)

Java面试核心知识点

已经有读者朋友靠着这一份Java面试知识点指导拿到不错的offer了

全靠这套面试题,才让我有惊无险美团二面拿offer  (面经解析)

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!
& array[i].equals(e)){

remove(i);

return true;

}

}

return false;

}

步骤 7 问题3–如果remove传进来的值是null?

如果remove传进来的值是null?还真有这种可能,虽然在当前的逻辑下,最多就是循环一圈找不到匹配的,返回false。但是,这样还是很影响效率,不如一开始就把这个情况判断掉。

public Object remove(Object e){

if(e == null)

return false;

for (int i = 0; i < size; i++) {

if(array[i] != null && array[i].equals(e)){

remove(i);

最后

本人也收藏了一份Java面试核心知识点来应付面试,借着这次机会可以送给我的读者朋友们

目录:

[外链图片转存中…(img-8EVw0jWv-1714684356896)]

Java面试核心知识点

一共有30个专题,足够读者朋友们应付面试啦,也节省朋友们去到处搜刮资料自己整理的时间!

[外链图片转存中…(img-2FX78f1Z-1714684356897)]

Java面试核心知识点

已经有读者朋友靠着这一份Java面试知识点指导拿到不错的offer了

[外链图片转存中…(img-KrWfz2vN-1714684356897)]

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

  • 16
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: ArrayListJava中的一个类,它可以动态地存储一组元素,可以根据需要动态增加或减少元素的数量,是一种非常方便的数据结构。 ArrayList的基本用法包括创建ArrayList对象、添加元素、获取元素、修改元素、删除元素等。首先,可以使用如下代码创建一个空的ArrayList对象: ```java ArrayList<String> list = new ArrayList<String>(); ``` 上面代码创建了一个类型为String的ArrayList对象,该对象初始为空。然后,可以使用add()方法向ArrayList中添加元素,例如: ```java list.add("apple"); list.add("banana"); list.add("orange"); ``` 上述代码向list中添加了三个字符串元素。可以使用get()方法获取ArrayList中的元素,例如: ```java String first = list.get(0); // 获取第一个元素 ``` 可以使用set()方法修改ArrayList中的元素,例如: ```java list.set(1, "pear"); // 将第2个元素改为"pear" ``` 可以使用remove()方法删除ArrayList中的元素,例如: ```java list.remove(2); // 删除第3个元素 ``` 以上就是ArrayList的基本用法。需要注意的是,ArrayList中的索引从0开始。 ### 回答2: ArrayListJava中非常常用的数据结构。它提供了一个可以动态添加、删除、修改的可变长度的序列。 使用ArrayList时,首先需要引入它的包:java.util。然后可以使用如下语法创建一个ArrayList对象: ```java ArrayList<String> list = new ArrayList<String>(); ``` 这里的`<String>`说明了这个ArrayList中的元素类型是String。当然,也可以使用其他类型作为元素类型。例如: ```java ArrayList<Integer> numbers = new ArrayList<Integer>(); ArrayList<Double> prices = new ArrayList<Double>(); ``` 可以使用`add()`方法来向ArrayList中添加元素: ```java list.add("apple"); list.add("orange"); list.add("banana"); ``` 可以使用`get()`方法来获取指定位置的元素: ```java String fruit = list.get(1); //获取第二个元素,即"orange" ``` 可以使用`size()`方法来获取ArrayList中元素的个数: ```java int size = list.size(); //获取ArrayList中元素的个数 ``` 可以使用`set()`方法来修改指定位置的元素: ```java list.set(1, "pear"); //将第二个元素修改为"pear" ``` 可以使用`remove()`方法来删除指定位置的元素: ```java list.remove(2); //删除第三个元素,即"banana" ``` 需要注意的是,ArrayList中的元素是有序的,且可以重复。因此,可以使用循环来遍历ArrayList中的元素: ```java for(int i=0; i<list.size(); i++){ String fruit = list.get(i); System.out.println(fruit); } ``` 或者使用增强型循环(foreach): ```java for(String fruit : list){ System.out.println(fruit); } ``` 总之,使用ArrayList可以方便地处理可变长度的序列。在实际开发中,它有着广泛的应用场景,例如处理文件或数据库中的数据,实现算法或数据结构等。 ### 回答3: ArrayListJava中一个非常常用的容器类。他的优点是可以存储任意类型的对象,可以动态扩容,因此在使用上非常的方便。 使用ArrayList需要在代码中首先调用import java.util.ArrayList进行导入,随后可以通过ArrayList<类型> name = new ArrayList<类型>()这个语句声明一个ArrayList,并将其命名为name,同时指定ArrayList中存储的对象类型为类型。当我们需要添加元素时,可以通过name.add(element)将元素添加到ArrayList中。我们也可以通过name.get(index)方法获取ArrayList中指定位置的元素,通过name.set(index,value)方法将ArrayList中某个位置的元素替换为新的元素。同时,我们也可以调用name.size()方法获取ArrayList中元素的数量。 值得注意的是,ArrayList中的元素是以索引的方式存储的,这意味着我们可以根据元素的位置进行添加、修改、删除等操作。而且,由于ArrayList的容量是可变的,因此其内部必须动态地管理数据的内存,这会影响到ArrayList的性能。当然,这个影响是很小的,不会对代码的运行产生显著的影响。 总之,ArrayListJava中非常常用的容器类,其可以存储任意类型的对象,同时调用也非常方便。但在使用时需要注意其操作的复杂度,以及不能存储基本数据类型。如果需要在ArrayList中存储基本数据类型,需要借助Boxing和Unboxing机制将其转换为对应的包装类。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值