手写 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代表下一个可以存放数据的数组的索引代表下一个可以存放数据的数组的索引代表下一个可以存放数据的数组的索引 重要的事情说三遍

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

public class QList<T> {
//默认的数组的大小
private final int DEFAULT_LIST_SIZE = 8; //存放数据的地方
private Object[] mData; //下一个可以存放数据的当前数组的索引
private int mSize; ......
}

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

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

public class QArrayList<T> {
//默认的数组的大小
private final int DEFAULT_LIST_SIZE = 8; //存放数据的地方
private Object[] mData; //下一个可以存放数据的当前数组的索引
private int mSize; public QArrayList() {
//new 一个数组,用来存放
mData = new Object[DEFAULT_LIST_SIZE]; //下一个可以存放数据的当前数组的索引为0
mSize = 0;
} public QArrayList(int capacity){
if(capacity <= 0 || capacity > Integer.MAX_VALUE){
throw new RuntimeException("invalid capacity");
} mData = new Object[capacity];
mSize = 0;
} //返回当时数组的已经存放了多少个元素
public int size() {
return mSize;
} //返回数组的总大小,其实这个接口没有必要对外提供,这里我们只是为了演示用
public int capacity() {
return mData.length;
} //添加一个元素
public void add(T e) {
//规定不允许添加一个空元素
if(e == null){
return;
} //如果当前数组已经满了,扩容为原来数组的2倍
if (mSize >= mData.length) { //扩容
resize();
} //将添加的元素添加到数组中
mData[mSize] = e; //同时 mSize++ 指向下一个可以存放数据的位置
mSize++;
} //获取指定位置的元素,如果position不合法,直接抛出异常
//这样做是有必要的,我们提供的是一个库
// 直接抛出异常让使用知道用错了,没有必要 return null
// 因为这是个库,不是业务,就算return null,也是业务层的事
public T get(int position) {
if (position < 0 || position >= mData.length) {
throw new RuntimeException("position is invalid");
} // position 大于 mSize 也没有关系,因为也是返回null,证明没有获取到
return (T) mData[position];
} //删除指定位置的元素
public T remove(int position) {
//和上面一样,位置不合法直接抛出异常
if (position < 0 || position >= mData.length) {
throw new RuntimeException("position is invalid");
} //把当前要删除的元素保存下来,最后返回要删除的元素
T e = (T) mData[position]; //删除后,把后面的所有元素都往前移位
for (int i = position + 1; i < mData.length; i++) {
mData[i - 1] = mData[i];
} //别忘了 mSize 要 --
mSize--; //返回删除的元素
return e;
} //删除指定的元素
public boolean remove(T e) {
//因为数组可能没有满,如果删除的是null,没有必要,我们不允许
if (e == null) {
return false;
} //找到删除元素的位置
int position = -1;
for (int i = 0; i < mData.length; i++) {
if (e == mData[i] || e.equals(mData[i])) {
position = i;
break;
}
} //没有找到就返回
if (position == -1) {
return false;
} //删除
return remove(position) != null;
} //扩容,我们都以2倍的容量扩容
private void resize() {
Object[] old = mData;
mData = new Object[mData.length * 2];
for (int i = 0; i < old.length; i++) {
mData[i] = old[i];
} old = null;
}
}

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

    public static void main(String[] args) {
QArrayList<String> list = new QArrayList<>();
list.add("tom");
list.add("jim");
list.add("lilei");
list.add("hanmeimei"); System.out.println("list.get(2)=" + list.get(2));
System.out.println("list.size()=" + list.size());
for (int i = 0; i < list.size(); i++) {
System.out.println("list.get(" + i + ") = " + list.get(i));
} System.out.println("=======================");
System.out.println("演示删除操作");
list.remove("jim"); for (int i = 0; i < list.size(); i++) {
System.out.println("list.get(" + i + ") = " + list.get(i));
}
}

输出如下: 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

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

 public static void main(String[] args) {
//新建一个只有2个元素的数组
QArrayList<String> list = new QArrayList<>(2); //打印出扩容后的容量
System.out.println("扩容前 : list.capacity()=" + list.capacity()); //我们添加了4个元素
list.add("tom");
list.add("jim");
list.add("lilei");
list.add("hanmeimei"); //打印出扩容后的容量
System.out.println("扩容后 : list.capacity()=" + list.capacity()); //打印
for (int i = 0; i < list.size(); i++) {
System.out.println("list.get(" + i + ") = " + list.get(i));
}
}

输出如下:

扩容前 : 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 的核心原理

手写 ArrayList 核心源码的更多相关文章

  1. 1 手写Java ArrayList核心源码

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

  2. 手撕spring核心源码,彻底搞懂spring流程

    引子 十几年前,刚工作不久的程序员还能过着很轻松的日子.记得那时候公司里有些开发和测试的女孩子,经常有问题解决不了的,不管什么领域的问题找到我,我都能帮她们解决.但是那时候我没有主动学习技术的意识,只 ...

  3. 3 手写Java HashMap核心源码

    手写Java HashMap核心源码 上一章手写LinkedList核心源码,本章我们来手写Java HashMap的核心源码. 我们来先了解一下HashMap的原理.HashMap 字面意思 has ...

  4. 2 手写Java LinkedList核心源码

    上一章我们手写了ArrayList的核心源码,ArrayList底层是用了一个数组来保存数据,数组保存数据的优点就是查找效率高,但是删除效率特别低,最坏的情况下需要移动所有的元素.在查找需求比较重要的 ...

  5. 6 手写Java LinkedHashMap 核心源码

    概述 LinkedHashMap是Java中常用的数据结构之一,安卓中的LruCache缓存,底层使用的就是LinkedHashMap,LRU(Least Recently Used)算法,即最近最少 ...

  6. 面试必会之ArrayList源码分析&手写ArrayList

    简介 ArrayList是我们开发中非常常用的数据存储容器之一,其底层是数组实现的,我们可以在集合中存储任意类型的数据,ArrayList是线程不安全的,非常适合用于对元素进行查找,效率非常高. 线程 ...

  7. 并发编程之 SynchronousQueue 核心源码分析

    前言 SynchronousQueue 是一个普通用户不怎么常用的队列,通常在创建无界线程池(Executors.newCachedThreadPool())的时候使用,也就是那个非常危险的线程池 ^ ...

  8. HashMap的结构以及核心源码分析

    摘要 对于Java开发人员来说,能够熟练地掌握java的集合类是必须的,本节想要跟大家共同学习一下JDK1.8中HashMap的底层实现与源码分析.HashMap是开发中使用频率最高的用于映射(键值对 ...

  9. 小D课堂 - 新版本微服务springcloud+Docker教程_4-06 Feign核心源码解读和服务调用方式ribbon和Feign选择

    笔记 6.Feign核心源码解读和服务调用方式ribbon和Feign选择         简介: 讲解Feign核心源码解读和 服务间的调用方式ribbon.feign选择             ...

  10. HTTP流量神器Goreplay核心源码详解

    摘要:Goreplay 前称是 Gor,一个简单的 TCP/HTTP 流量录制及重放的工具,主要用 Go 语言编写. 本文分享自华为云社区<流量回放工具之 goreplay 核心源码分析> ...

随机推荐

  1. 在centons7系统部署一套单master的k8s集群

    架构图: 操作系统:CentOS Linux release 7.7.1908 (Core) docker:docker-ce-20.10.14-3.el7.x86_64 kubernetes: 1. ...

  2. JQuery 页面滚动至指定元素位置

    $(window).scrollTop($("#id").offset().top - 20);

  3. RMAN架构

    关于 RMAN 环境 Recovery Manager 环境由在备份和恢复策略中发挥作用的各种应用程序和数据库组成. RMAN 环境的组件 组件 描述 RMAN 客户端 管理目标数据库的备份和恢复操作 ...

  4. mobx基础

    React 和 MobX 是一对强力组合.React 通过提供机制把应用状态转换为可渲染组件树并对其进行渲染.而MobX提供机制来存储和更新应用状态供 React 使用. 对于应用开发中的常见问题,R ...

  5. memoのPython环境配置

    Python环境配置 属予作文以记之. 首先 打开网页 https://mirrors.tuna.tsinghua.edu.cn 浏览一下,都是好东西. 把这个网址保存为书签,经常要用的. 有条件的话 ...

  6. win系统常用快捷键查询手册

    win+ctrl+左/右方向键 虚拟桌面切换

  7. MongoDB 分片模式

    Sharding (分片模式) 副本集可以解决主节点发生故障导致数据丢失或不可用的问题,但遇到需要存储海量数据的情况时,副本集机制就束手无策了.副本集中的一台机器可能不足以存储数据,或者说集群不足以提 ...

  8. java mybatisplus+springboot服务器跨域问题

    项目本地增删改测试正常,上传到  阿里服  页面出现了 跨域报错问题! 解决方案:添加一个过滤器 package com.rl;import org.springframework.stereotyp ...

  9. RStudio中有常用的快捷键

    1.常用快捷键 转自:https://blog.csdn.net/swuteresa/article/details/8649067 2.RStudio中如何撤销上一步操作: 一般运行过的程序都会在H ...

  10. vue+antd实现PDF预览(后端返回的是文件流)

    操作步骤: 第一步:下载包   npm install --save vue-pdf 第二步:导入组件 第三步:使用pdf标签进行展示,showUrl指的是访问路径 第四步:定义要用到的变量 第五步: ...