Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 separate list elements by class

given I have a list of items that all inherit from the same base class:

class Item {
    protected String name;

    public Item(String name) {

        this.name = name;
  }

  getName() {

      return name;
  }
}

class ItemA extends Item {...}
class ItemB extends Item {...}

List<Item> itemList = Arrays.asList(new ItemA("itemA"), new ItemB("itemB"));

In my case I have no control over how these classes are implemented but I need to separate the list into two different lists containing the name of the respective element.

Here is the first try at a solution that contains lots of if and instanceof statements:

List<String> itemAList = new ArrayList<>();
List<String> itemBList = new ArrayList<>();

itemList.forEach(item -> {

    if(item instanceof ItemA) {

        itemAList.add(item.getName());
    }        
    else if(item instanceof ItemB) {

        itemBList.add(item.getName());
    }
});

So this works but I gave it some thought on how to avoid the if statements. Since I'm using Java 8 I can do this:

List<String> itemAList = itemList.stream()
    .filter(ItemA.class::isInstance)
    .map(item -> item.getName())
    .collect(Collectors.toList());

List<String> itemBList = itemList.stream()
    .filter(ItemB.class::isInstance)
    .map(item -> item.getName())
    .collect(Collectors.toList());

This works as well but it means I have to process the list two times.

As I said I have no bearing on the implementation of the Item classes so what would be the best way to implement such a behavior?

Greetings

[edit:] Thank you for all the responses. I've learned something new today.

like image 546
Scyla101 Avatar asked Jan 01 '23 03:01

Scyla101


2 Answers

You can group the content based on the class of the elements :

Map<Class, List<String>> grouped = itemList.stream()
        .collect(Collectors.groupingBy(Item::getClass,
                Collectors.mapping(Item::getName, Collectors.toList())));

and access it as:

List<String> itemAList = grouped.get(ItemA.class);
like image 126
Naman Avatar answered Jan 13 '23 17:01

Naman


As you said you have different instances you have no control over, so you can't really be sure that only ItemA and ItemB occur in the list.

You're better of using a map, which has the name as the key and a List of Items as values:

Map<String, List<Item>> map = itemList.stream()
    .collect(Collectors.groupingBy(Item::getName, Function.identity()));
like image 35
Lino Avatar answered Jan 13 '23 17:01

Lino