Reputation: 81
The code below works fine for me now, but it is not future proof, becuase the numbers of if else
statments and instanceof
. I would like to extend the Transport list with more objects like bicyles, motors etc.... but every time when I add new object I need to add more if else
statements and create more instanceof
. Does anyone have a better idea or better solution?
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;
}
}
Upvotes: 0
Views: 712
Reputation: 22977
Well, you could do the following.
First, define your order:
static final List<Class<? extends Transport>> ORDER = List.of(
Car.class,
Airplane.class
);
Then, you could write the following method:
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);
}
What this does, is mapping each distinct Class
to the refNr
th element which is a subtype of the respective class.
Then it walks over ORDER
and checks if an element has been found within the original listOfTransport
. The key won't exist in the map if listOfTransport
does not contain any element of the particular class.
Note that if any element of a particular class exists in the map, the number of elements of that class is assumed to be at least refNr
, otherwise an IndexOutOfBoundsException
is thrown. With other words, each transport must occur 0 or at least refNr
times within the listOfTransport
.
Also note that getClass()
does not necessarily yield the same result as instanceof
. However, I have assumed here that each respective transport does not have further subclasses.
Upvotes: 0
Reputation: 16498
Just as you currently prioritize cars over planes, as your transport types grow you also need some kind of priority on which to return preferentially. You can solve this with an enum. You only need to expand your enum accordingly as soon as you add a new transport type. The enum could look something like:
enum Priority{
Car(1),
Airplane(2);
private int value;
Priority (int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
Then you can refactor your method by grouping the elements of your list by their simple class names and adding them to a sorted map using the priority you define in your enum. You can then use the first entry of the map to determine the return value. Example:
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;
}
Upvotes: 1
Reputation: 4935
First group the list elements into a map based on subtype, then create a list of subtypes of transport. Iterate this list and then check if corresponding entry exists in the map:
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;
}
}
Upvotes: 0
Reputation: 308743
Pass in the subtype you want. Maybe this would work:
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;
}
Upvotes: 1