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代表下一个可以存放数据的数组的索引代表下一个可以存放数据的数组的索引代表下一个可以存放数据的数组的索引

重要的事情说三遍

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

public class QList<T> {
    //默认的数组的大小
    private final int DEFAULT_LIST_SIZE = 8;

    //存放数据的地方
    private Object[] mData;

    //下一个可以存放数据的当前数组的索引
    private int mSize;

    ......
}    

好了,存放数据的数组也有了,下一个可以存放数据的当前的数组的索引也有了

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

此时,我们实现 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的核心原理

原文地址:https://www.cnblogs.com/start1225/p/10016108.html

时间: 2024-11-03 05:44:32

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

1小时手写SpringMVC T5大牛带你解读Spring核心源码(附详细视频教程)

SpringMVC简介 SpringMVC是当前最优秀的MVC框架,自从Spring 2.5版本发布后,由于支持注解配置,易用性有了大幅度的提高.Spring 3.0更加完善,实现了对Struts 2的超越.现在越来越多的开发团队选择了Spring MVC. Spring为展现层提供的基于MVC设计理念的优秀的Web框架,是目前最主流的MVC框架之一 Spring3.0后全面超越Struts2,成为最优秀的MVC框架 Spring MVC通过一套MVC注解,让POJO成为处理请求的控制器,而无须

Backbone事件机制核心源码(仅包含Events、Model模块)

一.应用场景 为了改善酷版139邮箱的代码结构,引入backbone的事件机制,按照MVC的分层思想搭建酷版云邮局的代码框架.力求在保持酷版轻量级的基础上提高代码的可维护性. 二.遗留问题 1.backbone的升级问题,新的特性无法引入 2.backbone中的潜在BUG,若官方已修复则无法同步更新 解决办法: 关注backbone官网的更新记录. http://github.com/documentcloud/backbone/ 三.核心源码 /** * @裁剪版backbone,仅包含Ev

携程系统架构师带你手写spring mvc,解读spring核心源码!

讲师简介: James老师 系统架构师.项目经理 十余年Java经验,曾就职于携程.人人网等一线互联网公司,专注于java领域,精通软件架构设计,对于高并发.高性能服务有深刻的见解, 在服务化基础架构和微服务技术有大量的建设和设计经验. 课程内容: 1.为什么读Spring源码? 如果你是一名JAVA开发人员,你一定用过Spring Framework. 作为一款非常经典的开源框架,从2004年发布的1.0版本到现在的5.0版本,经历了14年的洗礼, 持久不衰 与其说现在是JAVA的天下, 不如

手写ArrayList

ArrayList是Java中最为简单也是最为常用的一个容器,那么它的内部构造是什么呢?其实ArrayList的底层实现就是数组,它的各种方法就是对数组进行的一系列操作,例如对数组元素的修改,删除,位移,对数组长度的获取等.我们可以手写一个ArrayList容器,以助我们更好的理解这个容器: package Collection; import java.util.Iterator; /** * 深入迭代器的原理-->一个容器可以创建多个迭代器对象 * 1.使用了内部类,实现迭代器 * 2.使用

5 手写Java Stack 核心源码

Stack是Java中常用的数据结构之一,Stack具有"后进先出(LIFO)"的性质. 只能在一端进行插入或者删除,即压栈与出栈 栈的实现比较简单,性质也简单.可以用一个数组来实现栈结构. 入栈的时候,只在数组尾部插入 出栈的时候,只在数组尾部删除** 我们来看一下Stack的用法 :如下 public static void main(String[] args){ //新建一个栈 Stack<String> stack = new Stack<>(); /

java集合核心源码01——ArrayList

首先呢,对于ArrayList,相当于一个动态数组,里面可以存储重复数据,并且支持随机访问,不是线程安全的.对于更多的底层东西,且听分解. 打开源码,先看继承了哪些类,实现了哪些接口,然后继承的这些类或接口是否还有父类,一直深挖到顶部 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable 可以看

迷你版jQuery——zepto核心源码分析

前言 zepto号称迷你版jQuery,并且成为移动端dom操作库的首选 事实上zepto很多时候只是借用了jQuery的名气,保持了与其基本一致的API,其内部实现早已面目全非! 艾伦分析了jQuery,小钗暂时没有那个本事分析jQuery,这里就恬不知耻说说自己对zepto的源码理解,希望对各位有用 首先zepto的出现其实还是很讨巧的,他看见了巨人jQuery在移动浪潮来临时的转身慢.牵挂多的问题 马上搞出了一套轻量级类jQuery框架代码,核心代码1000行不到,快速占领了移动端的市场,

java并发之线程池Executor 核心源码解析

1.什么是线程池 定义:线程池是指管理一组同构工作线程的资源池 组成部分: 线程管理器(ThreadPool):用于创建并管理线程池.包括创建线程池,销毁线程池,添加新任务 工作线程(PoolWorker):线程池中的线程 任务接口(Task):每个任务必须实现的接口,一共工作线程调度任务的执行 任务队列:用于存放没有处理的任务,提供一种缓冲机制 2.为什么要使用线程池 通过重用现有的线程而不是创建新线程,从而减少了线程创建 和 销毁过程中的巨大开销 当请求到达时,工作线程已经存在,不用再等待线

[李景山php]thinkphp核心源码注释|Driver.class.php

<?php // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK IT ] // +---------------------------------------------------------------------- // | Copyright (c) 2006-2014 http://thinkphp.cn A