当前位置: 首页 > 编程日记 > 正文

实现Java中的ArrayList

最近深受轮子哥影响,觉得造一些轮子应该会对自己的技术功底有一定的帮助,就决定先从简单的容器开始实现。废话不多说,就先实现一个Java中的ArrayList。

ArrayList是我们在Java中使用非常多的一个类,它是顺序表的数组实现,LinkedList是顺序表的链式实现(自己编的名字,懂就好哈),还有个Vector,它与ArrayList比较像,区别是它是线程安全的。

顺序表应该都有相同的操作,所以我先定义一个接口,描述好顺序表需要哪些操作。代码如下:

public interface KIList<T> {public void add(T item);public void add(int index, T item);public void set(int index, T item);public void remove(int index);public void remove(T item);public void clear();public boolean contains(T item);public boolean isEmpty();public T get(int index);public int indexOf(T item);public int size();
}

这些方法的作用,看见方法名就应该可以明白其意义了,因此不必多说了。接下来就开始着手实现吧。

第一步:初始化

首先我们需要一个构造方法,空参数的构造方法,因为是ArrayList是数组实现的顺序表,因此我们需要在一开始就给ArrayList分配一个容量大小。因此构造方法中就需要初始化一个固定大小的数组。于是就有了下面这部分代码。

public class KArrayList<T> implements KIList<T>{/** 初始化的容量的大小 */private final static int INIT_CAPACITY = 12;private Object[] mList = null;/** 当前的容量 */private int mCurrentCapacity = 0;/** 容器中元素的个数 */private int mSize = 0;public KArrayList() {mList = new Object[INIT_CAPACITY];mCurrentCapacity = INIT_CAPACITY;}
}

首先我定义了一个常量INIT_CAPACITY为12,这个12是看得Java里面的源码,好像原本这个12指的是容量自增的值。INIT_CAPACITY标明我们数组初始化的时候就在内存中开辟了一个多大的空间。

mList指的就是存数据的数组。

mCurrentCapacity指的是当前的容量。这个值并不是一层不变的,显然会随着数组放满的时候扩张一次。

mSize指的就是当前的数组中有效元素的个数了。初始化的值为0。

构造方法中,就直接new一个数组,然后把mCurrentCapacity设置一下就好了。

到这里为止我们就完成了第一步。

第二步:增加元素add

add操作有两个方法,一个是add(T),另一个是add(int, T)。它们的区别是,前者直接在顺序表的结尾插入一个元素,mSize自增1,后者是在指定的位置插入一个元素,指定位置的元素及其之后的元素向后移动一位,mSize自增1。可以画个图来表示。

上图表示的是add(T)的执行过程。

上图是add(int, T)的执行过程。

插入的过程中,需要考虑,当mSize == mCurrentCapacity的时候需要进行一次扩容,另外对于add(int, T),需要对第一个参数index进行判断。最终代码如下:

    /*** 插入一个元素到链表尾部* @param item* */@Overridepublic void add(T item) {if (mSize == mCurrentCapacity) {expansion();}mList[mSize] = item;mSize++;}/*** 插入一个元素到指定位置,从插入位置及其后面的元素往后移动一个位置* @param index 要插入的位置* @param item* */@Overridepublic void add(int index, T item) {if (index < 0 || index >= mSize) {    //不允许index小于0,或者index >= 数组当前大小throw new IndexOutOfBoundsException();}if (mSize == mCurrentCapacity) {expansion();}Object[] newList = new Object[mCurrentCapacity];System.arraycopy(mList, 0, newList, 0, index);System.arraycopy(mList, index, newList, index + 1, mSize - index);newList[index] = item;mList = newList;mSize++;}

需要注意的几个地方:

1.expansion()方法是自己写的扩容函数,代码如下:

    /*** 扩容,当 mSize == mCurrentCapacity 时调用* */private void expansion() {Object[] oldList = mList;Object[] newList = new Object[getNewCapacity()];System.arraycopy(oldList, 0, newList, 0, oldList.length);mList = newList;}/** * 获取新的容量大小* 当满的时候每次增加当前容量的50%* */private int getNewCapacity() {return mCurrentCapacity = mCurrentCapacity + (mCurrentCapacity >> 1);}

2.注意System.arraycopy()方法,这个是Java提供的一个系统方法,作用就是对数组进行复制操作。

3.注意index的判断,如果越界需要抛出IndexOutOfBoundException。

到此为止,我们就完成了增加操作。

第三步:其他的方法

完成了增加方法后,其他的方法就比较简单或者类似了。直接贴代码就可以看懂了。(顺序表的数组实现本来就是相对比较简单的内容啦)

接下来就是整个类的全部代码。

package kross.java.util;/*** 用数组实现的链表* @author krosshuang(krossford@foxmail.com)* @update 2014-9-26 18:36:50 第一次创建* @update 2014-09-28 17:12:46 测试完成* */
public class KArrayList<T> implements KIList<T>{/** 初始化的容量的大小 */private final static int INIT_CAPACITY = 12;private Object[] mList = null;/** 当前的容量 */private int mCurrentCapacity = 0;/** 容器中元素的个数 */private int mSize = 0;public KArrayList() {mList = new Object[INIT_CAPACITY];mCurrentCapacity = INIT_CAPACITY;}/*** 插入一个元素到链表尾部* @param item* */@Overridepublic void add(T item) {if (mSize == mCurrentCapacity) {expansion();}mList[mSize] = item;mSize++;}/*** 插入一个元素到指定位置,从插入位置及其后面的元素往后移动一个位置* @param index 要插入的位置* @param item* */@Overridepublic void add(int index, T item) {if (index < 0 || index >= mSize) {    //不允许index小于0,或者index >= 数组当前大小throw new IndexOutOfBoundsException();}if (mSize == mCurrentCapacity) {expansion();}Object[] newList = new Object[mCurrentCapacity];System.arraycopy(mList, 0, newList, 0, index);System.arraycopy(mList, index, newList, index + 1, mSize - index);newList[index] = item;mList = newList;mSize++;}/*** 更新指定位置的元素* @param index* @param item* */@Overridepublic void set(int index, T item) {if (index < 0 || index >= mSize) {throw new IndexOutOfBoundsException();}mList[index] = item;}/*** 移除指定位置的元素,后面的元素向前移动一位* @param index* */@Overridepublic void remove(int index) {if (index < 0 || index >= mSize) {throw new IndexOutOfBoundsException();}Object[] newList = new Object[mCurrentCapacity];System.arraycopy(mList, 0, newList, 0, index);System.arraycopy(mList, index + 1, newList, index, mSize - index);mList = newList;mSize--;}/*** 移除链表中特定的元素。(如果item在链表中有多个,只移除第一个)* @param item* */@Overridepublic void remove(T item) {for (int i = 0; i < mSize; i++) {if (mList[i].equals(item)) {remove(i);break;}}}/*** 将链表清空,capacity不变* */@Overridepublic void clear() {mList = new Object[mCurrentCapacity];mSize = 0;}/*** 判断是否包含某个元素* @param item* @return true表示有这个元素,false表示没有这个元素* */@Overridepublic boolean contains(T item) {for (int i = 0; i < mSize; i++) {if (mList[i].equals(item)) {return true;}}return false;}/*** 判断链表是否为空* @return boolean* */@Overridepublic boolean isEmpty() {return (mSize == 0) ? true : false;}/*** 获取指定位置的元素* @param index* @return* */@SuppressWarnings("unchecked")@Overridepublic T get(int index) {if (index < 0 || index >= mSize) {throw new IndexOutOfBoundsException();}return (T)mList[index];}/*** 获取特定元素所在的位置。* 如果该链表中存在多个相同的元素,只返回第一个的位置,如果找不到,则返回-1。* @param item* @return int 如果没找到,返回-1* */@Overridepublic int indexOf(T item) {for (int i = 0; i < mSize; i++) {if (mList[i].equals(item)) {return i;}}return -1;}/*** 获取当前链表的长度* @return int* */@Overridepublic int size() {return mSize;}/*** 扩容,当 mSize == mCurrentCapacity 时调用* */private void expansion() {Object[] oldList = mList;Object[] newList = new Object[getNewCapacity()];System.arraycopy(oldList, 0, newList, 0, oldList.length);mList = newList;}/** * 获取新的容量大小* 当满的时候每次增加当前容量的50%* */private int getNewCapacity() {return mCurrentCapacity = mCurrentCapacity + (mCurrentCapacity >> 1);}public static void main(String[] args) {KArrayList<Integer> arr = new KArrayList<Integer>();for (int i = 1; i <= 50; i++) {arr.add(i);}arr.add(10, 99);arr.add(0, 99);System.out.println(arr.get(51));//arr.clear();//System.out.println(arr.contains(99));//System.out.println(arr.indexOf(59));
        arr.remove(11);arr = null;} 
}

另外,里面main方法是用来做测试的,我自己测的感觉没啥问题。

然后对比一下Java的源码,有一个比较大的困惑就是:

Java中它将数组对象前面加上了transient关键字,这个关键字的作用是:序列化的时候,不会将该字段序列化。也就是说,我在程序中创建了一个[1, 2, 3]的数组,序列化的时候不会序列化这些内容,从文件中反序列化的时候难道[1, 2, 3]就取不出来了吗?

不光是ArrayList,其他的几个容器,数据相关的属性都有这个声明。这一点是比较困惑。mark一下,以后搞明白了再更新上来。

【更新】2014-10-07 17:43:52

关于transient关键字的作用,对于ArrayList来说,是使用数组来存储数据的,但实际的数据只有5个,也就是mSize=5,但数组实际的大小是7(假设),或者更大,那么如果要序列化的时候,就会把那两个空数据的也序列化,无疑就浪费了空间,实际上只有5个有效数据。所以增加了transient字段。而wirteObject方法会自动的将ArrayList中有效数据的部分进行序列化,这样就避免了取出来是null的情况。

原文地址:http://www.cnblogs.com/kross/p/4009446.html

转载于:https://www.cnblogs.com/kross/p/4009446.html

相关文章:

6-flutter 状态管理

1 StatelessWidget 不需要状态改变的widget,它没有要管理的内部状态。 Text&#xff0c;CircleAvator 都是其子类 其传递的参数别final 修饰&#xff0c;不可变的 无状态的widget build 方法在以下三种情况下进行调用 当widget 插入到数中去当widget 父级更改配置的时候当…

大二上学数据结构和操作系统_毕业后的工作比上学要重要得多。 这是数据。...

大二上学数据结构和操作系统by Aline Lerner通过艾琳勒纳(Aline Lerner) 毕业后的工作比上学要重要得多。 这是数据。 (What you do after you graduate matters way more than where you went to school. Here’s the data.) The first blog post I published that got any r…

关于C#中编译器保证变量必须初始化规则猜想

现在两种情况&#xff1a; 第一种情况&#xff1a; using System; namespace Wrox {public class Program{static void Main(string[] args){int index; if(true){ index 100; } Console.WriteLine(index); Cons…

Bootstrap table表格

Bootstrap table 使用类 Class"table" 既可让table美化样式 table 相关的Class 隔行换色 &#xff1a; table-striped 鼠标悬停效果&#xff1a; table-hover 表格的边框 : table-bordered 垂直居中 : vertical-align 表头颜色&#xff1a;c…

flutter报错Could not connect to lockdownd, error code -

关于 flutter 报错信息解决方案 第一步&#xff1a; cmdshiftg 前往 /var/db 文件夹&#xff0c;找到lockdown文件夹&#xff0c;修改读写权限 第二步 &#xff1a; 打开命令行,依次执行 brew update brew uninstall --ignore-dependencies libimobiledevice brew uninstall…

k8s aws 部署_如何在短短30分钟内使用CircleCI设置到AWS S3的持续部署

k8s aws 部署by Adam Watt通过亚当瓦特 如何在短短30分钟内使用CircleCI设置到AWS S3的持续部署 (How to setup Continuous Deployment to AWS S3 using CircleCI in just 30 minutes) Continuous Deployment might seem complicated at first, but don’t be intimidated. In…

SharePoint 2010 单点登录

SharePoint2010单点登录 1.进入管理中心》应用程序管理 2.找到 Secure Store Service 应用程序代理 3.然后就是新建了 5.输入网站集管理员 6.这个时候SharePoint就知道你需要给OA这个系统做单点登录了。 7.下一步就是我们要把我们进OA系统的帐号密码告诉SharePoint&#xff0c…

Java IO流学习总结三:缓冲流-BufferedInputStream、BufferedOutputStream

Java IO流学习总结三&#xff1a;缓冲流-BufferedInputStream、BufferedOutputStream 转载请标明出处&#xff1a;http://blog.csdn.net/zhaoyanjun6/article/details/54894451 本文出自【赵彦军的博客】 InputStream |__FilterInputStream|__BufferedInputStream 首先抛出一个…

7-flutter Navigator 和Route

Route 和 Navigator 用于页面之间的跳转 一 Navigator 的 push 和 pop 用于页面之间的跳转 创建MaterialApp时可以指定routes参数&#xff0c;该参数是一个映射路由名称和构造器的Map 跳转的时候 使用 push 跳回的时候使用 pop import package:flutter/cupertino.dart; im…

小规模网络数据公开数据_大规模的在线公开课程曾经是100%免费的。 但是他们没有那样做。...

小规模网络数据公开数据I took one of the first Massive Open Online Courses (MOOCs) in 2011. Back then, everything was 100% free: the videos, the assignments, and the certificates. But in 2017, you can’t find this sort of free learning experience anymore.我…

swift -charts框架雷达图

参考资料 import UIKit import Chartsclass ViewController: UIViewController {let activities ["力量", "敏捷", "生命", "智力", "魔法"]override func viewDidLoad() {super.viewDidLoad()// Do any additional setup…

vector容器总结.xml

1 清空所有元素m_itemVector.clear(); 2 遍历vector<ITEM_CHECK>::iterator iterm_itemVector.begin(); for(i0;iter!m_itemVector.end();iter,i) { if(iter->flag-1) { break; } iter->flag1; } vector<ITEM_CHECK>::iterator iterm_itemVector.b…

Syncthing源码解析 - 第三方库

1&#xff0c;AudriusButkevicius/cli 网址&#xff1a;https://github.com/AudriusButkevicius/cli 2&#xff0c;bkaradzic/go-lz4 网址&#xff1a;https://github.com/bkaradzic/go-lz4 3&#xff0c;calmh 备注&#xff1a;这位是Syncthing项目创立者和最主要的开发者&…

安全工程师2017年真题_以下是2017年全球软件工程师的平均薪水

安全工程师2017年真题And here are those same salaries adjusted to San Francisco’s cost of living:以下是根据旧金山的生活费用调整后的相同工资&#xff1a; As you can see, cost of living is an important consideration. Also, you don’t need to move to San Fran…

测试思想 什么是软件测试(摘录)

什么是软件测试(摘录) by:授客 QQ&#xff1a;1033553122 IEEE 标准的定义:使用人工或自动的手段来运行或测定某个系统的过程&#xff0c;其目的在于检验;它是否满足规定的需求或是弄清预期结果与实际结果之间的差别。对软件测试还有一些不同的定义。 G.J.Myers给出的定义:“程…

8-flutter 异步和线程

线程和异步的UI 1 异步的使用 Dart 有一个单线程执行模型&#xff0c;支持Isolate&#xff08;一种在另外一种线程运行dart的方法&#xff09;,一个事件循环和异步编程。 可以使用async / await 来做网络请求不会挂起UI 使用http 导入 import ‘dart:io’; import ‘dart:c…

前端页面紫红色_谷歌正在开发一种神秘的新型移动操作系统,称为紫红色

前端页面紫红色Google seems to be building a replacement for Android called Fuchsia. Yesterday, they revealed what their new Armadillo user interface looks like (see photo above, courtesy of Ars Technica).谷歌似乎正在建立一个名为Fuchsia的 Android替代产品。 …

iOS UIButton 文字图片上下左右布局

例如文字在左 图片在右,iOS 9 之后一句话搞定 backBtn.semanticContentAttribute UISemanticContentAttributeForceRightToLeft;按钮标题居左实现 dateBtn.contentHorizontalAlignment UIControlContentHorizontalAlignmentLeft; dateBtn.contentEdgeInsets UIEdgeInsetsMak…

linux xampp eclipse xdebug 无法进入断点

一、xampp 版本 1.8.3-5 xampp安装后会自动集成xdebug,目录一般为 /opt/lampp/lib/php/extensions/***-debug-***目录 关于php 与php.ini路径 php程序路径为&#xff1a;/opt/lampp/bin/ php.ini配置文件路径为&#xff1a;/opt/lampp/etc/ 1、配置文件一般在/opt/lampp/etc/ph…

sliva数据库简介--转载

sliva rRNA数据库&#xff08;http://www.arb-silva.de/&#xff09;用来检查和比对RNA序列&#xff0c;既可以针对16S/18S,SSU&#xff0c;也可以针对23S/28S, LSU&#xff0c;包括了Bacteria, Archaea and Eukarya。同时也是ARB的官方指定数据库。 LSU: Large subunit (23S/2…

haproxy ssl_我们如何微调HAProxy以实现2,000,000个并发SSL连接

haproxy sslby Sachin Malhotra由Sachin Malhotra 我们如何微调HAProxy以实现2,000,000个并发SSL连接 (How we fine-tuned HAProxy to achieve 2,000,000 concurrent SSL connections) If you look at the above screenshot closely, you’ll find two important pieces of in…

OC文件操作(1)

1.文件的浅度遍历与深度遍历&#xff1a; //NSFileManager * fm [[NSFileManager alloc]init];//创建文件管理器 //第一步创建一个文件管理器 NSError * error nil; //显示路径下的内容,作用类似于ls -a指令 //返回值是把目录下的内容放到NSArray中 //浅度遍历 NSFileManager …

10-flutter 使用http包请求和网络指示器

使用http package 进行网络请求操作 1 安装步骤 Step1 在pubspec.yaml 文件中添加依赖 dependencies:http: ^0.12.01Step2 flutter packages getStep3 导入头文件 import ‘package:http/http.dart’ as http; 2 使用 var responseBody;http.Response response await http.…

使用nat方式解决虚拟机联网问题

本文全文参考&#xff1a;http://jingyan.baidu.com/album/4e5b3e1957979d91901e24f1.html?picindex1&#xff0c;谢谢 对于很多的linux初学者来说&#xff0c;最开始学习linux时通常是在虚拟机上进行的&#xff0c;然而对于新手来说虚拟机联网会对他们来说是比较困难的。…

老年痴呆 数字化_设计老年人愉快数字体验的5条原则

老年痴呆 数字化by Kaye Mao毛凯(Kaye Mao) 设计老年人愉快数字体验的5条原则 (5 Principles for Designing Delightful Digital Experiences for Seniors) When we got my grandfather his first smart phone, he was thrilled. He had heard all about the wonders of video…

hdu 3664 1~n排列(aii ) 为k个数

http://acm.hdu.edu.cn/showproblem.php?pid3664 求1~n的排列个数&#xff0c;使得逆序数&#xff08;ai>i ) 为给定的k. dp[i][j]表示前1~i的排列中&#xff0c;有j个数是逆序数的个数. #include <cstdio> #include <cstdlib> #include <cmath> #includ…

四边参数值的设定

border&#xff0c;margin&#xff0c;padding 拿border举例 border&#xff1a;上&#xff0c;右&#xff0c;下&#xff0c;左。 border&#xff1a;上下&#xff0c;左右。 border&#xff1a;上下左右。 border&#xff1a;上&#xff0c;左右&#xff0c;下。转载于:https…

11-flutter事件监听

事件监听 1 本身支持事件检测&#xff0c;就可以直接使用onpress body:Center(child: RaisedButton(child: Text("Click"),onPressed: (){print("我被Click了");}),),2 如果本身不支持事件的检测&#xff0c; 使用 GestureDetector 添加一个点击事件 hom…

react前端开发_是的,React正在接管前端开发。 问题是为什么。

react前端开发by Samer Buna通过Samer Buna 是的&#xff0c;React正在接管前端开发。 问题是为什么。 (Yes, React is taking over front-end development. The question is why.) Update: This article is now part of my book “React.js Beyond The Basics”.更新&#xf…

12-flutter Textfield的使用

获取用户的输入用 TextField 或者TextFormField 的实现&#xff0c;通过控制器来实现获取用户的输入。 1 TextField 的属性 const TextField({Key key,this.controller,this.focusNode,// 这个属性可以用来监听输入框是否获取this.decoration const InputDecoration(),Text…