6

I want to remove all entries from a LinkedHashMap that were added after an entry with a given key.

My first try was:

LinkedHashMap<String, SomeObject> var = new LinkedHashMap<String, SomeObject>();

public void removeEntriesAfter(String key) {
  boolean deleteEntries = false;
  for (String currentKey : var.keySet()) {
    if(deleteEntries) {
      var.remove(currentKey);
    } else {
      if(key.equalsIgnoreCase(currentKey)) {
        // Do not remove the current entry
        deleteEntries = true;
      }
    }
  }
}

But then I received a java.util.ConcurrentModificationException.

My second idea was to first determine the keys, an remove them afterwards.

public void removeEntriesAfter(String key) {
  boolean deleteEntries = false;
  List<String> listOfEntriesToBeRemoved = new ArrayList<String>();

  // Determine entries to be deleted
  for (String currentKey : var.keySet()) {
    if(deleteEntries) {
      listOfEntriesToBeRemoved.add(currentKey);
    } else {
      if(key.equalsIgnoreCase(currentKey)) {
        // Do not remove the current entry
        deleteEntries = true;
      }
    }
  }

  // Removed selected entries
  for (String currentKey : listOfEntriesToBeRemoved) {
    var.remove(currentKey);
  }
}

That works, but I'm sure there is a more elegant/efficient way of doing this.

Edward
  • 4,453
  • 8
  • 44
  • 82
  • 4
    Get the key iterator through `m.keySet().iterator()`. Call `next()` until you reach `key`. After that call `remove()`/`next()` until `hasNext()` returns false. – aioobe Apr 05 '16 at 15:35

1 Answers1

8

To avoid a ConcurrentModificationException you can use an Iterator.

Iterator<String> it = map.keySet().iterator();
while (it.hasNext())
    if (it.next().equalsIgnoreCase(currentKey))
        break;
while (it.hasNext()) {
    it.next();
    it.remove();
}

If you wanted the most efficient solution, it would be to go straight to the appropriate entry in the first place. To do this you would have to only ever put lower case keys into the map (rather than putting any old strings and comparing using equalsIgnoreCase). Then, using reflection, you could access the Map.Entry object corresponding to currentKey.toLowerCase() and then, using reflection again, you could follow the links all the way through the map. None of this is possible without reflection because neither the entry corresponding to a key nor the links between entries are exposed through public API. I do not recommend reflection as your code could easily break in the future if the code for LinkedHashMap is changed.

Paul Boddington
  • 37,127
  • 10
  • 65
  • 116
  • Interesting, I thought that `it.remove();` removes from the `Iterator` and not from the `LinkedHashMap`. It's a bit confusing, but seems to work. – Edward Apr 05 '16 at 15:52