制作通用数组是不是不好的做法,还有什么替代方案?

2022-09-04 00:43:30

我已经在学校和C++一起编码了3年。我2天前才开始用Java编码;我的问题是:

制作泛型数组是不是不好的做法?还有什么替代方案?

我很困惑,除了做一些奇怪的事情之外,我似乎无法制作一个通用数组,例如这个例子:

//Class implementing the MergeSort algorithm with generic types
// Revised by Doina January 2014

package Sorting;

import java.lang.*;

public class MergeSort {

    // Wrapper method for the real algorithm
    // T is the generic type which will be instantiated at runtime
    //  elementas are required to be comparable
    public static <T extends Comparable<T>> void sort(T[] a) {
        mergesort(a, 0, a.length - 1);
    }

    // Recursive mergesort method, following the pseudocode
    private static <T extends Comparable<T>> void mergesort(T[] a, int i, int j) {
        if (j - i < 1) return;
        int mid = (i + j) / 2;
        mergesort(a, i, mid);
        mergesort(a, mid + 1, j);
        merge(a, i, mid, j);
    }

    // Merge method
    // Here we need to allocate a new array, but Java does not allow allocating arrays of a generic type
    // As a work-around we allocate an array of type Object[] the use type casting
    // This would usually generate a warning, which is suppressed
    @SuppressWarnings("unchecked")
    private static <T extends Comparable<T>> void merge(T[] a, int p, int mid, int q) {

        Object[] tmp = new Object[q - p + 1];
        int i = p;
        int j = mid + 1;
        int k = 0;
        while (i <= mid && j <= q) {
            if (a[i].compareTo(a[j]) <= 0)
                tmp[k] = a[i++];
            else
                tmp[k] = a[j++];
            k++;
        }
        if (i <= mid && j > q) {
            while (i <= mid)
                tmp[k++] = a[i++];
        } else {
            while (j <= q)
                tmp[k++] = a[j++];
        }
        for (k = 0; k < tmp.length; k++) {
            a[k + p] = (T) (tmp[k]); // this is the line that woudl generate the warning
        }
    }

    // Main methos to test the code, using Integer Objects
    public static void main(String[] args) {
        Integer[] a = new Integer[5];
        a[0] = new Integer(2);
        a[1] = new Integer(1);
        a[2] = new Integer(4);
        a[3] = new Integer(3);
        a[4] = new Integer(-1);

        // T will be instantiated to Integer as a resutl of this call
        MergeSort.sort(a);

        // Print the result after the sorting
        for (int i = 0; i < a.length; i++)
            System.out.println(a[i].toString());
    }
}

答案 1

这并不是说这本身就是一个坏主意;只是泛型和数组不能很好地混合。

原因是由于协方差和不变性。数组是协变的(是一个因为是一个,但泛型类是不变的(即使一个是),也不是一个)。Integer[]Object[]IntegerObjectList<Integer>List<Object>IntegerObject

您还必须处理不受检查的转换,这违背了泛型的整个目的。创建泛型数组 - 的最常见方法不是类型安全的,并且无法在编译时强制执行。可以在运行时对此进行推理,但是泛型带到表中的编译时检查在此时会丢失。E[] foo = (E[]) new Object[10];

为了直接回答这个问题,在可能的地方和时间,你想改用Java集合,因为它们与泛型配合得非常好

只是瞥了一眼你提供的代码,我想使用而不是会让你解决你的大多数问题(我希望你正在传递一个,因为这些操作可能会变得昂贵与链接列表)。List<T>T[]ArrayList


答案 2

创建一个泛型数组并不是一个坏习惯,但是正确地这样做是很麻烦的,人们通常会避免它。

它之所以麻烦,是因为泛型被擦除,而数组被重新初始化。也就是说,在编译期间将擦除类型参数,同时保留数组的组件类型。因此,运行时知道每个数组的组件类型,但忘记了所有对象的类型参数,即行

E[] array = new E[10];

不编译,因为运行时需要知道新数组的组件类型,但忘记了是。E

Makoto答案中的解决方法:

E[] array = (E[]) new Object[10];

不是一个好主意,因为它实际上创建了一个 ,但随后向编译器假装是一个 .正如运行时忘记的那样,这种强制转换在运行时也会成功,即使它的类型不正确。但是,运行时仍然通过尽可能执行其他检查来强制执行内存安全,即当对象存储在类型不是泛型的变量中时。例如:Object[]E[]E

static <E> E[] createArray(int size) {
    return (E[]) new Object[size];
}

public static void main(String[] args) {
    String[] array = createArray(size); // throws ClassCastException
    for (String s : array) {
        // whatever
    }
}

也就是说,此解决方法是黑客攻击,仅在特定情况下有效,否则会导致高度令人费解的行为(不包含强制转换的代码行中的ClassCastException...)。

创建的唯一方法是通过反射,通过提供我们所需组件类型的类对象:E[]

Class<E> eClass = ...;
E[] array = Arrays.newInstance(eClass, 10);

但是我们如何获得这个类对象呢?如果我们的调用方知道,他们可以向我们传递一个类文字(如),或者我们可以在其他对象上使用反射。在你的情况下,你手头有另一个,所以你可以问那个数组是什么:Integer.classE[]E

E[] originalArray = ...;
Class<E> eClass = (Class<E>) originalArray.getClass().getComponentType();
E[] newArray = (E[]) Array.newInstance(eClass, size);

这将确保新数组与旧数组的类型相同,除非有人使用 Makoto 的解决方法向我们谎报了该数组的类型。E[]

如您所见,可以创建一个泛型数组,但它非常麻烦,人们通常会竭尽全力避免它。通常的替代方法是使用某种超类型的数组(在合并排序中,可能比 更好,因为您不必强制转换),或者改用 。Comparable[]Object[]ArrayList


推荐