在java程序设计中,经常需要用到数组,数组的存储和读取速度是数据结构中最快的一种。但是,在稍为复杂的程序设计中,我们就会发现数组的局限性,其局限性有一部分是其优点所带来的。例如:数组的长度是固定的,而且数组所存储的数据类型也是固定的。然而,在实际应用中,经常需要根据所要存储的数据的长度确定要开辟的内存空间,而且,需要存储不同类型的数据,例如存储对象。另一方面,我们需要一些通用的方法,以便对不同类型的对象进行处理,例如:一种通用的排序方法,对所有类型的对象都可以进行排序。这样,就可以增加代码的重用度,减少工作量。
由于在java中,Object是所有类的父类,因此在这些方法中,要把参数,返回值设为Object类型的,以便接收所有类型的对象。
为深入体会泛型的作用,我们先在不使用泛型的前提下,写一个数组队列类,对所有类型的传人对象都能进行处理。数组队列具有数组所不具有的灵活性,其长度可变,存储的数据类型可变。
下面是一个自定义的数组队列类,具有增删查改功能
package cn.lzj0724; /** * 纯粹的数组队列,实行增删改查等功能,一个第三方的类 * * @author lzj * */ public class MyArrayList{ private Object[] array;// 声明对象数组 private int size = 0;// 声明size属性,设置初值为0 /** * 构造方法 */ public MyArrayList() { array = new Object[0];// 初始化 } public MyArrayList(int size) { array = new Object[size];// 初始化 } /** * 向数组队列中添加元素的方法 * * @param elements要添加的元素 */ public void add(Object element) { // 创建一个新的数组,长度为size+1 Object[] newArray = new Object[size + 1]; for (int i = 0; i < size; i++) { newArray[i] = array[i];// 赋值 } newArray[size] = element;// 添加元素至数组最后的位置 size++;// size加1 array = newArray;// 把newArray的地址给array } /** * 根据索引删除对应的元素(删除区别于移除,删除需要释放空间) * * @param index要删除的元素的索引 * @return */ public Object delete(int index) { if (index < 0 || index >= size)// 防止所给下标为负值或超出size return null; Object temp;// Object类型变量 temp = array[index];// 将被删除的元素赋予临时变量temp Object[] newArray = new Object[size - 1];// 创建Object类数组,数组长度减1 // 小于索引时,照搬过去 for (int i = 0; i < index; i++) { newArray[i] = array[i];// 把值移到新数组中 } // 大于索引时 for (int i = index; i < size - 1; i++) { array[i] = array[i + 1];// 移位填补空位 newArray[i] = array[i];// 把值移到新数组中 } array = newArray;// 把newArray的地址给array size--; return temp;// 返回被删除的元素 } /** * 根据索引插入对应的元素 * * @param index要插入的位置的索引 * @return */ public Object ins(int index, Object element) { Object[] newArray = new Object[size + 1];// 创建Object类数组,数组长度减1 // 小于索引时,照搬过去 for (int i = 0; i < index; i++) { newArray[i] = array[i];// 把值移到新数组中 } newArray[index] = element; // 大于索引时 for (int i = index; i < size; i++) { newArray[i + 1] = array[i];// 把值移到新数组中 } array = newArray;// 把newArray的地址给array size++; return element;// 返回被插入的元素 } // 得到数组队列的长度的方法 public int size() { return size; } // 根据索引和传人的元素设定相应的元素 public void set(int index, Object obj) { array[index] = obj; } // 根据索引得到相应的元素 public Object get(int index) { if (index < 0 || index >= size)// 防止所给下标为负值或超出size return null; return array[index]; } public Object find(Object obj) { for (int i = 0; i < size; i++) { if (array[i].equals(obj)) return array[i]; } return null; } // 修改元素,根据元素和下标 public void modify(Object element, int index) { array[index] = element; } }
测试数组队列:
package cn.lzj0724; import java.util.Scanner; /** * 测试数组队列的类 * * @author lzj * */ public class TestArrayList { public static void main(String[] args) { // 实例化MyArrayList类的对象 数组队列对象 MyArrayList list = new MyArrayList(); Student[] stu = new Student[5];//实例化一个Student类型的数组 // 实例化一个随机类对象 java.util.Random rand = new java.util.Random(); int size = stu.length; // 循环添加元素 for (int i = 0; i < size; i++) { stu[i] = new Student("张" + i, "male", i, rand.nextInt(100), rand.nextInt(100), rand.nextInt(100));// 创建学生数组,随机分数 list.add(stu[i]);// 将Student元素添加到数组队列之中 System.out.println(list.get(i));// 输出 } System.out.println("------------------------"); list.delete(2);// 删除数组队列中下标为2的元素 for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i));// 输出 } System.out.println("------------------------"); list.modify(stu[1], 3);// 把数组队列中下标为3的元素修改为下标为1的元素 for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } System.out.println("----排序后-------"); sort(list);//调用排序方法 for (int i = 0; i < list.size(); i++) {// 输出 System.out.println(list.get(i)); } System.out.println("------------------------"); Student stu9 =new Student("张六", "male", 9, rand.nextInt(100), rand.nextInt(100), rand.nextInt(100)); list.ins(3,stu9);// 创建学生数组,随机分数) for (int i = 0; i < list.size(); i++) {// 输出 System.out.println(list.get(i)); } // 选择排序 public static void sort(MyArrayList list) { Student[] stu = new Student[list.size()];// 定义Student类型数组 for (int i = 0; i < list.size(); i++) { stu[i] = (Student) list.get(i);// 强制转换为Student类型 } for (int i = 0; i < list.size(); i++) { int minIndex = i; // 找出最小的一个索引 for (int j = i + 1; j < list.size(); j++) { if (stu[j].getAverage() < stu[minIndex].getAverage()) { minIndex = j; } } // 交换 Student temp = stu[i]; stu[i] = stu[minIndex]; stu[minIndex] = temp; } for (int i = 0; i < list.size(); i++) { list.set(i, stu[i]);//把改变后的stu数组应用到list中 } } }
运行结果如下:
代码实现了对学生信息的管理和排序处理,也可以看出,由于list不能直接调用Student类的子方法,该代码排序时事实上是对学生数组进行了排序处理,然后才逐个add到数组队列list中的。若不能实现对list的直接处理的话,代码的冗余度将会很高,在对不同的类进行处理时,也要修改,添加很多行的代码。
那么如何解决这个问题呢?
需要用到泛型。对MyArrayList类进行修改
泛型可以泛指java中的任意一种引用类型(不能指代基本数据类型)。
泛型的格式:
类名<引用数据类型> 对象名 = new 类名<引用数据类型> ( );
Java中的泛型有三种,分别为E、K、V,E是element,代表元素的数据类型;K即key,代表键值类型;V即value,代表值的类型。
声明支持泛型的类的方法:
Public class 类名
为方法添加元素的时候,形参表中应将元素声明为泛型型,泛指任意一种引用类型,即用户可以在实参表中填入任意数据类型。方法的返回值类型也应为泛型,在返回之前需要强制转型为泛型。
对于支持泛型的类,在创建类的对象时为了防止任意类型的元素被传入,可以再创建对象时使用尖括号指定特定的引用数据类型,此时如果向对象中传入非指定的引用类型的数据类型便会引发错误提示。
例如:
public E get(int index){ E st = (E)array[index]; return st; }
这样,在使用该队列对象前,只要指定要放入对象的类型,之后,这个队列就只能放入这种类型的对象,而且,取出后,也是这种类型的对象
使用泛型的数组队列类如下:
package cn.lzj0724; /** * 纯粹的数组队列,实行增删改查等功能,一个第三方的类 * * @author lzj * */ public class MyArrayList{ private Object[] array;// 声明对象数组 private int size = 0;// 声明size属性,设置初值为0 /** * 构造方法 */ public MyArrayList() { array = new Object[0];// 初始化 } public MyArrayList(int size) { array = new Object[size];// 初始化 } /** * 向数组队列中添加元素的方法 * * @param elements要添加的元素 */ public void add(E element) { // 创建一个新的数组,长度为size+1 Object[] newArray = new Object[size + 1]; for (int i = 0; i < size; i++) { newArray[i] = array[i];// 赋值 } newArray[size] = element;// 添加元素至数组最后的位置 size++;// size加1 array = newArray;// 把newArray的地址给array } /** * 根据索引删除对应的元素(删除区别于移除,删除需要释放空间) * * @param index要删除的元素的索引 * @return */ public E delete(int index) { if (index < 0 || index >= size)// 防止所给下标为负值或超出size return null; Object temp;// Object类型变量 temp = array[index];// 将被删除的元素赋予临时变量temp Object[] newArray = new Object[size - 1];// 创建Object类数组,数组长度减1 // 小于索引时,照搬过去 for (int i = 0; i < index; i++) { newArray[i] = array[i];// 把值移到新数组中 } // 大于索引时 for (int i = index; i < size - 1; i++) { array[i] = array[i + 1];// 移位填补空位 newArray[i] = array[i];// 把值移到新数组中 } array = newArray;// 把newArray的地址给array size--; return (E) temp;// 返回被删除的元素 } /** * 根据索引插入对应的元素 * * @param index要插入的位置的索引 * @return */ public E ins(int index, E element) { Object[] newArray = new Object[size + 1];// 创建Object类数组,数组长度减1 // 小于索引时,照搬过去 for (int i = 0; i < index; i++) { newArray[i] = array[i];// 把值移到新数组中 } newArray[index] = element; // 大于索引时 for (int i = index; i < size; i++) { newArray[i + 1] = array[i];// 把值移到新数组中 } array = newArray;// 把newArray的地址给array size++; return element;// 返回被插入的元素 } // 得到数组队列的长度的方法 public int size() { return size; } // 根据索引和传人的元素设定相应的元素 public void set(int index, E obj) { array[index] = obj; } // 根据索引得到相应的元素 public E get(int index) { if (index < 0 || index >= size)// 防止所给下标为负值或超出size return null; return (E) array[index]; } public E find(E obj) { for (int i = 0; i < size; i++) { if (array[i].equals(obj)) return (E) array[i]; } return null; } // 修改元素,根据元素和下标 public void modify(E element, int index) { array[index] = element; } }
使用泛型的测试类如下:
package cn.lzj0724; import java.util.Scanner; /** * 测试数组队列的类 * * @author lzj * */ public class TestArrayList { public static void main(String[] args) { // 实例化MyArrayList类的对象 数组队列对象 MyArrayListlist = new MyArrayList (); Student[] stu = new Student[5];// 实例化一个Student类型的数组 // 实例化一个随机类对象 java.util.Random rand = new java.util.Random(); int size = stu.length; // 循环添加元素 for (int i = 0; i < size; i++) { stu[i] = new Student("张" + i, "male", i, rand.nextInt(100), rand.nextInt(100), rand.nextInt(100));// 创建学生数组,随机分数 list.add(stu[i]);// 将Student元素添加到数组队列之中 System.out.println(list.get(i));// 输出 } System.out.println("------------------------"); list.delete(2);// 删除数组队列中下标为2的元素 for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } System.out.println("------------------------"); list.modify(stu[1], 3);// 把数组队列中下标为3的元素修改为下标为1的元素 // 输出 for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } System.out.println("----排序后-------"); sort(list);// 调用排序方法,传人MyArrayList类型对象 // 输出 for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } System.out.println("------------------------"); Student stu9 = new Student("张六", "male", 9, rand.nextInt(100), rand.nextInt(100), rand.nextInt(100)); list.ins(3, stu9);// 把学生对象stu9插入数组队列3处 // 输出 for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } // 修改学生信息 Scanner read = new Scanner(System.in);// 实例化Scanner int i = -1; System.out.println(" 修改学生信息"); System.out.println(); System.out.println("======================="); System.out.println(); System.out.println("请输入要修改的学生学号:"); int number1 = read.nextInt();// 得到键盘输入的数字 for (i = 0; i < size; i++) { if (list.get(i).getNumber() == number1) { System.out.println(number1 + "学号的学生姓名为:" + list.get(i).getName()); System.out.println("1 姓名 2 性别 3 语文成绩 4 数学成绩 5 英语成绩"); System.out.println("该学生的哪一项信息需要修改:"); int r = read.nextInt();// 得到键盘输入的数字 switch (r) { case 1: System.out.println("原姓名为" + list.get(i).getName() + "现姓名修改为:"); String s1 = read.nextLine(); s1 = read.nextLine(); list.get(i).setName(s1);// 调用数组队列的第i个元素的setName方法,把name属性设置为s1 break; case 2: System.out.println("原姓别为" + list.get(i).getSex() + "现姓别修改为:"); String s2 = read.nextLine(); s2 = read.nextLine(); list.get(i).setSex(s2);// 调用数组队列的第i个元素的setSex方法,把sex属性设置为s2 break; case 3: System.out.println("原语文成绩为" + list.get(i).getChinese() + "现语文修改为:"); double d1 = read.nextDouble(); list.get(i).setChinese(d1);// 调用数组队列的第i个元素的setChinese方法,把Chinese属性设置为d1 break; case 4: System.out.println("原数学成绩为" + list.get(i).getChinese() + "现数学修改为:"); double d2 = read.nextDouble(); list.get(i).setMath(d2);// 调用数组队列的第i个元素的setMath方法,把Math属性设置为d2 break; case 5: System.out.println("原英语成绩为" + list.get(i).getChinese() + "现英语修改为:"); double d3 = read.nextDouble(); list.get(i).setEnglish(d3);// 调用数组队列的第i个元素的setEnglish方法,把English属性设置为d3 break; } } } for (int j = 0; j < list.size(); j++) {// 输出list数组队列中的内容 System.out.println(list.get(j)); } // 查找学生信息 Scanner read1 = new Scanner(System.in);// 实例化Scanner int j = -1; System.out.println(" 查找学生信息"); System.out.println(); System.out.println("======================="); System.out.println(); System.out.println("请输入要查找的学生学号:"); int number2 = read.nextInt(); System.out.println(number2 + "学号的学生姓名为:" + list.get(number2).getName()); System.out.println(list.get(number2));// 输出所查找的学生的全部信息 } // 选择排序 public static void sort(MyArrayList list) { for (int i = 0; i < list.size(); i++) { int minIndex = i; // 找出最小的一个索引 for (int j = i + 1; j < list.size(); j++) { if (list.get(j).getAverage() < list.get(minIndex).getAverage()) {// 调用第i个元素的getAverage方法,与下标为minIndex的元素的Average比较,若前者小于后者 minIndex = j;//把minIndex的值改为j } } // 交换 Student temp = list.get(i); list.set(i, list.get(minIndex)); list.set(minIndex, temp); } } }
所使用的学生类如下:
package cn.lzj0724; public class Student { private String name;// 姓名 private String sex;// 性别 private int number;// 学号 private double chinese, math, english;// 科目 private double average;// 平均分 public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } public double getChinese() { return chinese; } public void setChinese(double chinese) { this.chinese = chinese; } public double getMath() { return math; } public void setMath(double math) { this.math = math; } public double getEnglish() { return english; } public void setEnglish(double english) { this.english = english; } public double getAverage() { return average; } public void setAverage(double average) { this.average = average; } public Student() { } public Student(String name, String sex, int number, double chinese, double math, double english) { this.name = name; this.sex = sex; this.number = number; this.chinese = chinese; this.math = math; this.english = english; average = (chinese + math + english) / 3; } // 控制输出格式 public String toString() { return name + " " + "性别:" + sex + " 学号:" + number + " " + "语文:" + chinese + " " + "数学:" + math + " " + "英语:" + english + "平均分" + average; } /** * 重写比较方法,然后进行比较时调用 * @param stu * @return */ public boolean equals(Student [] stu){ return false; } }
收获:
1.学会了泛型的使用
2.学会了编写和使用数组队列,为五子棋的悔棋的实现打下了基础
不足:
1.对泛型的理解还不够深刻透彻
2.未实现学生信息管理的图形界面