Home > Software design >  Deleting items the Lists contained within a HashMap
Deleting items the Lists contained within a HashMap

Time:03-17

I have a Hashmap<Long, List\<Foo>> myMap. I want to remove an item from any List<Foo> where the Foo.key equals value assign to the key variable.

Is there a good way to do this with streams? I am using the following code to delete the item, which doesn't seem the best to me:

for (List<Foo> l : myMap.values()) {
   for (Foo f : l) {
      if (f.getKey().equals(key)) {
         l.remove(f);
         break;
      }
   }
}

CodePudding user response:

Probably there are multiple ways to do this. Here is one:

myMap.values().forEach(list->list.removeIf(foo -> Objects.equals(foo.getKey(), key)));

The idea is to go over each list in the map and remove the elements that have the key you want to remove.

CodePudding user response:

The code you have is actually the most suitable tool for this task.

Since you need to modify the contents of the map, it means that you need a stream that will cause side effects. And that is discouraged by the documentation of the Stream API.

But you can enhance your code by using removeIf() method added to the Collection interface with Java-8.

public static void removeFooWithKey(Map<Long, List<Foo>> myMap, 
                               String key) {
    
    for (List<Foo> foos : myMap.values()) {
        foos.removeIf(foo -> foo.getKey().equals(key));
    }
}

CodePudding user response:

The simplest way is:

myMap.values().forEach(list -> list.removeIf(foo -> foo.getKey().equals(key)))

But removeIf removes all of the elements of this collection that satisfy the given predicate. It does not equal to your initial code. Probably if your collection consists of unique keys and you need to remove only one object removeIf will do a little overhead and compare all elements of colections.

Solution 1. Remove by first matched index

myMap.values().forEach(list -> IntStream.range(0, list.size()).filter(index -> key.equals(list.get(index).getKey())).findFirst().ifPresent(list::remove))

Solution 2. Own removeFirstIf method

myMap.values().forEach(list -> StreamUtils.removeFirst(list, foo -> key.equals(foo.getKey())));

public class StreamUtils {
    private StreamUtils() {

    }
    public static <T> T removeFirstIf(Iterable<? extends T> collection, Predicate<? super T> condition) {
        Objects.requireNonNull(collection);
        Objects.requireNonNull(condition);

        T value;

        final Iterator<? extends T> iterator = collection.iterator();
        while (iterator.hasNext()) {
            value = iterator.next();
            if (condition.test(value)) {
                iterator.remove();
                return value;
            }
        }

        return null;
    }
}

Solution 3. Change your structure to Map<Long, Map<String, Foo>>
Assumption: probably if your List consists of unique keys then you can change your structure from Map<Long, List<Foo>> to Map<Long, Map<String, Foo>>
Removing elements by Key will be simple and fast:

Map<Long, Map<String, Foo>> myMapOfMaps = new HashMap<>();
myMapOfMaps.values().forEach(map -> map.remove(key));
  • Related