2-6 使用泛型
优化数组类 ---- 使用泛型
将上一节刚创建的只适用int型 Array类,优化成任意类型
java中泛型可以放置任意类(不包括基本类型8种),只能是类对象。
但基本类型可以使用他们的包装类
改变成泛型类的注意事项
-
更改类名为泛型类
在class Array后面添加<> ,
<>内部可以起名字Item Element等等
根据习惯,起名,这个E是随便起的,E本质代表某个类型,是类,在具体使用时可以再声明
所以类名变成public class Array {} -
将类中的逻辑(方法和变量)修改成泛型
变量
private int[] data;→改成private E[] data;构造函数
data = new int[capacity];→改成 data = new E[capacity];
但java中不支持这种写法,泛型数组要换一种写法,要绕一个湾子,先new出一个object[]数组,然后将数组强制转换成泛型(E())
data = new int[capacity];→改成data = (E[])new Object[capacity]方法中
将方法接收参数中的 data数组和数组元素e 都改成泛型E
方法体中的data和E也改成泛型E= =改成equals
原本是int型变量比较相等使用两个= =
但泛型比较是对象比较,使用equals
不变的在前面 变量放在equals(3)里面
== 两个等号 是值比较
equals是引用比较 == -
remove泛型方法中,每个数组元素存储的是对象的引用
当经过方法中删除逻辑时,size-- ,可对象引用还是存在不用的元素中了。需要将删除的对象引用释放掉。在size–;后面加一句data[size]=null;手动释放掉不用的。
当data[size]不用时,即使不被手动null,以后也会被别的取代。这种数据被叫做loitering objects 闲逛对象
loitering objects != memory leak
闲逛对象!=内存泄漏,但如果可以将loitering objects手动处理掉是最好的 -
当使用泛型Array时,要指定声明的类型
Array arr = new Array(20);→改成 Array arr = new Array<>(20);
使用泛型Array
//定义一个Student类型 ,作为泛型测试
public class Student {
//定义变量
private String studentName;
private int studentAge;
//构造函数,当new Student对象时使用构造函数来初始化
public Student (String studentName, int studentAge) {
this.studentName = studentName;
this.studentAge = studentAge;
}
//重新toString,打印对象时可以按照对象特点输出
@Override
public String toString () {
return String.format("studeng : name = %s, age = %d \n",studentName,studentAge);
}
}
public static void main(String[] args) {
Array<Student> arr = new Array<>();
arr.addLast(new Student("zhang",10));
arr.addLast(new Student("wang",22));
arr.addLast(new Student("li",33));
System.out.println(arr);
}
修改成泛型后的Array类
public class Array<E> {
//创建一个数组
private E[] data;
//size代表数组有多少个有效的元素
//size也指向了第一个没有元素的索引位置
private int size;
//构造函数,传入数组的容量capacity来构造Array
public Array(int capacity) {
data = (E[])new Object[capacity];
size = 0;
}
//默认构造函数,当用户不知道(不传入capacity时调用)数组容量时,用无参构造函数创建一个10的数组
public Array() {
this(10); //这个参数的语义时capacity,时IDEA提供的
}
//用户传入静态数组,然后生成Array类
public Array(E[] args) {
data = args;
size = args.length;
}
//获取数组有多少个元素
public int getSize() {
return size;
}
//获取数组的容量
public int getCapactiy() {
return data.length;
}
//返回数组是否为空
public boolean isEmpty() {
return size == 0;
}
//下面是一个方法,没写完整类
//向数组最后位置添加一个元素,在添加之前,要看数组是否还有空余位置
// public void addLast(int e) {
// if (size == data.length) {
// throw new IllegalArgumentException("AddLast failed. Array is Full.");//先抛个异常,以后再处理
// } else {
// data[size] = e;
// size ++;
// }
// }
//在第index个位置添加一个新元素
public void add(int index, E e) {
if (size == data.length) {
throw new IllegalArgumentException("AddElement failed. Array is Full.");//先抛个异常,以后再处理
} else if (index < 0 || index > size) {
//index>size 代表不是紧密地在最后添加,会有跳过的地方
throw new IllegalArgumentException("AddElement failed. Require index < 0 || index > size.");//先抛个异常,以后再处理
} else {
//index之后的元素依次向后挪动,然后将e放到poz处,size增加1
//要被处理i的开始位置是size-1结束位置是index
for (int i = size - 1; i >= index; i--) {
data[i + 1] = data[i];
}
data[index] = e;
size++;
}
}
//因为上一个方法是可以应用到上上个方法的
public void addLast(E e) {
add(size,e);
}
//复用add方法
public void addFirst(E e) {
add(0,e);
}
//获取index索引位置的元素
public E get (int index) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException("AddElement failed. Require index < 0 || index >= size.");//先抛个异常,以后再处理
} else {
return data[index];
}
}
//查找是否包含某个元素
public boolean contains (E e) {
for (int i = 0; i < size; i ++) {
if (data[i].equals(e)) {
return true;
}
}
return false;
}
//查找某个元素的索引
public int find (E e) {
for (int i = 0; i < size; i ++) {
if (data[i].equals(e)) {
return i;
}
}
return -1;
}
//查找某个元素e的所有索引
public void findAll (E e) {
StringBuilder ret;
for (int i = 0; i < size; i ++) {
if (data[i].equals(e)) {
remove(i);
}
}
}
//更新index索引位置的元素
public void set (int index,E e) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException("AddElement failed. Require index < 0 || index >= size.");//先抛个异常,以后再处理
} else {
data[index] = e;
}
}
//删除索引为index的某个元素,并且返回该元素的值
public E remove (int index) {
E ret = data[index];
if (index < 0 || index > size) {
//index>size 代表不是紧密地在最后添加,会有跳过的地方
throw new IllegalArgumentException("AddElement failed. Require index < 0 || index > size.");//先抛个异常,以后再处理
} else {
//index之后的元素依次向前挪动,size减1
//要被处理i的开始位置是size-1结束位置是index
for (int i = index; i < size -1 ; i ++) {
data[i] = data[i + 1];
}
data[size] = null;
size --;
}
return ret;
}
//从数组中删除第一个元素,复用remove
public E removeFirst (int index) {
return remove(0);
}
//从数组中删除最后一个元素,复用remove
public E removeLast (int index) {
return remove(size - 1);
}
//从数组中删除元素e(e存在的情况下) 复用find 和 remove
public boolean removeElement (E e) {
int index = find(e);
if (index != -1) {
remove(index);
return true;
} else {
return false;
}
}
//从数组中删除所有元素e
public void removeAllElement (E e) {
for (int i = 0; i < size; i ++) {
if (data[i].equals(e)) {
remove(i);
}
}
}
@Override
public String toString () {
StringBuilder res = new StringBuilder();
res.append(String.format("Array: size = %d, capacity = %d \n",size,data.length));//先打印数组有多少个内容和总容量
res.append('[');//然后追究第一个左边的【
for (int i = 0; i < size; i ++) {//从第一个打印,打印到index=size-1(即最后一个内容)
res.append(data[i]);
if(i != size -1) {//如果不是最后一个内容,则后面加的,逗号
res.append(", ");
}
res.append(']');//如果是最后一个则追加 】
}
return res.toString();
}
}