3

I have a HashMap that stores a custom Object and maps it to a certain ArrayList in a class. My class communicates with another class (think MVC style) and it passes a copy of that hashmap. So, in my "model", I would have:

public Map<AbstractArtistry, ArrayList<AbstractCommand>> getHashMap() {
    return new LinkedHashMap<AbstractArtistry, ArrayList<AbstractCommand>>(this.hashmap);
  }

However, my "controller", when it gets that, can still edit the AbstractArtistries inside of the model's this.hashmap. To avoid this, do I have to create a new instance of an Abstract Artistry over and over, or is there a cleaner way to do this? Meaning, would I have to loop over model.hashmap.keySet(), create a new instance of every artistry, insert it into a new hashmap (and do the same for all the values), and then return that new hashmap? Or is there a cleaner way?

John Lexus
  • 3,576
  • 3
  • 15
  • 33

1 Answers1

1

You can use streams to copy the map and replace the keys with defensive copies:

this.hashmap.entrySet()
    .stream()
    .collect(Collectors.toMap(e -> createCopy(e.getKey()), Map.Entry::getValue))

If you need to copy the values as well, you can run them through a similar function:

ArrayList<AbstractCommand> copyList(ArrayList<AbstractCommand> list) {
    return list.stream()
        .map(c -> copyCommand(c))
        .collect(Collectors.toCollection(ArrayList::new));
}
shmosel
  • 49,289
  • 6
  • 73
  • 138
  • How do they all fit together so I could return a new hashmap? – John Lexus Apr 03 '18 at 01:47
  • @JohnLexus All of what? That snippet returns a map. – shmosel Apr 03 '18 at 01:48
  • I am saying how do I combine the first and second snippets? The first snippet looks like it return a map with just keys and no values, while the second return a (map?) of the values, not with my AbstractArtistries as keys. Or am I reading this wrong (sorry new to Java)? – John Lexus Apr 03 '18 at 01:51
  • It's a hard to give you a functional snippet with the little detail you've provided. But basically you'd replace `Map.Entry::getValue` with `e -> copyList(e.getValue())` to copy the values. – shmosel Apr 03 '18 at 01:53
  • 1
    I've updated the second snippet to return `ArrayList`. But ideally you should be sticking to the `List` interface. – shmosel Apr 03 '18 at 01:55
  • I would have to make the "createCopy" method, right? Also, once I execute the first snippet, what does it return a new hashmap without editing the `this.hashmap` variable? So I could literally do `return (first snippet)`? – John Lexus Apr 03 '18 at 01:58
  • 1
    That's right. You would also have to create `copyCommand()`. Unless you don't need a deep copy, in which case you can just return `new ArrayList<>(list)`. – shmosel Apr 03 '18 at 02:01
  • Hey man, do you know how I can make this return a LinkedHashMap instead of a HashMap? – John Lexus Apr 04 '18 at 00:28
  • You'll need to use the [`toMap()`](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html#toMap-java.util.function.Function-java.util.function.Function-java.util.function.BinaryOperator-java.util.function.Supplier-) overload that accepts a map supplier. – shmosel Apr 04 '18 at 00:30
  • think you can provide an example...? – John Lexus Apr 04 '18 at 00:31