Java 8单独的列表元素类别

时间:2019-03-22 13:55:18

标签: java lambda java-8 java-stream

鉴于我有一个列表,这些列表都继承自同一基类:

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"));

就我而言,我无法控制这些类的实现方式,但是我需要将列表分为两个包含相应元素名称的不同列表。

这是首次尝试包含许多ifinstanceof语句的解决方案:

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());
    }
});

所以这可行,但是我对如何避免使用if语句进行了思考。由于我使用的是Java 8,所以我可以这样做:

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());

这也可以,但是这意味着我必须处理两次列表。

正如我所说,我与Item类的实现无关,那么实现这种行为的最佳方法是什么?

问候

[edit:]谢谢您的所有答复。我今天学到了一些新东西。

3 个答案:

答案 0 :(得分:5)

您可以根据元素的类别对内容进行分组:

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

并以以下方式访问它:

List<String> itemAList = grouped.get(ItemA.class);

答案 1 :(得分:2)

正如您所说的,您无法控制其他实例,因此您不能真正确定列表中仅出现ItemAItemB

最好使用以名称为键,以List中的Item作为值的地图:

Map<String, List<Item>> map = itemList.stream()
    .collect(Collectors.groupingBy(Item::getName, Function.identity()));

答案 2 :(得分:2)

您也可以:

Map<Boolean, List<Item>> classMap = itemList.stream()
                .collect(Collectors.partitioningBy(ItemA.class::isInstance));

输出

 {
    false=[ItemB [getClass()=class stackoverflow.ItemB]],
    true =[ItemA [getClass()=class stackoverflow.ItemA]]
 }