πŸ• Reading time: 3 minutes

Today I ran into an interesting problem: extracting two lists from a collection of generic Object instances, knowing that inside I'd find objects of two distinct types (A and B) that I cared about, plus a set of objects of other types to ignore.

πŸ“Œ Solution 1

private Tuple2<List<String>, List<Car>> solution1(final List<Object> objects) {
    final List<String> strings = new ArrayList<>();
    final List<Car> cars = new ArrayList<>();
    for (final Object object : objects) {
        if (object instanceof String str) {
            strings.add(str);
        } else if (object instanceof Car car) {
            cars.add(car);
        }
    }
    return Tuples.of(strings, cars);
}

πŸ“Œ Solution 2

private Tuple2<List<String>, List<Car>> solution2(final List<Object> objects) {
    return Tuples.of(
            filterAndCast(objects, String.class),
            filterAndCast(objects, Car.class));
}

private static <T> List<T> filterAndCast(final List<?> objects, final Class<T> clazz) {
    return objects.stream().filter(clazz::isInstance).map(clazz::cast).toList();
}

βš™οΈ Efficiency, complexity and maintainability

In terms of efficiency, Solution 1 is better, as it reduces the number of iterations. However, Solution 2 offers a more elegant approach, ideal if readability is the priority and the data volume is small. In my case I went with the second solution: the list is limited in size, and I wanted to favour cleaner code that's consistent with the project's standards.