0

I have a multimap called clientPrinterConnections that maps printer names to SocketConnections in a many to many manner. When I close a SocketConnection, I want to remove every entry that uses it. I thought Multimap implements ConcurrentMap (it seems like it did in 8.0.0 but not 9.0.0) so I did it the easy way.

    for (Map.Entry<String, List<SocketConnection>> e: clientPrinterConnections.entrySet()) {
        if (e.getValue().contains(connection)) {
            clientPrinterConnections.removeValue(e.getKey(),connection);
        }
    }

It ends up that doesn't work, and it throws a ConcurrentModificationException. I can't really use an iterator though due to needing a removeValue call.

    Iterator iter = clientPrinterConnections.entrySet().iterator();
    while(iter.hasNext()) {
        Map.Entry<String, List<SocketConnection>> pair = (Map.Entry)iter.next();
        if (pair.getValue().contains(connection)) {
            //this deletes all connections to the specific printer
            iter.remove();
            //this throws the same exception as before
            clientPrinterConnections.removeValue(pair.getKey(),connection);
        }
    }

I can just compile a list of things to delete like this,

    ArrayList<String> itemsToDelete = new ArrayList();
    for (Map.Entry<String, List<SocketConnection>> e: clientPrinterConnections.entrySet()) {
        if (e.getValue().contains(connection)) {
            itemsToDelete.add(e.getKey());
        }
    }
    for (String s: itemsToDelete){
        clientPrinterConnections.removeValue(s,connection);
    }

but that seems messy. Is there a more elegant way to do this? I can imagine some situations where just compiling a list wouldn't be an adequate fix.

Kyle Berezin
  • 597
  • 4
  • 20
  • Note that `ConcurrentModificationException` has nothing to do with concurrency, i.e. using a concurrent map wouldn't remove the exception. – Kayaman Aug 10 '17 at 07:25

2 Answers2

1

Since it's the Iterator that throws the exception, any modifications need to go through it. As there is no special MultiMapIterator you could cast to, compiling a removal list is your best bet.

Kayaman
  • 72,141
  • 5
  • 83
  • 121
0

Use a temp map,

        Map<String, List<SocketConnection>> tempMap = new HashMap<String, List<SocketConnection>>();

        for (Map.Entry<String, List<SocketConnection>> e: clientPrinterConnections.entrySet()) {
          if (e.getValue().contains(connection)) {
            tempMap.put(e.getKey(),e.getValue());
          }
        }

        clientPrinterConnections.clear();
        clientPrinterConnections.putAll(tempMap);
Sharan De Silva
  • 598
  • 1
  • 8
  • 27