views:

101

answers:

7

Is there a more efficient way (preferably O(1) rather than O(n) but at least faster to type) to typecast the elements of a vector than this?

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

Note

To anyone who is running into an apparent need to typecast a Vector or other Generic class: as the accepted answerer points out, this is probably a code smell and you probably need to refactor the code in your class hierarchy.

Specifically, if you haven't already, you should consider making the classes that use said Vector or other Generic class use Generics themselves. When I did this in my own code, I completely eliminated the need for the function in my code that was analogous to the above function.

If you've never implemented Generics in your own code, check the "Generics" link above. You may be surprised (as I was) to find that they can be used to implement precisely the functionality you thought you needed from a Vector typecast.

+2  A: 

If you're definitely trying to create a new independent collection which contains a copy of N references, it's hard to see how that could possibly be O(1) without something like copy-on-write support.

Have you found this to actually be a performance bottleneck in your code? And is there any reason why you're using Vector instead of ArrayList?

Jon Skeet
No, I am not trying to create an independent collection, actually. In the code, I know that the original collection contains Strings. They are only casted as Objects because of inheritance. If there was a way to force the generic of the vector to be <String> instead of <Object>, that would be perfect. The potential for that kind of change of the superficial generic label (since the Objects are all known to be Strings) is why I thought it might be O(1).
Chris Redford
@credford: You could implement `List<T>` in a wrapper sense - but there'd be nothing to stop malicious code from adding a non-string to the collection. Of course, you could just cast to `Vector<String>` and annotate away the compile-time warning, which would accomplish much the same thing...
Jon Skeet
Okay. Casting to `Vector<String>` and annotating away the compile-time warning is definitely one O(1) solution I didn't think of. Though, I would prefer a method that was within the constraints of the language. Also, if there were a method that required **less typing**, that would be equally valuable to me (as the O(n) is _not_ a bottleneck).
Chris Redford
+2  A: 

If you think a bit about it, you will unsderstand that if you have to convert every element of the array that you will always end up with O(n).

Since the compiler cannot know in advance if all objects in the object vector are all strings, you cannot cast the vector directly, but only element by element.

codymanix
Note: The original Vector<Object> is known to contain all Strings. The only thing blocking the program from knowing they are all Strings is the fact that the generic label of the Vector is <Object>. If there was a way to magically flip the label to be <String>, nothing would need to be done to the elements since they are already Strings. Thus, this could technically be done in O(1), though I understand if Java's language restraints make it impossible.
Chris Redford
+1  A: 

That is indeed the best way to do it.

In order to cast n elements to a String, you will need to 'process' all n elements. That implies that the lower bound on the running time is going to have to be O(n).

As far as typing goes, you have already done all of the work. Just put that method in a utility class and call it when you need to.

jjnguy
+1  A: 

You could write a wrapper class that does the casting on-demand when an element is fetched. This might make sense if the list is large and you want to delay the performance penalty until an element is actually used. On the other hand it would cast each element every time it is accessed, so if you will be accessing elements of the Vector repeatedly, this might be a bad idea.

In your existing code, you might as well construct the Vector with the correct capacity:

Vector<String> strings = new Vector<String>(objects.size());

That might improve the efficiency since it won't have to repeatedly allocate more memory if your list is large.

Dave Costa
A: 

You should avoid having a Vector<Object> in the first place if it's 'really' a Vector<String>. Object is a mistake in Java.

You'll get a compiler warning, and with good reason, but you can take the route of casting to a Vector first and then to a Vector<String>. That's O(1), and pretty stupid, as you can later end up with a line of code like String s = vector.get(1); giving a ClassCastException, if you're incorrect about the Vector<Object> only ever containing Strings.

Ricky Clarkson
I only used Object to make the type of typecast I wanted clear. In the actual code a custom class and its custom superclass are used.
Chris Redford
+4  A: 

If the only thing you want to do is to cast from Vector<Object> to Vector<String>, that you can do. You'll have to be sure every object in your vector is a String though!

Obviously, this won't work:

    Vector<Object> objectVector = new Vector<Object>();
    Vector<String> stringVector = (Vector<String>)objectVector;

But you can do this:

    Vector<Object> objectVector = new Vector<Object>();
    Vector typelessVector = objectVector;
    Vector<String> stringVector = (Vector<String>)typelessVector;

You'll get some warnings, but the code should work fine.

As was mentioned before, this does feel like a code smell.

Joeri Hendrickx
+1. Beat me by a minute with this answer :o
mlaw
Thanks. I wish there was a method that didn't produce warnings, though. To me, this seems like no less of a risky operation than typecasting a non-collection, so I don't understand why they don't allow something like your first block of code above, without warnings.
Chris Redford
Thanks for the comment about the code smell. I actually remedied this whole situation by making the `CollectionSuperClass` generic. I then made the function in question return `<T extends ElementSuperClass>` in the `CollectionSuperClass` and then overrode it to return `<ElementSubClass>` in the `CollectionSubClass extends CollectionSuperClass<ElementSubClass>` (note: different domain-specific "collection" classes and element classes were used in the actual code).
Chris Redford
Well.. okay. Obviously I was due for education in Generics for this whole problem. I didn't even have to override the function in `CollectionSubClass` to return `<ElementSubClass>`. I just had to make a function that returned `T` in `CollectionSuperClass`. In short, anyone encountering this problem should consider looking into Generics as a possible solution to their class architecture.
Chris Redford
A: 

You are trying to use a run time feature to remove a compile time feature; the generics <String> is getting stripped anyway.

As others have noted, it is far better to find the source of this Vector and defined it to be a Vector<String>. That way you will use your compiler's warnings about putting the "wrong" object into the Vector instead of finding out if something bad was placed in there during program execution.

One of the problems this code has is that when you are grabbing your Vector<String> from your Vector<Object>, the code might fail. From a debugging perspective, failing at a conversion step isn't very useful. It would be far better to fail at the insertion step, because then you would have the opportunity to react immediately to the issue, instead of letting it lie latent for other parts of the code to discover.

I understand that you are concerned with typing speed; but that's an odd metric. It is always faster to not convert something because it is typed correctly in the first place; and, typing speed should not be optimized because it is usually done so at the expense of readability, proper code structure, reasonable design, etc. If you have to type something a bunch of times, perhaps it is time to make an abstract base class, or use some other means to allow the writing of one copy of the required code.

Edwin Buck