关于博主
努力与运动兼备~~~有任何问题可以加我好友或者关注微信公众号,欢迎交流,我们一起进步!
微信公众号: 啃饼思录
QQ: 2810706745(i思录)
写在前面
因为我们上篇实现的数组只能存放int类型,但是我们需要的是可以承载多种类型,甚至自定义对象的容器,所以本篇就来通过介绍数组的进阶修炼,来实现这个目的。
使用泛型
泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。使用泛型可以让我们的数据结构可以放置“任何”数据类型,注意既然是使用了泛型,因此不可以是基本数据类型,只能是类对象,要想使用基本数据类型就要使用它们对于的包装类对象。
基本数据类型是指byte,short,int,long,float,double,boolean,char。每个基本数据类型都有对应的包装类: Byte , Short, Integer , Long , Float , Double,Boolean,Character。
注意:只有int 和char其基本数据类型与对应的包装类有一个变形以外,其余的都是其首字母的大写而已。
我们可以通过装箱和拆箱来实现其基本数据类型和对应包装类之间的转换。
我们现在将前面的Array.java文件,复制一份,名字为ArrayElement.java,里面修改泛型E(Element这只是一个元素的简称,你可以自己随意定义)。
泛型是不能通过new来创建一个数组的data = new E[capacity];
这是错误的做法。我们知道java中的类都是隐式的继承Object类,因此可以先通过new Object[]
这样的方式先创建Object数组,然后再进行强制类型转换为E[]
即可。也就是这样的:
data = (E[]) new Object[capacity];
然后还要注意因为使用了泛型,因此对象的类型就都要修改为E了。当然对象的比较更不能使用==了,应该使用equals()方法:
public int find(E e){
for(int i =0;i<size;i++){
if(data[i].equals(e)){
return i;
}
}
return -1;
}
前面我们好像说过在删除指定元素的时候,就是假如你删除的不是最后一个元素,那么那个元素向前挪了一步,把前面的元素给覆盖了,但是后面的却没有元素将它(就是最后一个元素(data[size]),删除的是最后一个元素就更不必说了)给覆盖,而是依然存留在那里,但是由于元素的数量减少了,因此你永远也访问不到它。
现在在使用泛型的使用,我们尽管也可以这么做(不去管它,任其流浪,最后被java的垃圾回收机制收回,过程很漫长),但是建议这里使用data[size] = null;
立即调用java回收机制进行处理。
为什么呢?我们知道8大基本数据类型,在内存中存放的是数据本身,而引用数据类型在内存中存放的数据的引用地址。数据本身是不可以被修改的,但是引用地址却是可以修改的,因此如果你能把该空间进行释放那就可以继续使用了。尽管Java有自动的垃圾回收机制,但如果data[size]依旧存放着对对象的地址引用,就使得它不会被自动的垃圾回收机制给处理掉。因此使用data[size] = null;
就能立即调用java回收机制进行处理,从而释放资源。
如果data[size]不置为null,它会被称为loitering Objects(游荡对象),一点用都没有,而且垃圾回收机制是不回收的。loitering Objects != memory leak
只是为了程序优化而已,如果能手动去除它就更好了,因此最后添加data[size] = null;
,你是在不想添加也是可以的。
现在我附上完整的ArrayElement.java文件里面的代码:
package com.suanfa.test.Array;
public class ArrayElement<E> {
private E[] data; //数组
private int size; //数组中元素的个数
/**
* 带容量参数构造函数
*
* @param capacity 数组容量
* **/
public ArrayElement(int capacity){
data = (E[]) new Object[capacity];
size =0;
}
/***
* 默认的构造函数
* */
public ArrayElement(){
this(10);
}
/**
* 静态数组入参构造函数
* @param data 传入静态数组
*/
public ArrayElement(E[] data) {
this.data = data;
}
/**
* 获取数组元素个数
*
* @return size 数组元素个数
*/
public int getSize() {
return size;
}
/**
* 获取数组的容量
*
* @return capacity 获取容量
*/
public int getCapacity(){
return data.length;
}
/**
* 判断数组是否为空
*
* @return 是否为空
*/
public boolean isEmpty(){
return size==0;
}
/**
* 向所有元素末尾添加一个新元素。
*
* @param e 添加的元素
*/
public void addList(E e){
// if(size ==data.length){
// throw new IllegalArgumentException("对不起,数组容量已经满了,添加元素操作失败!");
// }
data[size++]=e; //这个和下面两行代码表达的意思一样,但还是建议分开写。
// data[size] =e;
// size++;
add(size,e);
}
/**
* 向所有元素开头添加一个新元素。
*
* @param e 添加的元素
*/
public void addFirst(E e){
add(0,e);
}
/**
* 在第Index的位置,添加e元素
*
* @param e 添加的元素,index 待添加元素的索引号
*/
public void add(int index, E e){
if(size ==data.length){
throw new IllegalArgumentException("对不起,数组容量已经满了,添加元素操作失败!");
}
if(index<0 || index>size){
throw new IllegalArgumentException("对不起,索引号必须在0和size之间");
}
//注意数组的索引范围为0-length-1;
//开始位置: size也就是最后一个元素(索引号size-1),目标位置:index(这个元素也是要挪移的),然后新的元素将其进行覆盖
for(int i =size-1;i>=index;i--){
data[i+1]=data[i];
}
//将新的元素添加到index位置处
data[index]=e;
//size每次都需要+1
size++;
}
/***
* 打印数组信息及遍历元素
*
* @return 数组信息和元素遍历结果
* */
@Override
public String toString() {
StringBuilder res =new StringBuilder();
res.append(String.format("ArrayElement: size = %d, capacity = %d\n",size,data.length));
res.append("[");
for(int i =0;i<size;i++){
res.append(data[i]);
//判断是否是最后一个元素
if(i != size-1){