一、自定义了一个ArrayList的模拟集合(源码+详细说明)

  前段时间分析了下ArrayList集合的源码,总觉得如果不自己定义一个的话,好像缺了点什么,所以有了如下的代码。

 代码可以说是逐行注释了,所以就不做过多的分析了。

类结构展示图:

自定义集合:MyArrayListDefin.java

 package com.xfwl.algorithmAnalysis.linklsit;

 import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer; /**
* 学习整理-模拟一个ArrayList(数组容器)
* @function 自定义ArrayList
* @author 小风微凉
* @time 2018-5-13 上午7:25:28
*/
public class MyArrayListDefin<T> implements Iterable<T>{
/**
* 定义一个默认扩展容量
*/
private static final int DEFAULT_CAPACITY=10;
/**
* 当前集合的容量
*/
private int intSize;
/**
* 当前集合容器
*/
private T[] theItems;
/**
* 构造器
*/
public MyArrayListDefin(){
//重置集合初始数据 this.clear();
}
/**
* 清除容器中数据
*/
private void clear(){
//设置初始化数据
this.intSize=0;
//重置数组容器:默认容量设置为初始值:10
this.ensureCapacity(DEFAULT_CAPACITY);
}
/**
* 设置容器大小
* @param newCapacity 新容量大小
* 说明:这里参考ArrayList源码中的一套扩展规则">>"
*/
public void ensureCapacity(int newCapacity){
//大小范围检查
if(newCapacity<=this.intSize){//不超过了当前容器的容量大小
return;//则不需要扩展容器容量
}
//数组初始值判断
if(this.theItems==null){
theItems=(T[]) new Object[newCapacity];
return;//第一次初始化进来
}
//需要扩展容器容量
T[] newItems=(T[]) Arrays.copyOf(this.theItems, newCapacity,this.theItems.getClass());
this.theItems=newItems;
//分析说明下:
/**
* Array.copyOf();内部封装了
* System.arraycopy(this.theItems, 0, newItems, 0, this.theItems.length);这个方法
*/
//效果等效于=》:System.arraycopy(this.theItems, 0, newItems, 0, this.theItems.length);
}
/**
* 返回容器容量大小
* @return
*/
public int size(){
return this.intSize;
}
/**
* 判断容器是否为空
* @return
*/
public boolean isEmpty(){
return this.size()==0;
}
/**
* 按照容器现有的数据所在的容量,瘦身容器容量
*/
public void trimCapacity(){
this.ensureCapacity(this.size());
}
/**
* 获取指定位置的数据
* @param index 位置索引值
* @return
*/
public T get(int index){
//范围合法性检查
if(index<0 ||index>=size()){
throw new ArrayIndexOutOfBoundsException("获取的位置不合法!");
}
//验证通过
return this.theItems[index];
}
/**
* 给容器中的指定位置设置数据
* @param index 位置索引值
* @param newData 数据
*/
public void set(int index,T newData){
//位置合法性判断
if(index<0 || index>size()){
throw new ArrayIndexOutOfBoundsException("设置的位置不合法!");
}
//验证通过
T oldData=this.theItems[index];
System.out.println("集合中索引值="+index+",的数据【"+oldData+"】即将被重置为:【"+newData+"】");
this.theItems[index]=newData;
}
/**
* 添加一个数据到集合中(追加到末尾)
* @param newData 数据
* @return true 添加成功 false 添加失败
*/
public boolean add(T newData){
this.set(this.size(), newData);
this.intSize++;
return true;
}
/**
* 在指定位置添加一个新数据
* @param index 指定的位置索引值
* @param newData 新的数据
*/
public void add(int index,T newData){
//位置合法性检查
if(this.theItems.length==this.size()){//当前数组长度刚好被占满了,那么就急需要扩容
//设置扩容规则:这里参考ArrayList源码中的一套扩展规则">>1"
int newCapacity=this.size()+this.size()>>1;//扩展量:取当前容量的一半,且向下取整
this.ensureCapacity(newCapacity);
}
//继续添加新数据,同时后续位置的数据向后挪移一位
/**
* System.arraycopy(...)
* 参数说明:
* 第一位:复制的源数组
* 第二位:从index的索引值处开始
* 第三位:目标数组
* 第四位:复制的数据目标数组的第(index+1)索引值处开始存放
* 第五位:总共需要复制(this.size()-index)个数据:有效数据大小-索引值前的数据个数(包含数值处的数据)
*/
System.arraycopy(this.theItems, index, this.theItems, index+1, this.size()-index);
//开始设置新数据
this.theItems[index]=newData;
//当前集合容器有效数据大小+1
this.intSize++;
}
/**
* 从集合数组中移除指定位置的数据
* @param index 数据的位置索引值
*/
public void remove(int index){
//位置的合法性检查
if(index<0 || index>this.size()){
throw new ArrayIndexOutOfBoundsException("要移除的数据的位置索引值不合法!");
}
//检查通过:要移除的位置之后的数据前移一位
System.arraycopy(this.theItems, index+1, this.theItems, index, this.size()-index-1);
//末尾的数据需要置空
this.theItems[this.size()-1]=null;
//当前有效数据大小-1
this.intSize--;
}
/**
* 从集合中移除指定的数据
* @param data 数据
*/
public void remove(T data){
//需要找到这个数据的所在位置
int index=0;
for(T t:this.theItems){
if(data==t){
remove(index);
break;
}
index++;
}
}
/**
* 获取迭代器的方法
* --用于拿到自定义的迭代器
*/
@Override
public Iterator<T> iterator() {
return new ArrayListIterator();
}
/**
* JDK1.8新增的方法
* 循环处理集合中的数据,
* 此处的规则是:把当前集合的数据容器中的每一条数据,交给action来处理
*/
@Override
public void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action); for (T t : this.theItems) {
action.accept(t);
}
}
/**
* 打印当前集合的基本信息
*/
@Override
public String toString() {
return "集合总容量:"+this.theItems.length+",集合当前使用的容量:"+this.intSize+",下一次容量扩展值:"+(this.theItems.length>>1);
}
/**
* JDK1.8新增的方法
* splitable iterator可分割迭代器)接口是Java为了并行遍历数据源中的元素而设计的迭代器,
* 这个可以类比最早Java提供的顺序遍历迭代器Iterator,但一个是顺序遍历,一个是并行遍历。
*/
@Override
public Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
/**
* 内置一个迭代器
* @function 可以在自定义的迭代器中,按照自己的方式实现功能
* 这里说明一下:
* 我们都知道:(1)数组的查询、修改 快于新增和删除,因为一般的删除和新增基本上都会牵涉到数据的后移或迁移,比较消费资源。
* (2)单链表结构的查询和修改,则慢于新增和删除,因为一般的查询和修改,需要从头结点开始向下遍历,直到找到要查询的结点方可修改。而删除则不需要遍历。
* (3)双链表结构的查询、修改、新增、删除则要快于单链表,因为双链表的结点中,包含了前驱结点和后驱结点的引用,双链表是环形结构。
* 所以呀,这个迭代器的实现就至少可以有上面三种的实现方式,三种方式代表迭代器会用三种方式去处理集合的数据,其中就优劣势就需要仔细考量了!
* 特别说明:下面的迭代器使用的是第一种:数组的方式,至于后面的2种方式,在之后的学习中,会慢慢研究。
* @author 小风微凉
* @time 2018-5-13 上午9:16:56
* @param <T>
*/
private class ArrayListIterator<T> implements Iterator<T>{
/**
* 当前迭代器,迭代的位置:默认值为0
*/
private int currPos=0;
/**
* 判断是否有下一个数据
*/
@Override
public boolean hasNext() {
return currPos<MyArrayListDefin.this.size();
}
/**
* 拿到集合中的下一个数据
*/
@Override
public T next() {
if(!hasNext()){
throw new NoSuchElementException("已经没有下一个数据了!");
}
return (T) MyArrayListDefin.this.theItems[currPos++];
}
/**
* 使用迭代器移除集合中的当前迭代到的数据
*/
@Override
public void remove() {
MyArrayListDefin.this.remove(--currPos);
}
/**
* jdk1.8新增的方法
* 针对性地处理下一个迭代数据
* 此处定义的规则是:把当前迭代器要处理的下一个数据,交给action来处理
*/
@Override
public void forEachRemaining(Consumer<? super T> action) {
action.accept(next());
}
}
}

运行类:Test.java

 package com.xfwl.algorithmAnalysis.linklsit;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer; /**
* 测试自定义集合:MyArrayListDefin
* @function
* @author 小风微凉
* @time 2018-5-13 上午11:01:54
*/
public class Test {
public static void main(String[] args) {
//拿到一个集合对象
System.out.println("--------创建集合对象并添加数据-----------------");
MyArrayListDefin<Object> arrList=new MyArrayListDefin<Object>();
//添值
arrList.add(1);
arrList.add("String_2");
arrList.add('3');
arrList.add("小风微凉");
System.out.println(arrList.toString());
//开始迭代器遍历
System.out.println("--------开始打印数据-----------------");
Iterator<Object> it=arrList.iterator();
for(;it.hasNext();){
Object eachObj=it.next();
System.out.println(eachObj); }
System.out.println(arrList.toString());
System.out.println("--------END-----------------");
//指定位置修改一个数据
arrList.set(0, "第一个位置的数据被修改");
Iterator<Object> it2=arrList.iterator();
System.out.println("--------开始打印数据-----------------");
for(;it2.hasNext();){
Object eachObj=it2.next();
System.out.println(eachObj); }
System.out.println(arrList.toString());
System.out.println("--------END-----------------");
//指定位置添加一个数据
arrList.add(1, "第二个位置的数据被添加");
Iterator<Object> it3=arrList.iterator();
System.out.println("--------开始打印数据-----------------");
for(;it3.hasNext();){
Object eachObj=it3.next();
System.out.println(eachObj);
}
System.out.println(arrList.toString());
System.out.println("--------END-----------------");
//删除一个指定位置的数据
arrList.remove(1);
Iterator<Object> it4=arrList.iterator();
System.out.println("--------开始打印数据-----------------");
for(;it4.hasNext();){
Object eachObj=it4.next();
System.out.println(eachObj);
}
System.out.println(arrList.toString());
System.out.println("--------END-----------------");
//删除一个数据
arrList.remove("String_2");
Iterator<Object> it5=arrList.iterator();
System.out.println("--------开始打印数据-----------------");
for(;it5.hasNext();){
Object eachObj=it5.next();
System.out.println(eachObj);
}
System.out.println(arrList.toString());
System.out.println("--------END-----------------");
//测试forEach()方法
arrList.forEach(new Consumer1("集合中的forEach方法:"));
//测试迭代器中的Consumer1Consumer1方法
Iterator<Object> it6=arrList.iterator();
it6.forEachRemaining(new Consumer1("集合中迭代器的forEachRemaining方法:")); }
private static class Consumer1<T> implements Consumer<T>{
private String info;
public Consumer1(String info){
this.info=info;
}
@Override
public void accept(T t) {
System.out.println(info+t);
}
@Override
public Consumer andThen(Consumer after) {
// TODO Auto-generated method stub
return null;
} }
}

运行测试结果:(正常)

--------创建集合对象并添加数据-----------------
集合中索引值=0,的数据【null】即将被重置为:【1】
集合中索引值=1,的数据【null】即将被重置为:【String_2】
集合中索引值=2,的数据【null】即将被重置为:【3】
集合中索引值=3,的数据【null】即将被重置为:【小风微凉】
集合总容量:10,集合当前使用的容量:4,下一次容量扩展值:5
--------开始打印数据-----------------
1
String_2
3
小风微凉
集合总容量:10,集合当前使用的容量:4,下一次容量扩展值:5
--------END-----------------
集合中索引值=0,的数据【1】即将被重置为:【第一个位置的数据被修改】
--------开始打印数据-----------------
第一个位置的数据被修改
String_2
3
小风微凉
集合总容量:10,集合当前使用的容量:4,下一次容量扩展值:5
--------END-----------------
--------开始打印数据-----------------
第一个位置的数据被修改
第二个位置的数据被添加
String_2
3
小风微凉
集合总容量:10,集合当前使用的容量:5,下一次容量扩展值:5
--------END-----------------
--------开始打印数据-----------------
第一个位置的数据被修改
String_2
3
小风微凉
集合总容量:10,集合当前使用的容量:4,下一次容量扩展值:5
--------END-----------------
--------开始打印数据-----------------
第一个位置的数据被修改
3
小风微凉
集合总容量:10,集合当前使用的容量:3,下一次容量扩展值:5
--------END-----------------
集合中的forEach方法:第一个位置的数据被修改
集合中的forEach方法:3
集合中的forEach方法:小风微凉
集合中的forEach方法:null
集合中的forEach方法:null
集合中的forEach方法:null
集合中的forEach方法:null
集合中的forEach方法:null
集合中的forEach方法:null
集合中的forEach方法:null
集合中迭代器的forEachRemaining方法:第一个位置的数据被修改

总结:

  上面这个自定义的(仿ArrayList)集合,还有不完善的地方,原本打算是把迭代器写成单链表,最好是双链表结构的,但是考虑现在是学习阶段,所以还是从最基础的方式学起,后续的LinkedList的学习和自定义在尝试使用单链表或多链表结构的迭代器。一步步来吧!

日常学习随笔-自定义了一个MyArrayListDefin集合(数组扩容+迭代器+JDK1.8新方法+详细说明)的更多相关文章

  1. PyQt学习随笔:QTableWidget的selectedRanges、setRangeSelected访问选中矩形范围的方法

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 在QTableWidget对项的操作支持选中多个项的情况下,可以通过方法selectedRanges ...

  2. PyQt(Python+Qt)学习随笔:QTableWidgetItem项文本和项对齐的setText、setTextAlignment方法

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 QTableWidget部件中的QTableWidgetItem项的文本可以通过text()和set ...

  3. PyQt(Python+Qt)学习随笔:QTreeWidget中给树型部件增加顶层项的方法

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 QTreeWidget对象创建后,是没有任何项的,要给部件增加项,首先要增加顶层项.顶层项的增加有三 ...

  4. PyQt(Python+Qt)学习随笔:QTableWidgetItem项操作相关的flags、isSelected、checkState方法

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 QTableWidget中项操作相关的属性包括是否可用.是否可选中.是否可编辑.是否可复选.是否选中 ...

  5. PyQt5学习随笔01--计算一个目录里我们码的代码行数&amp;&amp;PyQt的多线程通信

    今天突然想知道自学习Python以来我一共码了多少行代码了,于是写了一个简单的程序: __author__ = 'jiangzhiheng' # coding=utf-8 from PyQt5.QtC ...

  6. iOS学习之自定义视图时,在屏幕发生旋转时触发重新布局方法

    如果要对自定义的视图在屏幕旋转时重新布局,则在自定义视图中定义以下触发方法: -(void)layoutSubviews { [super layoutSubviews]; //1.获取到屏幕旋转的方 ...

  7. PyQt(Python+Qt)学习随笔:QTabWidget选项卡部件移除选项卡的removeTab和clear方法

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 要从一个QTabWidget中去除选项卡,可用使用removeTab和clear方法. 1.移除选项 ...

  8. PyQt(Python+Qt)学习随笔:QTreeWidget中获取可见项视口位置矩形的visualItemRect方法

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 树型部件的visualItemRect方法可以返回参数指定项在视口的位置矩形. QRect visu ...

  9. PyQt(Python+Qt)学习随笔:QTreeWidgetItem项下的子项列表中增加子项的方法

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 追加子项 QTreeWidgetItem类型的项构建以后,可以通过addChild(QTreeWid ...

随机推荐

  1. HihoCoder1449 重复旋律6(后缀自动机)

    描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为一段数构成的数列. 现在小Hi想知道一部作品中所有长度为K的旋律中出现次数最多的旋律的出现次数.但是K不是固定的,小Hi想知道对 ...

  2. 转载:关于消息队列的使用----ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ

    转载: http://blog.csdn.net/konglongaa/article/details/52208273

  3. J2EE分布式服务基础之RPC

    一.RPC介绍 什么是RPC 远程过程调用(RPC)是一个协议,程序可以使用这个协议请求网络中另一台计算机上某程序的服务而不需知道网络细节. RPC模型 C/S模式 基于传输层协议 (例如 TCP/I ...

  4. poj 3421 X-factor Chains——质因数分解

    题目:http://poj.org/problem?id=3421 记忆化搜索竟然水过去了.仔细一想时间可能有点不对,但还是水过去了. #include<iostream> #includ ...

  5. flask之全局对象

    from flask import current_app, g g is a special object that is unique for each request. It is used t ...

  6. Python网络编程中的select 和 poll I/O复用的简单使用

    首先列一下,sellect.poll.epoll三者的区别 select select最早于1983年出现在4.2BSD中,它通过一个select()系统调用来监视多个文件描述符的数组,当select ...

  7. Azure上批量创建OS Disk大于30G的Linux VM

    Azure上VM的OS盘的大小在创建时是固定的.Windows是127G,Linux是30G.如果需要批量创建的VM的OS Disk有更大的容量.可以考虑用下面的方法实现. 1 创建一台有Data-d ...

  8. 用PHP编写登陆界面

    网页的编写用PHP最方便.用php做了最简单的用户登录.创建的程序. 一. MySQL的设计 MySQL设计了两个表:members和sex.两张表的创建语句分别是: create table mem ...

  9. boot asio 非阻塞同步编程---非阻塞的accept和receive。

    boot asio 非阻塞同步编程---非阻塞的accept和receive. 客户端编程: #include<boost/timer.hpp> #include <iostream ...

  10. 机器学习:PCA(降噪)

    一.噪音 噪音产生的因素:可能是测量仪器的误差.也可能是人为误差.或者测试方法有问题等: 降噪作用:方便数据的可视化,使用样本特征更清晰:便于算法操作数据: 具体操作:从 n 维降到 k 维,再讲降维 ...