2

Given:

public abstract class Cars {}

...

public class Ford extends Cars {}

...

public class Dodge extends Cars {}

...

public class Volkswagen extends Cars {}

...

If I have two ArrayList objects:

List<Cars> dealer1 = new ArrayList<>;
List<Cars> dealer2 = new ArrayList<>;

dealer1.addAll(asList(new Ford("ford1"), new Dodge("dodge1")));
dealer2.addAll(asList(new Dodge("dodge2"), new Volkswagen("vw1")));

I then want to create a merged list from the two with only one instance of each subclass, such that:

dealerMerged = ["ford1", "dodge1", "vw1"]

OR

dealerMerged = ["ford1", "dodge2", "vw1"]

It doesn't matter which instance makes it into the merged list.

Is this possible? I had a search through and saw something about using Set but that seems to only ensure unique references, unless I've badly misunderstood something.

5
  • It's possible, but you'll never know till you try. Commented Feb 14, 2018 at 5:42
  • 2
    Set will identify duplicates and wont add elements if present, it uses equals and hashCode methods to identify duplicates. You have to override those methods in your sub classes and tell on which attribute you have to identify duplicates, otherwise it will check duplicates based on address of the objects, like you said unique references Commented Feb 14, 2018 at 5:45
  • 1
    Imagine in real life I handed you two bags. In each bag was a random assortment of fruits and vegetables. I want you to give me one bag back that has one fruit and one vegetable in it. Don't care which they are. How would you do it? Think of the steps you'd take. Then translate those steps to code. Commented Feb 14, 2018 at 5:49
  • Here is a hint: javatpoint.com/downcasting-with-instanceof-operator Commented Feb 14, 2018 at 5:59
  • not entirely related, but brands are not often modeled as subclasses, but rather as a property Commented Feb 14, 2018 at 5:59

4 Answers 4

2

Overriding equals() will work but DON'T

You can always make your collection distinctful converting it to a Set (as @Arun states in comment) or using distinct operation over the Stream of your collections. But remember those approaches use the equal() methods for that. So a quick thinking would be overriding equals() and return its Class type. But wait ! If you do so you will end up having all Dodge objects equals to each other despite they have different properties like name dodge1, dodge2. You may not only handle a single business in read world. equal() method has lots of other significances. So stay away of doing so.

If you are thinking a Java 8 way, Stateful Filter is perfect

We have a choice to use the filter operation for our concatenated stream. filter operation works pretty straight forward. It takes a predicate and decide which element to take or ignore. This a commonly used function that you will find all over the blogs that solves this problem.

public static <T> Predicate<T> distinctBy(Function<? super T, ?> keyExtractor) {
    Map<Object, Boolean> seen = new ConcurrentHashMap<>();
    return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}

Here the distinctBy function returns a predicate (that will be used in filter operation). It maintains state about what it's seen previously and returns whether the given element was seen for the first time. (You can read further explanation about this here)

You can use this Stateful Filter like

Stream.of(dealer1, dealer2)
        .flatMap(Collection::stream)
        .filter(distinctBy(Cars::getClass))
        .collect(Collectors.toList())
        .forEach(cars -> System.out.println(cars));

So What we actually did here ?

  • We concatenated the 2 ArrayList with flatmap that will give us a single stream of the merged elements (If you are new to Stream API. See this Stream Concatenation article
  • We then exploits the filter() operation that is feed with the distinctBy method which return a predicate.
  • And you see a ConcurrentHashMap is maintained to track which element satisfies the predicate or not by a boolean flag.
  • And the predicate uses the getClass() method which returns the full class name, that distinguise the elements as subclasses
  • We then can collect or iterate over the filtered list.
Sign up to request clarification or add additional context in comments.

Comments

1

Try using Map instead of List. You may please try following solution. This will let you put Car instances by their types. Thereby you will always have only one entry per class (this will be the latest entry in your map by the way).

public class CarsCollection {
    Map<Class<? extends Cars>, ? super Cars> coll = new HashMap<>();

    public <T extends Cars> void add(Class<T> cls, T obj) {
        coll.put(cls, obj);
    }
}

public class Temp {

    public static void main(String[] args)  {

        CarsCollection nos1 = new CarsCollection();

        cars.add(Ford.class, new Ford("ford1"));
        cars.add(Dodge.class, new Dodge("dodge1"));

        cars.add(Dodge.class, new Dodge("dodge2"));
        cars.add(Volkswagen.class, new Volkswagen("vw1"));

        System.out.println(cars);
    }

}

Comments

0

You could add all the element of the first list into the result list (assuming there is no duplicate in the first list) and then loop through the second list and add the elements to the resulting list only if there is no instance of the same class in the first list.

That could look something like this :

dealerMerged = dealer1;
boolean isAlreadyRepresented;
for (car2 : dealer2) {
    isAlreadyRepresented = false;
    for (car1 : dealer1) {
        if (car1.getClass().equals(car2.getClass())) {
            isAlreadyRepresented = true;
        }
    }
    if (!isAlreadyRepresented) {
        dealerMerged.add(car2);
    }
}

Comments

0

Just use class of the object as key in the map. This example with Java stream does exactly that:

List<Cars> merged = Stream.of(dealer1, dealer2)
                    .flatMap(Collection::stream)
                    .collect( Collectors.toMap( Object::getClass, Function.identity(), (c1, c2) -> c1 ) )
                    .values()
                        .stream().collect( Collectors.toList() );

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.