在Java中高效地将向量元素转换类型

4

有没有一种更有效的方法(最好是O(1),而不是O(n),但至少输入速度更快)来对vector的元素进行类型转换,而不是像这样:

public Vector<String> typecastVector(Vector<Object> objects){
    Vector<String> strings = new Vector<String>();
    for(Object o : objects)
        strings.add((String) o);
    return strings;
}

注意

对于那些似乎需要对Vector或其他泛型类进行强制类型转换的人:正如所接受的答案者指出的那样,这可能是一种代码异味,您可能需要在类层次结构中进行重构

具体来说,如果您还没有考虑过,应该考虑使使用上述Vector或其他泛型类的类本身使用泛型。当我在自己的代码中这样做时,我完全消除了我代码中类似于上面函数的功能的需要。

如果您从未在自己的代码中实现过泛型,请查看上面的“泛型”链接。您可能会惊讶地发现,它们可以用于精确实现您认为需要从Vector类型转换中获得的功能。


这段代码中唯一可调整的部分是使用 new ArrayList(objects.size()),它将使用正确的大小初始化列表。在此情况下,您无法获得比 O(n) 更快的速度。 - f1sh
注意:我不想创建一个独立的向量。原始的Vector<Object>已知包含所有字符串。阻止程序知道它们都是字符串的唯一事情是向量的通用标签为<Object>。如果有一种神奇的方法可以将标签翻转为<String>,则元素无需进行任何操作,因为它们已经是字符串。因此,这在技术上可以在O(1)中完成,尽管我理解如果Java的语言限制使其不可能。 - Chris Redford
8个回答

6
如果你只想将Vector<Object>转换为Vector<String>,那是可以实现的。不过,你需要确保向量中的每个对象都是字符串!显然,这样做是行不通的:
    Vector<Object> objectVector = new Vector<Object>();
    Vector<String> stringVector = (Vector<String>)objectVector;

但是你可以这样做:
    Vector<Object> objectVector = new Vector<Object>();
    Vector typelessVector = objectVector;
    Vector<String> stringVector = (Vector<String>)typelessVector;

你会收到一些警告,但代码应该可以正常工作。

如之前所提到的,这感觉像是代码异味。


谢谢。虽然我希望有一种不会产生警告的方法。对我来说,这似乎和将非集合类型转换一样具有风险,所以我不明白为什么他们不允许像你上面第一个代码块那样的操作,而不会产生警告。 - Chris Redford
谢谢你对代码异味的评论。我实际上通过将CollectionSuperClass泛型化来解决了整个情况。然后,我在CollectionSuperClass中使相关函数返回<T extends ElementSuperClass>,并在CollectionSubClass extends CollectionSuperClass<ElementSubClass>中重写它以返回<ElementSubClass>(注意:实际代码中使用了不同的领域特定的“集合”类和元素类)。 - Chris Redford
好的,显然我需要在泛型方面接受教育才能解决这个问题。我甚至不需要覆盖CollectionSubClass中的函数来返回<ElementSubClass>。我只需要在CollectionSuperClass中编写一个返回T的函数即可。简而言之,任何遇到这个问题的人都应该考虑研究泛型作为他们类架构的可能解决方案。 - Chris Redford

3

如果你确实想要创建一个包含N个引用副本的新独立集合,那么很难看出没有类似于写入时复制支持的情况下如何可能是O(1)。

你是否发现这实际上成为了代码中的性能瓶颈?并且你为什么使用Vector而不是ArrayList?


不,实际上我并不想创建一个独立的集合。在代码中,我知道原始集合包含字符串。它们之所以被强制转换为对象,是因为继承关系。如果有一种方法可以强制向量的泛型为<String>而不是<Object>,那就太完美了。由于所有对象都已知为字符串,所以表面泛型标签的这种潜在变化是我认为它可能是O(1)的原因。 - Chris Redford
@credford:你可以以包装器的方式实现List<T>,但是没有什么能阻止恶意代码向集合中添加非字符串。当然,你可以将其强制转换为Vector<String>并注释掉编译时警告,这样也能达到同样的效果... - Jon Skeet
好的。将类型转换为Vector<String>并注释掉编译时警告绝对是我没有考虑过的一种O(1)解决方案。尽管如此,我更喜欢符合语言约束的方法。此外,如果有一种需要更少键入的方法,那对我来说同样有价值(因为O(n)不是瓶颈)。 - Chris Redford

2
如果您仔细考虑一下,就会明白如果您必须转换数组的每个元素,那么最终复杂度将始终为O(n)。
由于编译器无法预先知道对象向量中的所有对象是否都是字符串,因此您无法直接转换向量,而只能逐个转换元素。

注意:原始的Vector<Object>已知包含所有字符串。阻止程序知道它们都是字符串的唯一事情是向量的通用标签为<Object>。如果有一种神奇的方法可以将标签翻转为<String>,则不需要对元素进行任何操作,因为它们已经是字符串。因此,这在技术上可以在O(1)中完成,尽管我理解如果Java的语言限制使其不可能。 - Chris Redford

1
你可以编写一个包装类,在获取元素时按需进行转换。如果列表很大,并且您希望将性能损失延迟到实际使用元素时,这可能是有意义的。另一方面,每次访问元素时都会进行转换,因此如果您将重复访问向量的元素,则可能不是一个好主意。
在现有代码中,最好使用正确的容量构造向量:
Vector<String> strings = new Vector<String>(objects.size());

如果列表很大,这可能会提高效率,因为它不必重复分配更多的内存。


1

这确实是最好的方法。

为了将 n 个元素强制转换为 String,您需要“处理”所有 n 个元素。这意味着运行时间下界必须是 O(n)

就类型而言,您已经完成了所有工作。只需将该方法放入实用类中,在需要时调用即可。


0

如果它“真的”是Vector<String>,那么一开始就应该避免使用Vector<Object>。在Java中,Object是一个错误。

你会得到编译器警告,并且有充分的理由,但你可以先将其转换为Vector,然后再转换为Vector<String>。这是O(1),但相当愚蠢,因为如果你对Vector<Object>只包含字符串的假设不正确,你最终可能会得到像String s = vector.get(1);这样的代码行,导致ClassCastException。


我只使用了Object来明确我想要的类型转换的类型。在实际代码中,使用了自定义类及其自定义超类。 - Chris Redford

0

您正在尝试使用运行时功能来删除编译时功能;泛型<String>无论如何都会被剥离。

正如其他人所指出的那样,最好找到此向量的源并将其定义为Vector<String>。这样,您将使用编译器关于将“错误”对象放入向量中的警告,而不是在程序执行期间发现是否放置了错误对象。

此代码的问题之一是,当您从Vector<Object>中获取Vector<String>时,代码可能会失败。从调试角度来看,在转换步骤失败并不是很有用。如果在插入步骤失败,则会更好,因为这样您就有机会立即对问题做出反应,而不是让它潜伏在代码的其他部分中发现。

我理解您关心打字速度;但这是一个奇怪的指标。如果一开始正确输入,不需要进行转换,通常会更快;而且,不能优化打字速度,因为这往往是以牺牲可读性、正确的代码结构、合理的设计等为代价的。如果您必须多次输入某些内容,也许是时候创建一个抽象基类,或使用其他方法来编写所需代码的一份副本。


0
如果你说 Vector 包含 Object 元素,那么它应该 只包含 Object 元素。
Vector<String> stringVector = new Vector<String>();
Vector<Object> objectVector = stringVector; // ERROR

如果您想让Vector包含任何从Object派生的内容,请使用以下语法:

Vector<String> stringVector = new Vector<String>();
Vector<? extends Object> objectVector = stringVector; // OK

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