3

I have two clases:

public class A {
   //attibutes and methods
}

public class B extends A {
   //atributes and methods
}

Now I have a service that returns a List with elements of type A. Lets call it generateAElements();

I want to call that method, filter the List obtained to only keep the elements of type B, which also are of type A.

List<A> listA = generateAElements();
List<A> listAA = listA.filter( p -> p instanceof B).collect(Collectors.toList());
List<B> listB = new ArrayList<>();
// need to create a new list, iterate overListA and add elements of type B?
for (A itemA : listA) {
    listB.add((B) itemA);       
}

Is there an efficient way to do this?

Important: The list may contain a large number of elements.

Alexis C.
  • 91,686
  • 21
  • 171
  • 177
melli-182
  • 1,216
  • 3
  • 16
  • 29

2 Answers2

9

Assuming you're using Java 8+, you could use streams:

List<B> listB = listA
    .stream()
    .filter(B.class::isInstance)
    .map(B.class::cast)
    .collect(Collectors.toList());

Of course, this is doable in earlier Java versions, it's just likely to be more verbose.


A note on terminology: This is not "casting from List<A> to List<B>" - the only things being cast here are individual entries.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
1

Stream free version. You just need to do a compatibility check before casting and you are good to do whatever you want.

List<A> listA = generateAElements();
List<B> listB = new LinkedList<>();
for (A item : listA) {
    // Skip non compatable items
    if(item instanceof B) 
        listB.add((B) item);       
}

The key difference from the OP's code is the line if(item instanceof B), that does the filter/error-checking the OP needed, but didn't do. (This is technically the exact same thing the stream version is doing, but slightly more verbose and much less overhead)

Tezra
  • 8,463
  • 3
  • 31
  • 68
  • @PM77-1 The key difference is the line `if(item instanceof B)`, that does the filter/error-checking the OP needed, but didn't do. I also use a LinkList instead of ArrayList for performance reasons (faster inserts). – Tezra Jun 05 '17 at 19:04
  • 2
    Tezra, have you checked that insertions are faster in `LinkedList` than in `ArrayList`? Every time I tested it, `LinkedList` was slower, even for `add`... (BTW, I haven't downvoted your answer) – fps Jun 05 '17 at 19:09
  • @FedericoPeraltaSchaffner ArrayList is faster if you know the size ahead of time, as it can jump to tail index and write. If you exceed ArrayList's buffer (OP said he has large dataset), than you will start to lost to LinkeList as ArrayList has to destroy and reconstruct itself every time you exceed it. – Tezra Jun 05 '17 at 19:53
  • 2
    That's still O(1) amortized, which probably wins out compared to LinkedList's memory fragmentation. – Oliver Charlesworth Jun 05 '17 at 19:53
  • 1
    In my experience, `ArrayList` is *always* faster, no matter the size of the list or if you know the size of the list in advance. Claiming that `X` is faster than `Y` if `Z` condition is always met is a myth. You always have to benchmark for your specific use case with real data in your production hardware. Then, with the evidence at your sight, you can safely come to a conclusion. Just my 2 cents. – fps Jun 05 '17 at 20:09
  • Most of the time it just depends on how you will use the list. Most cases will be negligible, and it's just a matter of preference. If it is an issue, you can make an intelligent optimization based on where your issues start arising. (And if you really need that optimization, you definitely don't want to use streams) – Tezra Jun 05 '17 at 20:20
  • Tezra, I totally agree with you on that. Streams are nice, but they come to a price. If you need performance, go the old imperative way. – fps Jun 05 '17 at 20:59