Java如何:从具有泛型的类创建泛型数组?

3
我有一个类FirstClass<O>和一个类SecondClass<O>,我想在从FirstClass<O>调用的例程中,在SecondClass<O>中创建一个O[],其中O是一个泛型类参数。我无法找到如何做到这一点。
我需要一个特定的O[](而不是ArrayList<O>或类似的东西),因为我需要在循环体内经常从中获取元素,并且它对我的算法的执行时间很重要。
所以我想要类似于以下的东西。
public class FirstClass<O> {
    void someRoutine(n and other params) {
        //Do some stuff
        SecondClass<O> = new SecondClass(n, possibly_other_params);
        //Do some stuff
    }
}

并且

public class SecondClass<O> {
    O[] myArray;
    SecondClass<O>(int n, possibly_other_params) {
        //Here the constructor that creates an O[n]
    }
}

我在网上找到了一些方法,但它们对我的情况不起作用:
  • 使用O[] array = (O[]) new Object[n];,但编译器无法通过。
  • 使用Object[] array = new Object[n];,并且每次从数组中请求数据时进行(O)类型转换,但这太慢了。
  • 使用Array.newInstance(Class<O> type, int n);,其中O o;type=o.class,但是它抱怨type现在是类型为Class<CAP#1>而不是Class<O>的类型,无论CAP#1是什么意思...
在Java中如何正确执行此操作,并考虑最优执行速度?

5
你真正对这进行了分析吗?铸造实际上是否导致了你的性能问题?考虑到Java中的泛型在运行时被擦除,我不认为有一个简单的解决方法可以让它按照你期望的方式工作。我认为你最好重新审视一下你期望的API,而不是试图让Java的泛型工作符合你的期望。 - ziesemer
只需使用 ArrayList,唉。 - Paul Bellora
ArrayList的问题在于我需要不断地更改数组中的一个元素,这基本上就是内部循环所做的所有工作,因此也是算法中最昂贵的部分。反复执行a[i]=a[j]以获得某些排列比一直执行list.set(i,list.get(j))要便宜得多。使用固定的数组类型确实可以使其更快,但是我需要为O可能的25种类型之一复制粘贴代码,这并不是真正的好风格。:/ 好吧,如果不可能,那当然就不可能了。无论如何,感谢您的建议。 - user1111929
4个回答

3

Java在泛型的处理上存在着很多问题,但如果你愿意做一些小的调整,就可以实现接近你想要的效果。请参考:

public class FirstClass<O> {
    Class<O> type;

    public static <O> FirstClass<O> create(Class<O> type) {
        return new FirstClass<O>(type);
    }

    public FirstClass(Class<O> type) {
        this.type = type;
    }

    public void routine(int size /*, other params */ ) {
        SecondClass<O> instance = new SecondClass<O>(type, size);
    }
}

public class SecondClass<O> {
    public O[] array;

    @SuppressWarnings("unchecked")
    public SecondClass(Class<O> type,int size) {
        array = (O[])Array.newInstance(type,size);
    }
}

一个使用案例:

FirstClass<Integer> instance = FirstClass.create(Integer.class);
instance.routine(110);

这只是一个粗略的例子,尽管我相信您可以在不使用这种方法的情况下完成类似的事情。


1
使用工厂方法来推断类型,可以简化代码!static <T> FirstClass<T> create(final Class<T> type) { ... }instance = FirstClass.create(Integer.class); - obataku
更不用说你关于不能使用基本类型的观点是错误的...请参见Integer.TYPE,或者等价地,使用int.class - obataku
@oldrinb 我认为他的意思是使用泛型排除了原始类型,而不是 Array.newInstance 调用本身。 - Paul Bellora

1
使用O[] array = (O[]) new Object[n]; 但这需要每次从数组请求内容时进行(O)转换,因此速度太慢了。
什么?类型O[]的整个重点在于获取其中的内容时不需要(O)转换。

抱歉,显然我错过了这一点。现在已经更正:使用O[] array = (O[]) new Object[n];是不可能的,因为这些类型是不可转换的,需要每次进行(O)强制转换的是Object[] array = new Object[n]; - user1111929
1
@user1111929: "这些类型无法转换" 其实不是这样的。O 被类型擦除为 Object。只要数组没有超出 O 的作用域,它就不会引起任何问题。 - newacct
你是正确的,我道歉。它的速度不如本地的O[],但它可以工作,并且比(O[])Array.newInstance(type,size)解决方案更清晰。谢谢! - user1111929

0
重复我之前的评论,希望能将此问题标记为已解答:
你实际上对这个进行了分析吗?强制类型转换是否真的是引起性能问题的原因?考虑到 Java 中泛型在运行时被擦除,我不认为有容易的解决方法来让它按照你的期望工作。我认为你最好尝试重新审视你期望的 API,而不是试图让 Java 的泛型按照你想要的方式工作。

1
我为您创建了一个简化版本的算法,并进行了适当的时间测量:http://pastebin.com/QeCW0W1R。正如您可以在计算机上验证的那样,强制类型转换会导致35%-50%的延迟。这不是什么小事。在我更复杂的最终版本中,它只有15-20%的延迟,但如果有任何替代方案,这仍然不是可以忽略的问题。 - user1111929
实际上,你的基准测试明显存在缺陷,没有留出时间让JIT预热或其他操作。我不会相信它的结果是准确的。尝试使用像Caliper这样的工具,在Java中进行适当的基准测试。 - Louis Wasserman
取决于你如何使用它。我按照这个顺序运行了5次基准测试,然后按照测试顺序反转的方式运行了5次,结果总是相同的。如果以那种方式使用仍然明显存在缺陷吗? - user1111929

0
这是关于 ArrayList.set源代码
public E set(int index, E element) {
    rangeCheck(index);

    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

虽然它会进行额外的查找以获取旧元素,但这是我们所说的随机访问(即:O(1)时间)。除非你有确凿的数据表明它在减慢速度,否则只需使用ArrayList


1
我为您创建了一个简化版本的算法,并进行了适当的时间测量:http://pastebin.com/NUj90ZPv。您可以在计算机上验证,使用 None[]ArrayList<None> 进行此算法时始终存在4倍差异因素。 - user1111929
1
@user1111929 嗯,我无法反驳。尝试Jordan White的解决方案 - 假设数组本身的创建是罕见的,那应该可以解决问题。 - Paul Bellora
我没有看到直接测试它的方法,但出于好奇,为什么他的解决方案中数组的创建比创建ArrayList<O>更昂贵?难道不是以几乎相同的方式在内部创建一个新的O[]吗? - user1111929
1
Array.newInstance 使用反射/本地魔法来创建动态类型数组,使其稍微慢一些。不过,对于您的要求来说,这种差异可能可以忽略不计。 - Paul Bellora
实际上,你的基准测试明显存在缺陷,没有给JIT预热或任何其他时间。我不会相信它的结果是准确的。尝试使用像[Caliper](https://code.google.com/p/caliper/)这样的工具,在Java中知道如何进行正确的基准测试。 - Louis Wasserman
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接