Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java - remove objects in a collection that are in another collection, with an arbitrary meaning of "equals"

This is very easy if I just want to base this on absolute equality. I'd just do:

collectionA.removeAll(collectionB).

However, let's say I have this object:

class Item {
   private String color;
   private String name;
   private String type;
}

And two collections...

List<Item> items1, List<item> items2.

...but I just want to remove everything from item1 that has the same name and type as something in item2.

Note that I can't subclass or define equals, hashcode for this class.

I'd want this to be the same complexity of the existing collections.removeAll method.

The best solution I can think of would be something like:

class SimpleItem {
  String name;
  String type;
  Item item;

  public SimpleItem(Item item) {
    this.name = item.getName();
    this.type = item.getType();
  }

  @Override
  public boolean equals(Object obj) {
    ...
  }

  @Override
  public int hashCode() {
    ...
  }
}

Set<SimpleItem> simpleItems1 = ...;
for (Item item : items1) {
  simpleItems1.add(new SimpleItem(item));
}

Set<SimpleItem> simpleItems2 = ...;
for (Item item : items2) {
  simpleItems2.add(new SimpleItem(item));
}

simpleItems1.removeAll(simpleItems2);

Set<Item> items = ...;
for (SimpleItem simpleItem : simpleItems) {
  items.add(simpleItem.item);
}

...but that is insanely verbose. It's Java 8. What clever solution am I missing?

like image 368
Jeremy Avatar asked Oct 22 '25 19:10

Jeremy


1 Answers

You mention that it's Java 8. In that case you have a very simple and straightforward way to achieve this:

list1.removeIf(item1 -> list2.stream().anyMatch(item2 -> customEquals(item1, item2));

If your customEquals method is a member of Item you could use a method reference to make it a bit neater:

list1.removeIf(item -> list2.stream().anyMatch(item::customEquals));

In your case you could put your condition directly into the statement rather than creating a separate method:

list1.removeIf(item1 -> list2.stream().anyMatch(item2 ->
    item1.getName().equals(item2.getName()) && item1.getType().equals(item2.getType())));

Conveniently removeIf is a default member of the Collection interface so any class that implements Collection should support it as long as the implementation's iterator supports remove.

like image 145
sprinter Avatar answered Oct 25 '25 09:10

sprinter



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!