过滤器列表包含多个对象 java

时间:2021-05-05 13:01:27

标签: java java-stream

下面的代码现在对我来说很好用,但它不是未来的证明,因为 if else 语句和 instanceof 的数量。我想用更多的对象(如自行车、马达等)来扩展运输列表……但每次添加新对象时,我都需要添加更多的 if else 语句并创建更多的 instanceof。有没有人有更好的主意或更好的解决方案?

private static Transport filterObjects(List<Transport> listOfTransport, int refNr) {
    List<Transport> cars = listOfTransport.stream()
                                          .filter(transport -> transport instanceof Cars)
                                          .collect(Collectors.toList());
    
    List<Transport> airPlanes = listOfTransport.stream()
                                               .filter(transport -> transport instanceof Airplanes)
                                               .collect(Collectors.toList());
   
    if (!cars.isEmpty()){
        return cars.get(refNr);
    } else if (!airPlanes.isEmpty()) {
       return airPlanes.get(refNr);
    } else {
      return null;
    }
}

4 个答案:

答案 0 :(得分:2)

传入你想要的子类型。也许这会奏效:

private static Transport filterObjects(List<Transport> listOfTransport, Class clazz, int refNr) {
    List<Transport> transports = listOfTransport.stream().filter(clazz::isInstance).collect(Collectors.toList());
        
    return !transports.isEmpty() ? transports.get(refNr) : null;
}

答案 1 :(得分:2)

正如您目前优先考虑汽车而不是飞机一样,随着您的交通工具类型的增长,您也需要某种优先权来优先返回。您可以使用枚举解决此问题。添加新的传输类型后,您只需相应地扩展您的枚举。枚举可能类似于:

enum Priority{
    Car(1), 
    Airplane(2);
    
    private int value;
    Priority (int value) {
        this.value = value;
    }
    public int getValue() {
        return value;
    }
}

然后您可以重构您的方法,方法是将列表中的元素按简单的类名分组,并使用您在枚举中定义的优先级将它们添加到已排序的映射中。然后,您可以使用映射的第一个条目来确定返回值。示例:

private static Transport filterObjects(List<Transport> listOfTransport, int refNr) {
    Comparator<String> comp = Comparator.comparingInt(e -> Priority.valueOf(e).getValue());
    
    List<Transport> result = 
            listOfTransport.stream()
                    .collect(Collectors.groupingBy(
                                        e -> e.getClass().getSimpleName(), 
                                        () -> new TreeMap<>(comp), 
                                        Collectors.toList()))
                   .firstEntry().getValue();
     
     return (result != null && 0 <= refNr && refNr < result.size()) ? 
            result.get(refNr) : null;        
}

答案 2 :(得分:0)

首先根据子类型将列表元素分组到一个映射中,然后创建一个传输子类型列表。迭代这个列表,然后检查映射中是否存在相应的条目:


private static final List<Class> subTypes = List.of(Cars.class, Airplanes.class);

private static Transport filterObjects(List<Transport> listOfTransport, int refNr) {
    Map<Class, List<Transport>> map = listOfTransport.stream()
                                                     .collect(Collectors.groupingBy(t -> t.getClass()));
    
    Optional<List<Transport>> op = subTypes.stream()
                                           .filter(map::containsKey)
                                           .findFirst();
    if(op.isPresent()) {
        return op.get().get(refNr); // This could cause IndexOutOfBoundsException
    }else{    
      return null;
    }
}

答案 3 :(得分:0)

好吧,您可以执行以下操作。

首先,定义您的订单:

static final List<Class<? extends Transport>> ORDER = List.of(
    Car.class,
    Airplane.class
);

然后,您可以编写以下方法:

private static Transport filterObjects(List<Transport> listOfTransport, int refNr) {
    Map<Class<? extends Transport>, Transport> map = listOfTransport.stream()
        .collect(Collectors.groupingBy(Transport::getClass, Collectors.collectingAndThen(Collectors.toList(), list -> list.get(refNr))));

    return ORDER.stream()
        .filter(map::containsKey)
        .map(map::get)
        .findFirst()
        .orElse(null);
}

这样做是将每个不同的 Class 映射到作为相应类的子类型的第 refNr 个元素。

然后它遍历 ORDER 并检查是否在原始 listOfTransport 中找到了一个元素。如果 listOfTransport 不包含特定类的任何元素,该键将不存在于映射中。


请注意,如果地图中存在特定类的任何元素,则假定该类的元素数量至少为 refNr,否则为 IndexOutOfBoundsException被抛出。换言之,每次传输必须在 refNr 内出现 0 次或至少 listOfTransport 次。

另请注意,getClass() 不一定会产生与 instanceof 相同的结果。但是,我在这里假设每个传输都没有进一步的子类。