views:

284

answers:

5

This is a Java question.

What is the fastest way to convert a List<?> to a List<ObjectType>? I am aware that this is possible through iteration please exclude that option.

Example by iteration,

final List<ObjectType> targetList = new ArrayList<ObjectType>();
// API returns List<?> so I have no choice.
List<?> resultList = resultSet.getResults(); 

// This is slow. Average list size is 500,000 elements.
while (resultList != null && !resultList.isEmpty()) {
    for (final Object object : resultList) {
        final ObjectType objectType = (ObjectType) object;
        targetList.add(objectType);
    }
    resultSet = someService.getNext(resultSet);
    resultList = resultSet.getList();
}

Thanks.

+1  A: 

Umm... if you are really dealing with 500,000 elements and expreriencing a performance issue, I'd bite the bullet (the bullet being compiler's "unchecked" warning) and cast it:

List<ObjectType> targetList = new ArrayList<ObjectType>((List<ObjectType>) resultList);

You can then use @SuppressWarnings("unchecked") annotation to suppress the warning.

The above is, of course, assuming that resultList does not satisfy you in some way (perhaps it's unmodifiable or array-backed or what have you). Otherwise you can simply cast it.

ChssPly76
+4  A: 

It's a bit scary but depending on context you could possibly get away with just casting:

final List<ObjectType> targetList = (List<ObjectType>)problemSolver.getResults();
Tom
I would go with that solution too, for this problem.
Ravi Wallau
In general, this is not type-safe.
Derek Mahar
That's correct but given the API being consumed it can't be.
Tom
+1  A: 

The correct solution depends on whether you want to ensure that the elements you put into targetList really are instances of ObjectType (or a subtype).

If you don't care, one of the solutions with an unsafe typecast will do. Unless you need to copy, @Toms solution is better. (You might need to copy if getResults() returns a linked list and your algorithm needs to use targetList.get(int) a lot.) But beware that you might get an unexpected ClassCastException later on if your assumptions are incorrect.

If you need to be sure that there are no elements of the wrong type, then you have to use an iterator-based solution unless you know what kind of List type that getResults() gives you. (If you can cast the result of getResults() to ArrayList, then indexing using get(int) should be faster than using an Iterator. On the other hand, if the result is a LinkedList, then using get(int) to copy the list is O(N**2)!!)

How about this?

final List<?> resultList = problemSolver.getResults(); 

List<ObjectType> targetList;
if (resultList == null || resultList.isEmpty()) {
    targetList = Collections.empyList();
} else {
    int len = resultList.size();
    // it is important to preallocate the ArrayList with the
    // right size ... to conserve space and avoid copy on realloc
    // of the list's private backing array.
    targetList = new ArrayList<ObjectType>(len);
    if (resultList instanceof ArrayList) {
        // Copy using indexing - O(N) for ArrayLists
        ArrayList<?> arrayList = (ArrayList) resultList;
        for (int i = 0; i < len; i++) {
            targetList.add((ObjectType) (arrayList.get(i)));
        }
    } else {
        // Copy using iterator - O(N) for all sane List implementations,
        // but with a larger C than the best case for indexing.
        for (Object obj : resultList) {
            targetList.add((ObjectType) obj);
        }
    }
}
Stephen C
A: 

What's wrong with iteration?

But as you wish:

final List<?> src = ...;
final int len = src.size();
final SomeType[] dstArray = new SomeType[len];
src.<SomeType>toArray(dstArray);
final List<SomeType> dst = java.util.Arrays.asList(dstArray);

Or:

(Disclaimer: Not so much as compiled.)

I prefer the iteration method.

Tom Hawtin - tackline
Umm ... doesn't `src.toArray()` create a copy? So you end up double copying.
Stephen C
Yes, although that should not be a huge issue in the scheme of things (still better than `LinkedList`!). I forgot the `toArray` with argument has strange bounds.
Tom Hawtin - tackline
+2  A: 

As Tom said above, if you know for a fact that everything in the original List<?> is an ObjectType -- as implied by the iteration code -- casting to List<ObjectType> will work just fine.

The only difference from the iteration code is that, if anything's not an ObjectType, in the iteration code the ClassCastException will happen when you're populating targetList, while with the straight cast, it'll happen when you're getting the values out. E.g.:

public static void main(String[] args) {
 List<?> list = Arrays.<Object> asList('I', "am", "not", "homogeneous");
 List<Character> targetList = (List<Character>) list;

 int index = 0;
 try {
  for (Character c : targetList) {
   System.out.println(c);
   index++;
  }
 } finally {
  System.out.println("Reached " + index);
 }
}

prints

I
Exception in thread "main" java.lang.ClassCastException: java.lang.String 
cannot be cast to java.lang.Character
    at Foo.main(Foo.java:100)
Reached 1
David Moles