基于泛型的数组列表

时间:2022-07-22 19:47:19

  在《数组和数组列表》那篇随笔里我们定义了一个int类型的数组列表,但是这样实际上是有问题的,在日后的开发中我们使用的数组列表实际上并不一定是int类型的,甚至于,还不一定会是基本数据类型的。那这个时候,按照原来的方法,我们在开发中每要定义一个数组列表,我们就要重写一遍,这样做实际上是非常不友好的。那么有什么方法来解决这一问题呢,这样我们便引出了泛型这个概念

泛型

  什么是泛型?泛型简单来理解实际上就是一个类类型,它是表示一个类的类型,而不具体表示某一个对象。由于在此重点是数组列表,关于泛型的其他便不再展开。

泛型的数组列表

  与前文类似,基于泛型的数组列表也是由接口类和继承类来实现。参考代码如下:

 1 public interface C<E> {
 2 
 3     
 4     /**
 5      * 添加一个元素
 6      */
 7     public void store(E element);
 8     
 9     /**
10      * 获取数组长度
11      */
12     public int getSize();
13     
14     /**
15      * 修改元素
16      */
17     public void upData(int index, E newElement);
18     
19     /**
20      * 取出元素
21      */
22     public E getElement(int index);
23     
24     /**
25      * 删除元素
26      */
27     public void delete(int index);
28 }

  接口的定义

  在定义泛型的数组列表接口中,用一对尖括弧<>来表示要在此传入的具体类型,其中的E表示的是Element,即元素,也就是这个数组列表具体是一个什么类型的数组列表。

public class D<E> implements C<E> {

    private int count; // 保存数组的长度
    private Object data[];    //创建一个数组

    @Override
    public void store(E element) {
        // TODO Auto-generated method stub

        // 创建一个临时数组
        Object[] temp = new Object[count + 1];

        // 将原数组内容复制到临时数组中
        for (int i = 0; i < count; i++) {

            temp[i] = data[i];

        }

        data = temp;
        
        //添加新的元素
        data[count] = element;
        
        //让数组长度加一
        count++;
        
    }

    @Override
    public int getSize() {
        // TODO Auto-generated method stub
        return count;
    }

    @Override
    public void upData(int index, E newElement) {
        // TODO Auto-generated method stub
        data[index] = newElement;
        
    }

    @Override
    public E getElement(int index) {
        // TODO Auto-generated method stub
        return (E)data[index];
    }

    @Override
    public void delete(int index) {
        // TODO Auto-generated method stub
        
        //创建一个临时数组
        Object temp[] = new Object[count-1];
        
        //将删除项前的元素复制到临时数组中
        for(int i = 0; i < index-1; i++){
            temp[i] = data[i];
        }
        
        //将删除项后的元素复制到临时数组中
        for(int i = index-1; i < count-1; i++){
            temp[i] = data[i+1];
        }
        
        data = temp;
        
        //数组长度减一
        count--;
    }

}

  继承类的实现

  在实现继承类的过程中,有几点需要注意的。首先是数组的创建,因为不能确定实际上是要创建一个什么类型的数组,此时已经不能创建int型的了,那应该创建什么类型的呢?可能我们会想创建一个E类型的数组,但实际上这会报错

基于泛型的数组列表

一个解决方案就是创建一个Object类型的数组,因为Object类是所有类型的父类,在我们需要得到数组中一个具体的值的时候,再将这个数组项取出强制转型为E类型并传出。事实证明,这是可行的。

  另外还有一个问题,就是临时数组的创建。在每次往数组中增加一个元素时创建一个临时数组并更新原数组,这种逻辑是很清晰的,但是一旦数组的长度高达成千上万时,这样便不可取了。它会消耗代价很高的空间和时间。那又什么办法去解决呢?

  优化的方法很多,我在这里使用的方法仅供参考。增加一个判断,一次创建一个具有上限的临时数组,当增加的元素达到临时数组上限时,再次扩充这个上限。代码实现如下:

 1 public class D<E> implements C<E> {
 2 
 3     private int count; // 保存数组的长度
 4     private Object data[] = new Object[0];    //创建一个数组
 5 
 6     @Override
 7     public void store(E element) {
 8         // TODO Auto-generated method stub
 9 
10         if(count==data.length){
11             // 创建一个临时数组
12         Object[] temp = new Object[count + 1];
13 
14         // 将原数组内容复制到临时数组中
15         for (int i = 0; i < count; i++) {
16 
17             temp[i] = data[i];
18 
19         }
20 
21         data = temp;
22         }
23         
24         
25         
26         //添加新的元素
27         data[count] = element;
28         
29         //让数组长度加一
30         count++;
31         
32     }
33 
34     @Override
35     public int getSize() {
36         // TODO Auto-generated method stub
37         return count;
38     }
39 
40     @Override
41     public void upData(int index, E newElement) {
42         // TODO Auto-generated method stub
43         data[index] = newElement;
44         
45     }
46 
47     @Override
48     public E getElement(int index) {
49         // TODO Auto-generated method stub
50         return (E)data[index];
51     }
52 
53     @Override
54     public void delete(int index) {
55         // TODO Auto-generated method stub
56         
57         //创建一个临时数组
58         Object temp[] = new Object[count-1];
59         
60         //将删除项前的元素复制到临时数组中
61         for(int i = 0; i < index-1; i++){
62             temp[i] = data[i];
63         }
64         
65         //将删除项后的元素复制到临时数组中
66         for(int i = index-1; i < count-1; i++){
67             temp[i] = data[i+1];
68         }
69         
70         data = temp;
71         
72         //数组长度减一
73         count--;
74     }
75 
76 }

  测试运行

 

 1 public class Client {
 2 
 3     
 4     public static void main(String[] args) {
 5         System.out.println("实例化一个数组列表d");
 6         D<Integer> d = new D<Integer>();
 7         System.out.println("*******添加项************");
 8         for (int i = 0; i < 5; i++) {
 9             d.store(i);
10             System.out.println("d的第" + i + "项为:" + d.getElement(i));
11         }
12         System.out.println("数组长度为" + d.getSize());
13         System.out.println("**********删除项***************");
14         d.delete(2);
15         for (int i = 0; i < d.getSize(); i++) {
16             System.out.println("d的第" + i + "项为:" + d.getElement(i));
17         }
18         System.out.println("数组长度为" + d.getSize());
19         System.out.println("***********更新项*************");
20         d.upData(3, 15);
21         for (int i = 0; i < d.getSize(); i++) {
22             System.out.println("d的第" + i + "项为:" + d.getElement(i));
23         }
24         System.out.println("数组长度为" + d.getSize());
25 
26     }
27 
28 }

  运行结果

实例化一个数组列表d
*******添加项************
d的第0项为:0
d的第1项为:1
d的第2项为:2
d的第3项为:3
d的第4项为:4
数组长度为5
**********删除项***************
d的第0项为:0
d的第1项为:2
d的第2项为:3
d的第3项为:4
数组长度为4
***********更新项*************
d的第0项为:0
d的第1项为:2
d的第2项为:3
d的第3项为:15
数组长度为4