Java Convert List to List
I don’t know that efficiency is a concern here. You’re always going to need to iterate over every element in List
Efficient in what way — Runs fastest? Requires least amount of thinking? If «runs fastest» — why do you have TypeA and TypeB to begin with, the most efficient way could be getting rid of one and using only the other? Do you even need all items in the list?
Because there’s no setters, getters or constructor, you. can’t . convert these types. You have no way to get or put data from either object.
2 Answers 2
Answer depends on what you mean by «efficient».
// Option 1 List bList = new ArrayList<>(); for (TypeA a : aList) < bList = new TypeB(s.getA()); >// Option 2 List bList = aList.stream() .map(TypeA::getA) .map(TypeB::new) .collect(Collectors.toList()); // Option 3 List bList = aList.stream().map(a -> new TypeB(s.getA())).collect(toList());
The first performs best. That is one type of efficiency.
The second and third are single statements. That is another type of efficiency.
Efficient in what way? In terms of maintainability and clarity, I vote for object mapping libraries such as ModelMapper or MapStruct that are based on both the reflection and annotations. In case of MapStruct, you can define a mapping for the objects TypeA and TypeB and use the relevant method within the same mapping interface.
@Mapper public interface TypeA Mapper < @Mapping(target="a") TypeB typeAToTypeB(TypeA typeA) ListlistOfTypeAToListOfTypeB(List list); >
You can always use just a simple iteration using java-stream or a simple for-loop:
List listOfTypeB = listOfTypeA.stream() .map(typeA -> new TypeB(typeA.getA()) .collect(Collectors.toList());
Converting an untyped Arraylist to a typed Arraylist
Note that this is inherently unsafe, so it should only be used if getProductsList can’t be updated.
Look up type erasure with respect to Java.
Typing a collection as containing ProductListBean is a compile-time annotation on the collection, and the compiler can use this to determine if you’re doing the right thing (i.e. adding a Fruit class would not be valid).
However once compiled the collection is simply an ArrayList since the type info has been erased. Hence, ArrayList and ArrayList are the same thing, and casting (as in Matthew’s answers) will work.
The way you describe is typesafe (well not really, but it will throw a ClassCastException if you got it wrong), but probably the slowest and the most verbose way of doing this (although it is pretty clear). The example given which simply casts the the ArrayList is the fastest, but as the poster points out is not typesafe and of course you probably want to copy that to another list anyway. If you are prepared to give up your need to have this copied to an ArrayList and are happy with a List instead (which you should be), simply do:
List productList = Arrays.asList(((List)getProductsList()).toArray(new ProductListBean[] <>));
It’s fast because the underlying copying is done with System.arraycopy which is a native method, and it’s typesafe (well again not really, but it’s as safe as your example) because System.arraycopy will throw an ArrayStoreException if you try to add something that’s not of the same type.
Converting lists of one element type to a list of another type
I’m writing an adapter framework where I need to convert a list of objects from one class to another. I can iterate through the source list to do this as in Java: Best way of converting List to List However, I’m wondering if there is a way to do this on the fly when the target list is being iterated, so I don’t have to iterate through the list twice.
12 Answers 12
List original = . ; List converted = original.stream().map(Wrapper::new).collect(Collectors.toList());
assuming Wrapper class has a constructor accepting a String .
My answer to that question applies to your case:
import com.google.common.collect.Lists; import com.google.common.base.Functions List integers = Arrays.asList(1, 2, 3, 4); List strings = Lists.transform(integers, Functions.toStringFunction());
The transformed list is a view on the original collection, so the transformation happens when the destination List is accessed.
As an alternative to the iterator pattern, you can use a abstract generic mapper class, and only override the transform method:
- create a generic collection mapper for any data type
- [optional] create a library of methods that transform between different data types (and override the method)
- use that library
// Generic class to transform collections public abstract class CollectionTransformer < abstract F transform(E e); public Listtransform(List list) < ListnewList = new ArrayList(); for (E e : list) < newList.add(transform(e)); >return newList; > > // Method that transform Integer to String // this override the transform method to specify the transformation public static List mapIntegerToStringCollection(List list) < CollectionTransformer transformer = new CollectionTransformer() < @Override String transform(Integer e) < return e.toString(); >>; return transformer.transform(list); > // Example Usage List integers = Arrays.asList(1,2); List strings = mapIntegerToStringCollection(integers);
This would be useful is you have to use transformations every time, encapsulating the process. So you can make a library of collection mappers very easy.
How do I convert from List to List in Java using generics?
Note that the above code does not generate any type-safety warnings and, ideally, your solution shouldn’t generate any such warnings, either. Will the following solution work provided that list Class
public class ListUtil < public static > L typedList(List untypedList, Class itemClass, Class listClass) < L list = null; try < list = listClass.newInstance(); >catch (InstantiationException e) < >catch (IllegalAccessException e) < >for (Object item: untypedList) list.add(itemClass.cast(item)); return list; > >
- T is the type of each item in the resulting list.
- L is the type of the list that I wish to create (which extends List ).
- untypedList is the «untyped» input list, effectively the same as List .
- itemClass represents the runtime class of T .
- listClass represents the runtime class of L .
9 Answers 9
I would use Guava and its Iterables.filter(Iterable,Class) method along with a factory method from the Lists class, like so:
List original = . ; List typed = Lists.newArrayList( Iterables.filter(original, String.class));
This will actually check each object in the original list and the resulting list will contain only those elements that are instances of the given type ( String in this case) or a subtype of it. I really don’t think it makes sense to have users provide a Class for the resulting List type and try to instantiate it via reflection.
There is no unchecked conversion, since Iterables.filter(Iterable,Class) returns an Iterable
Good stuff! Guava is clearly a well-designed library. For the moment, though, I’d prefer an approach that doesn’t require the use of an external library, but I’ll certainly propose to my team that we consider adopting Guava to deal with generic collections.
Guava has a lot of great stuff, collections-related code being only one part. I definitely highly recommend it!
Keep in mind that this solution will throw away items which are not a String (this may be exactly what you want, but it looks like the intent behind the original question is to throw a ClassCastException). If the method which provides the original list is not behaving as expected (let’s say you expect a List
Rather than passing in the type of the list you want to instantiate, why not just pass in the empty Collection that you want populated? This gives the users of your api much more flexibility, as using the default constructor is not always ideal. (for example, maybe I want a Set where I provide the expected number of elements, or I want a sorted list where I provide the Comparator).
Also, as a side note, you should always program to the most generic interface possible. In this case, your input need be nothing more specific than an Iterable, and your output a Collection.
Given this, I would write the method this way —
public static > C typesafeAdd(Iterable from, C to, Class listClass) < for (Object item: from) < to.add(listClass.cast(item)); >return to; >
then the calling code looks like:
public static void main(String[] args) < ListuntypedStringList = LegacyApi.getStringList(); List typesafeStringList = typesafeAdd(untypedStringList, new ArrayList(), String.class); >
- If you can really trust LegacyApi (or whatever provided you the untyped List) to only return to you a collection with the expected type in it, then you can just do an unchecked cast and suppress it. This should be localized to the smallest scope possible. ie: create something like TypesafeLegacyApiWrapper which delegates calls to LegacyApi.
- This method signature still breaks down if you have anything more complicated. For example if you have a List> this method does not work.