1 手写ArrayList核心源码

手写ArrayList核心源码

ArrayList是Java中常用的数据结构,不光有ArrayList,还有LinkedList,HashMap,LinkedHashMap,HashSet,Queue,PriorityQueue等等,我们将手写这些常用的数据结构的核心源码,用尽量少的代码来揭示核心原理。

下面我们来手写ArrayList的核心源码

首先我们定义一个QArrayList,不要问为什么叫QArrayList,因为我之前写过Qt,仅此而已。源码 public class<T> QArrayList,Java中的ArrayList的底层就是用一个Object[] 结构来保存数据的。我们也要定义一个Object[] 属性。

而且我们还要定义一个默认的数据的大小,以便在调用默认构造函数的情况下使用。 private final int DEFAULT_LIST_SIZE = 8;

还要定义一个 int mSize 变量,mSize 默认为0代表下一个可以存放数据的数组的索引代表下一个可以存放数据的数组的索引代表下一个可以存放数据的数组的索引 重要的事情说三遍

到现在为止我们的类如下:

1public class QList<T> { 2 //默认的数组的大小 3 private final int DEFAULT_LIST_SIZE = 8; 4 5 //存放数据的地方 6 private Object[] mData; 7 8 //下一个可以存放数据的当前数组的索引 9 private int mSize; 10 11 ...... 12}

好了,存放数据的数组也有了,下一个可以存放数据的当前的数组的索引也有了 ArrayList 底层是用数组存放数据,那么会有一个问题,如果此时数组满了我们再往里面存放数据的时候,怎么办呢?ArrayList是再新建一个数组,新数组的大小是原来数组大小的2倍,那么我们也这样做。

此时,我们实现 add,get,remove,resize等这几个核心方法,QArrayList完整的代码如下 :

1public class QArrayList<T> { 2 //默认的数组的大小 3 private final int DEFAULT_LIST_SIZE = 8; 4 5 //存放数据的地方 6 private Object[] mData; 7 8 //下一个可以存放数据的当前数组的索引 9 private int mSize; 10 11 public QArrayList() { 12 //new 一个数组,用来存放 13 mData = new Object[DEFAULT_LIST_SIZE]; 14 15 //下一个可以存放数据的当前数组的索引为0 16 mSize = 0; 17 } 18 19 public QArrayList(int capacity){ 20 if(capacity <= 0 || capacity > Integer.MAX_VALUE){ 21 throw new RuntimeException("invalid capacity"); 22 } 23 24 mData = new Object[capacity]; 25 mSize = 0; 26 } 27 28 //返回当时数组的已经存放了多少个元素 29 public int size() { 30 return mSize; 31 } 32 33 //返回数组的总大小,其实这个接口没有必要对外提供,这里我们只是为了演示用 34 public int capacity() { 35 return mData.length; 36 } 37 38 //添加一个元素 39 public void add(T e) { 40 //规定不允许添加一个空元素 41 if(e == null){ 42 return; 43 } 44 45 //如果当前数组已经满了,扩容为原来数组的2倍 46 if (mSize >= mData.length) { 47 48 //扩容 49 resize(); 50 } 51 52 //将添加的元素添加到数组中 53 mData[mSize] = e; 54 55 //同时 mSize++ 指向下一个可以存放数据的位置 56 mSize++; 57 } 58 59 //获取指定位置的元素,如果position不合法,直接抛出异常 60 //这样做是有必要的,我们提供的是一个库 61 // 直接抛出异常让使用知道用错了,没有必要 return null 62 // 因为这是个库,不是业务,就算return null,也是业务层的事 63 public T get(int position) { 64 if (position < 0 || position >= mData.length) { 65 throw new RuntimeException("position is invalid"); 66 } 67 68 // position 大于 mSize 也没有关系,因为也是返回null,证明没有获取到 69 return (T) mData[position]; 70 } 71 72 //删除指定位置的元素 73 public T remove(int position) { 74 //和上面一样,位置不合法直接抛出异常 75 if (position < 0 || position >= mData.length) { 76 throw new RuntimeException("position is invalid"); 77 } 78 79 //把当前要删除的元素保存下来,最后返回要删除的元素 80 T e = (T) mData[position]; 81 82 //删除后,把后面的所有元素都往前移位 83 for (int i = position + 1; i < mData.length; i++) { 84 mData[i - 1] = mData[i]; 85 } 86 87 //别忘了 mSize 要 -- 88 mSize--; 89 90 //返回删除的元素 91 return e; 92 } 93 94 //删除指定的元素 95 public boolean remove(T e) { 96 //因为数组可能没有满,如果删除的是null,没有必要,我们不允许 97 if (e == null) { 98 return false; 99 } 100 101 //找到删除元素的位置 102 int position = -1; 103 for (int i = 0; i < mData.length; i++) { 104 if (e == mData[i] || e.equals(mData[i])) { 105 position = i; 106 break; 107 } 108 } 109 110 //没有找到就返回 111 if (position == -1) { 112 return false; 113 } 114 115 //删除 116 return remove(position) != null; 117 } 118 119 //扩容,我们都以2倍的容量扩容 120 private void resize() { 121 Object[] old = mData; 122 mData = new Object[mData.length * 2]; 123 for (int i = 0; i < old.length; i++) { 124 mData[i] = old[i]; 125 } 126 127 old = null; 128 } 129} 130

注释都有相关的解释 我们来测试,测试代码如下:

1 public static void main(String[] args) { 2 QArrayList<String> list = new QArrayList<>(); 3 list.add("tom"); 4 list.add("jim"); 5 list.add("lilei"); 6 list.add("hanmeimei"); 7 8 System.out.println("list.get(2)=" + list.get(2)); 9 System.out.println("list.size()=" + list.size()); 10 for (int i = 0; i < list.size(); i++) { 11 System.out.println("list.get(" + i + ") = " + list.get(i)); 12 } 13 14 System.out.println("======================="); 15 System.out.println("演示删除操作"); 16 list.remove("jim"); 17 18 for (int i = 0; i < list.size(); i++) { 19 System.out.println("list.get(" + i + ") = " + list.get(i)); 20 } 21 } 22

输出如下: list.get(2)=lilei list.size()=4 list.get(0) = tom list.get(1) = jim list.get(2) = lilei list.get(3) = hanmeimei ============ 演示删除操作 list.get(0) = tom list.get(1) = lilei list.get(2) = hanmeimei

但是最重要的扩容功能还没有演示,下面是扩容演示的测试代码:

1 public static void main(String[] args) { 2 //新建一个只有2个元素的数组 3 QArrayList<String> list = new QArrayList<>(2); 4 5 //打印出扩容后的容量 6 System.out.println("扩容前 : list.capacity()=" + list.capacity()); 7 8 //我们添加了4个元素 9 list.add("tom"); 10 list.add("jim"); 11 list.add("lilei"); 12 list.add("hanmeimei"); 13 14 //打印出扩容后的容量 15 System.out.println("扩容后 : list.capacity()=" + list.capacity()); 16 17 //打印 18 for (int i = 0; i < list.size(); i++) { 19 System.out.println("list.get(" + i + ") = " + list.get(i)); 20 } 21 }

输出如下:

扩容前 : list.capacity()=2 扩容后 : list.capacity()=4 list.get(0) = tom list.get(1) = jim list.get(2) = lilei list.get(3) = hanmeimei

可以看到,我们新建了一个底层只有2个元素的数组,但是我们添加了4个元素,我们打印出扩容后的数组的容量是 4 ,可见我们的扩容机制是没有问题的。

以上就是QArrayList的核心原理,我们下节手写LinkedList的核心原理

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

手写 ArrayList 核心源码

手写ArrayList核心源码手写ArrayList核心源码ArrayList是Java中常用的数据结构,不光有ArrayList,还有LinkedList,HashMap,LinkedHashMap,HashSet