——脱离弟中弟,看破楼外楼;
需要注意的是,在这里我们仅做简单的阐述,不做深入展开。
在这里我们实现数组的动态存储主要是两个方面:
- 存储对象的类的动态化(可以只存一类,也可以全部储存),这里我们使用泛型来解决。
- 存储对象的总量的动态化/数组长度动态,我们通过自定义数组来解决这个问题。
从另外一个角度看,我们实现JDK中Arraylist的部分功能,
目的是能够做到自定义一个数组队列,无关功能丰富,
能够满足需求的精简队列才是实际上会使用的;
再从一个角度上看,我们在学习数据结构的一些基础知识嘛:)
那么我们开始吧!
存储对象的类的动态化
泛型的需要场合
当我们在写方法但是却不知道方法具体会调用什么类的时候,
我们可以先将需要的对象类型写为Object
(包括基本数据类型,如int,float等基本数据类型都有封装的类作为object的子类)
public void fuc(Object o){}
之后再强制转型成相应的类
int a=10;
int i=(int)fuc(a);
但是,当我们传入的对象有时需要是特定的类,有时需要是所有的类,
这个时候前面的需求就无法解决了,所以我们需要用到泛型。
泛型的简单使用
泛型的直观理解是占位:
我们先指定这里需要有个东西存在,就在这里占个位,
之后再用具体的事物把这个占位替换掉。
具体操作如下,当我们在自定义数列列表中这么写
当我们要新建该类时,可以指定只能传入特定的类
public class Myarraylist<E>{}
public MyArrayList() {array = new Object[0];}
实例化数组时这么写
Myarraylist<student>[] ml=new Myarraylist<student>();//假设已经定义了一个student的类。
或者不指定,所有的类都可以传入。
Myarraylist[] ml=new Myarraylist();
需要注意的是,泛型有特定的字母,这里我们就用< E >!
同时泛型不能指定基本数据类型(int等),那实在要用到基本数据类型怎么办呢?
还记得刚刚前面有提到过,基本数据类型都有相应封装的类作为Object的子类,
比如int对应着一个Integer的类,于是我们可以用
Integer a=new Integer(1);
来取代
int a=1;
存储对象的总量的动态化
我们先做一个小补充吧:)
数组的定义:
首先是一维数组
- 数据类型[] 数组名 = {值,…};
- 数据类型[] 数组名 = new 数据类型[长度];
- 数据类型[] 数组名 = new 数据类型[]{值,…};
仔细回想使用数组的过程,通常用1来储存已知的体量较小的数据,用2去存储需要用for语句去不断存储的数据。
需要注意的是,1其实是规定好自动添加3的"new 数据类型[]",也仅在该种定义方式下做省略。
当然如果在某个类中已经定义了一个数组作为属性,那么可以这么用
- 数组名 = new 数据类型[长度];
- 数组名 = new 数据类型[]{值,…};
需要注意的是,数组名 ={值,…};语句是无效的,因为在这个语句中不会自动添加"new 数据类型[]"。
二维数组是类似的,这样就可以得到想要的二维数组了。
- 数据类型[][] 数组名 = new 数据类型[行][列];
- 数据类型[][] 数组名 = new 数据类型[][]{{值,…},…};
使用数组过程中需要熟记于心的是:
调用方法时,数组通过数组名传入首地址,也就是说方法内改变了数组,方法外数组也会被改变了;
与之相较的变量传入的是值所以方法外的变量不受方法影响;
数组的存储特点:连续存储,大小固定,按序访问
-
第一个特点导致我们在进行数据操作时十分繁琐,
具体而言就是当我们进行插入或是删除,在对应索引位置之后的元素都要进行移动
不然我们会在插入时无位可插,或删除后在队列中间的位置空出一个位。
——>那么能不能进行自动补位或挪位的操作呢? -
第二个特点导致我们在数据元素个数未知时显得十分被动,
数组长度太长,开辟太多内存空间,浪费。
数组长度太短,我们时常会面临数组越界的尴尬情况。
——>能否确定合适的数组大小呢?能否自动改变数组长度呢? -
可谓数组的最大优点,是这位小老弟在数据结构中的核心竞争力,
数组是所有数据结构中访问速度最快的一种。
基于以上的叙述,我们充分发挥主观能动性:)
定义一个新类,封装一些基础方法,让这位小老弟扬长避短。
基于数组特定,数组列表要实现的功能及实现的原理
一.判断是否需要改变数组长度
-
对应的两种主要情况:
数组数据被大量删除,缩短空间
——>数据元素远小于数组长度;
数据总数超过数组长度
——>数组长度为零; -
数组长度:array.length;
数据总量:private int size;
二.实现数组长度的动态改变
- 创建一个长度符合要求的新数组;
- 将原数组数数据copy到新数组;
- 原组名指向的地址改成指向新数组的地址;
- 原数组jvm自动销毁空间,无需我们操作;
- 不知道大家看到第三点能不能理解呢?
- 对于由java中数组内存空间的分配到java整体内存空间分配,
我没有弄懂,导致对声明和实例化对象有一种半知不解的感觉,
这里先做一个小预告吧,之后会写一篇详细介绍java内存分配的文章,
写完了会把这里替换掉,稍微期待一下:)
毕竟要对自己文章负责嘛;
没了,核心就这两点,搞定它我们就完成一大部分了。
基于此上我们可以写一个refresh方法,用于判断并改变数组长度。
//判断并更新数组长度的方法,size过小,数组长度不够,数组长度为0,手动开启
private void refresh()
{
if(size>=array.length||array.length == 0||flag ==1)
{
Object[] newarray=new Object[array.length+rate];
for(int i=0;i<array.length;i++)
{
newarray[i]=array[i];
}
array=newarray;
}
else if(size<array.length/2)
{
Object[] newarray=new Object[size+rate];
for(int i=0;i<size;i++)
{
newarray[i]=array[i];
}
array=newarray;
}
}
三、基础操作的实现
- 增加,减少,替换数据,及对应的补位;
- 操作的对象:一个对象(一个索引),一个队列(一个索引);
- 补位或插入空元素我们先各写一个基本方法(move/add);
- 每次添加或删除时都会调用到他们。每次添加或删除时都会调用到他们;
基本的实现逻辑就是,索引值对应的位置后的元素左移(将空位补掉)或右移(腾出空位);
需要注意的是他们开始的位置是不同的,前者从左往右,后者从右往左,目的是避免元素被覆盖;
//进行补位的操作,通过指定位置来补位,每次移除单个元素时都会调用此方法进行补位,同时同步元素总数个数
private void move(int index)
{
for(int i=index;i<size;i++)
{
array[i]=array[i+1];
}
size--;
}
//进行插入空位置的方法,每次插入单个元素时会调用此方法,同时同步元素总数个数
private void add0(int index)
{
for(int i=size;i>index;i--)
{
array[i+1]=array[i];
}
size++;
}
2019.2.15:代码更新,运行检验无误,基本完成JDK中ArrayList的方法;
问题: 1.jvm的内存分配机制;
2.collection的理解;
3.java中的应用,浅表副本的理解;
4.异常抛出存在细节问题;
完整代码如下
实现的主要功能如图
package Myarraylist;
public class MyArrayList<E> {
public Object[] Array;
private int size=0;
public String name;
public MyArrayList(int size) {
Array=new Object[size];
}
public MyArrayList()
{
Array=new Object[0];
}
private void refresh()
{
if(size>=Array.length)
{
Object[] newList=new Object[size+10];
for(int i=0;i<size;i++)
{
newList[i]=Array[i];
}
Array=newList;
}
}
private void refresh(boolean j)
{
Object[] newList=new Object[Array.length+10];
for(int i=0;i<size;i++)
{
newList[i]=Array[i];
}
Array=newList;
}
private void moveR(int index)
{
if(index>Array.length)
{
System.out.println("error");
}
else
{
refresh();
for(int i=size;i>=index;i--)
{
Array[i+1]=Array[i];
}
}
}
private E moveL(int index)
{
if(index>=Array.length)
{
System.out.println("error");
}
else
{
E a=(E) Array[index];
for(int i=index;i<size-1;i++)
{
Array[i]=Array[i+1];
}
size--;
return a;
}
return null;
}
public boolean add(E e)
{
refresh();
Array[size]=e;
size++;
return true;
}
public void add(int index,E e)
{
refresh();
if(index>=size)
{
System.out.println("error");
}
else
{
moveR(index);
Array[index]=e;
size++;
}
}
public boolean addAll(MyArrayList<E> a)
{
if(a.size<10)
{
refresh(true);
int bsize=size;
for(int i=0;i<a.size;i++)
{
Array[bsize+i]=a.Array[i];
size++;
}
}
else
{ int bsize=size;
refresh(true);
for(int i=0;i<a.size;i++)
{
if(i%10==0)
{
refresh(true);
}
Array[bsize+i]=a.Array[i];
this.size++;
}
}
return true;
}
public boolean addAll(MyArrayList<E> a,int index)
{
if(a.size<10)
{
refresh(true);
for(int i=0;i<a.size;i++)
{
moveR(index+i);
Array[index+i]=a.Array[i];
size++;
}
}
else
{
for(int i=0;i<a.size;i++)
{
if(i%10==0)
{
refresh(true);
}
moveR(index+i);
Array[index+i]=a.Array[i];
size++;
}
}
return true;
}
public void clear()
{
Object[] newList=new Object[size];
Array=newList;
size=0;
}
public Object clone()
{
return (Object)this;
}
public boolean contains(Object o)
{
boolean a=false;
for(int i=0;i<size;i++)
{
//三目运算符;
a=(o==null ? Array[i]==null : o.equals(Array[i]));
if(a==true)
{
return a;
}
}
return a;
}
public E get(int index)
{
return (E)Array[index];
}
public int indexof(Object o)
{
for(int i=0;i<size;i++)
{
if(Array[i].equals(o))
{
return i;
}
}
return -1;
}
public boolean isEmpty()
{
for(int i=0;i<size;i++)
{
if(Array[i]!=null)
{
return false;
}
}
return true;
}
public int lastIndexOf(Object o)
{
int g=-1;
for(int i=0;i<size;i++)
{
if(o.equals(Array[i]))
{
g=i;
}
}
return g;
}
public E remove(int index)
{
if(index>=size)
{
System.out.println("error");
}
else
{
return moveL(index);
}
return null;
}
public boolean removeO(Object o)
{
for(int i=0;i<size;i++)
{
if(o.equals(Array[i]))
{
moveL(i);
return true;
}
}
return false;
}
protected void removeRange(int fromIndex,int toIndex)
{
int times =toIndex-fromIndex;
if(times<0)
{
System.out.println("error");
}
else
{
for(int i=0;i<times;i++)
{
moveL(fromIndex);
}
}
}
public E set(int index,E element)
{
E a=(E)Array[index];
Array[index]=element;
return a;
}
public Object[] toArray()
{
return Array;
}
//待完善;
public <T> T[] toArray(T[] a)
{
return a;
}
public void trimTosize()
{
Object[] newArray=new Object[size];
for(int i=0;i<size;i++)
{
newArray[i]=Array[i];
}
Array=newArray;
}
//这是为了方便检验结果而定义的方法;
public void toMyString()
{
System.out.println("name: "+name+" size:"+size+" length:"+Array.length);
for(int i=0;i<size;i++)
{
System.out.print("第"+i+"位"+":"+Array[i]+" ");
if(i%7==0&&i!=0)
{
System.out.println();
}
}
System.out.println();
System.out.println();
}
public static void main(String[] args) {
System.out.println("——————————————————————————————————————————————————————————————————");
MyArrayList a=new MyArrayList();
MyArrayList b=new MyArrayList(10);
MyArrayList c=new MyArrayList(10);
a.name="a";
b.name="b";
c.name="c";
System.out.println("调用add:");
for(int i=0;i<6;i++)
{
a.add(i);
b.add(i*2);
c.add(i*3);
c.add(i*4);
}
a.toMyString();
b.toMyString();
c.toMyString();
System.out.println("调用add(指定索引):");
b.add(2, 9);
b.toMyString();
System.out.println("调用addAll(比自身短):");
b.addAll(a);
b.toMyString();
System.out.println("调用addAll(比自身长):");
b.addAll(c);
b.toMyString();
System.out.println("调用addAll(比自身短+指定索引):");
b.addAll(a, 2);
b.toMyString();
System.out.println("调用addAll(比自身长+指定索引):");
c.toMyString();
c.addAll(b,2);
c.toMyString();
System.out.println("——————————————————————————————————————————————————————————————————");
System.out.println("调用clear:");
c.clear();
c.toMyString();
System.out.println("clone存在问题");
System.out.println( );
//不是很懂浅表副本的含义,复制元素但不复制引用,说起来Java的对象赋值似乎就在进行引用?
//此处留空。
System.out.println("调用contains(1):");
System.out.println(a.contains(1));
System.out.println( );
System.out.println("调用contains(元素为null):");
System.out.println(a.contains(null));
System.out.println( );
System.out.println("调用get(index:2):");
System.out.println(a.get(2));
a.toMyString();
System.out.println("调用indexOf:");
System.out.println(a.indexof(2));
System.out.println( );
System.out.println("调用indexOf(不存在元素):");
System.out.println(a.indexof(6));
System.out.println( );
System.out.println("调用isEmpty:");
System.out.println(a.isEmpty());
System.out.println( );
System.out.println("调用isEmpty(空数列):");
System.out.println(c.isEmpty());
System.out.println( );
System.out.println("调用lastIndexOf:");
System.out.println(b.lastIndexOf(0));
b.toMyString();
System.out.println("调用lastIndexOf(不存在):");
System.out.println(b.lastIndexOf(36));
b.toMyString();
System.out.println("——————————————————————————————————————————————————————————————————");
System.out.println("调用remove(Index):");
System.out.println(b.remove(2));
b.toMyString();
//移除此列表中首次出现的指定元素(如果存在);
System.out.println("调用removeO(Object):");
int numbera=1;
System.out.println(b.removeO(numbera));
b.toMyString();
//注意不包括尾指引;
System.out.println("调用removeRange(指定首尾索引):");
b.removeRange(0,2);
b.toMyString();
System.out.println("调用set(指定索引+元素):");
b.set(0,20);
b.toMyString();
System.out.println("toarray存在问题");
System.out.println( );
//一是不知道作用何在,二是不是很懂collection;
//此处留空。
System.out.println("调用trimTosize:");
System.out.println("b.length:"+b.Array.length);
b.trimTosize();
System.out.println("b.length:"+b.Array.length);
}
}
}
运行结果如下:
——————————————————————————————————————————————————————————————————
调用add:
name: a size:6 length:10
第0位:0 第1位:1 第2位:2 第3位:3 第4位:4 第5位:5
name: b size:6 length:10
第0位:0 第1位:2 第2位:4 第3位:6 第4位:8 第5位:10
name: c size:12 length:20
第0位:0 第1位:0 第2位:3 第3位:4 第4位:6 第5位:8 第6位:9 第7位:12
第8位:12 第9位:16 第10位:15 第11位:20
调用add(指定索引):
name: b size:7 length:10
第0位:0 第1位:2 第2位:9 第3位:4 第4位:6 第5位:8 第6位:10
调用addAll(比自身短):
name: b size:13 length:20
第0位:0 第1位:2 第2位:9 第3位:4 第4位:6 第5位:8 第6位:10 第7位:0
第8位:1 第9位:2 第10位:3 第11位:4 第12位:5
调用addAll(比自身长):
name: b size:25 length:50
第0位:0 第1位:2 第2位:9 第3位:4 第4位:6 第5位:8 第6位:10 第7位:0
第8位:1 第9位:2 第10位:3 第11位:4 第12位:5 第13位:0 第14位:0
第15位:3 第16位:4 第17位:6 第18位:8 第19位:9 第20位:12 第21位:12
第22位:16 第23位:15 第24位:20
调用addAll(比自身短+指定索引):
name: b size:31 length:60
第0位:0 第1位:2 第2位:0 第3位:1 第4位:2 第5位:3 第6位:4 第7位:5
第8位:9 第9位:4 第10位:6 第11位:8 第12位:10 第13位:0 第14位:1
第15位:2 第16位:3 第17位:4 第18位:5 第19位:0 第20位:0 第21位:3
第22位:4 第23位:6 第24位:8 第25位:9 第26位:12 第27位:12 第28位:16
第29位:15 第30位:20
调用addAll(比自身长+指定索引):
name: c size:12 length:20
第0位:0 第1位:0 第2位:3 第3位:4 第4位:6 第5位:8 第6位:9 第7位:12
第8位:12 第9位:16 第10位:15 第11位:20
name: c size:43 length:60
第0位:0 第1位:0 第2位:0 第3位:2 第4位:0 第5位:1 第6位:2 第7位:3
第8位:4 第9位:5 第10位:9 第11位:4 第12位:6 第13位:8 第14位:10
第15位:0 第16位:1 第17位:2 第18位:3 第19位:4 第20位:5 第21位:0
第22位:0 第23位:3 第24位:4 第25位:6 第26位:8 第27位:9 第28位:12
第29位:12 第30位:16 第31位:15 第32位:20 第33位:3 第34位:4 第35位:6
第36位:8 第37位:9 第38位:12 第39位:12 第40位:16 第41位:15 第42位:20
——————————————————————————————————————————————————————————————————
调用clear:
name: c size:0 length:43
clone存在问题
调用contains(1):
true
调用contains(元素为null):
false
调用get(index:2):
2
name: a size:6 length:10
第0位:0 第1位:1 第2位:2 第3位:3 第4位:4 第5位:5
调用indexOf:
2
调用indexOf(不存在元素):
-1
调用isEmpty:
false
调用isEmpty(空数列):
true
调用lastIndexOf:
20
name: b size:31 length:60
第0位:0 第1位:2 第2位:0 第3位:1 第4位:2 第5位:3 第6位:4 第7位:5
第8位:9 第9位:4 第10位:6 第11位:8 第12位:10 第13位:0 第14位:1
第15位:2 第16位:3 第17位:4 第18位:5 第19位:0 第20位:0 第21位:3
第22位:4 第23位:6 第24位:8 第25位:9 第26位:12 第27位:12 第28位:16
第29位:15 第30位:20
调用lastIndexOf(不存在):
-1
name: b size:31 length:60
第0位:0 第1位:2 第2位:0 第3位:1 第4位:2 第5位:3 第6位:4 第7位:5
第8位:9 第9位:4 第10位:6 第11位:8 第12位:10 第13位:0 第14位:1
第15位:2 第16位:3 第17位:4 第18位:5 第19位:0 第20位:0 第21位:3
第22位:4 第23位:6 第24位:8 第25位:9 第26位:12 第27位:12 第28位:16
第29位:15 第30位:20
——————————————————————————————————————————————————————————————————
调用remove(Index):
0
name: b size:30 length:60
第0位:0 第1位:2 第2位:1 第3位:2 第4位:3 第5位:4 第6位:5 第7位:9
第8位:4 第9位:6 第10位:8 第11位:10 第12位:0 第13位:1 第14位:2
第15位:3 第16位:4 第17位:5 第18位:0 第19位:0 第20位:3 第21位:4
第22位:6 第23位:8 第24位:9 第25位:12 第26位:12 第27位:16 第28位:15
第29位:20
调用removeO(Object):
true
name: b size:29 length:60
第0位:0 第1位:2 第2位:2 第3位:3 第4位:4 第5位:5 第6位:9 第7位:4
第8位:6 第9位:8 第10位:10 第11位:0 第12位:1 第13位:2 第14位:3
第15位:4 第16位:5 第17位:0 第18位:0 第19位:3 第20位:4 第21位:6
第22位:8 第23位:9 第24位:12 第25位:12 第26位:16 第27位:15 第28位:20
调用removeRange(指定首尾索引):
name: b size:27 length:60
第0位:2 第1位:3 第2位:4 第3位:5 第4位:9 第5位:4 第6位:6 第7位:8
第8位:10 第9位:0 第10位:1 第11位:2 第12位:3 第13位:4 第14位:5
第15位:0 第16位:0 第17位:3 第18位:4 第19位:6 第20位:8 第21位:9
第22位:12 第23位:12 第24位:16 第25位:15 第26位:20
调用set(指定索引+元素):
name: b size:27 length:60
第0位:20 第1位:3 第2位:4 第3位:5 第4位:9 第5位:4 第6位:6 第7位:8
第8位:10 第9位:0 第10位:1 第11位:2 第12位:3 第13位:4 第14位:5
第15位:0 第16位:0 第17位:3 第18位:4 第19位:6 第20位:8 第21位:9
第22位:12 第23位:12 第24位:16 第25位:15 第26位:20
toarray存在问题
调用trimTosize:
b.length:60
b.length:27