-
-
Notifications
You must be signed in to change notification settings - Fork 638
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
LinkedHashMap.put should replace existing entry and maintain order #1838
Comments
I remember that topic popped up a while ago. Before we change it I want to ensure that other immutable implementations act the same. When looking at Java and Scala, there are currently only immutable versions of LinkedHashMap. |
alas it seems Scala behaves the same scala> import collection.immutable.ListMap
import collection.immutable.ListMap
scala> val map = ListMap(1 -> 1, 2 -> 2, 3 -> 3)
map: scala.collection.immutable.ListMap[Int,Int] = ListMap(1 -> 1, 2 -> 2, 3 -> 3)
scala> val updated = map + (2 -> 20)
updated: scala.collection.immutable.ListMap[Int,Int] = ListMap(1 -> 1, 3 -> 3, 2 -> 20) |
We align to Scala. Changing the behavior is currently out of scope. |
Hello everyone. |
@danieldietrich , I agree with @yuriykulikov |
Would you prefer a pull request or to implement it yourselves? This is, in case you decide it makes sense:-) Should be a small change in
|
@yuriykulikov ok, we can do that. PR's welcome! |
I'm not sure we need to remove the entry if the key is already contained, see |
Yep |
|
For example @Override
public LinkedHashMap<K, V> put(K key, V value) {
if (contains(key)) {
return replaceValue(key, value);
} else {
final Queue<Tuple2<K, V>> newList = list.append(Tuple.of(key, value));
final HashMap<K, V> newMap = map.put(key, value);
return new LinkedHashMap<>(newList, newMap);
}
} Update: replaceValue calls put 🙈=> StackOverflow @Override
public LinkedHashMap<K, V> put(K key, V value) {
final Queue<Tuple2<K, V>> newList;
if (contains(key)) {
newList = list.replace(list.find(t -> Objects.equals(t._1, key)).get(), Tuple.of(key, value));
} else {
newList = list.append(Tuple.of(key, value));
}
final HashMap<K, V> newMap = map.put(key, value);
return new LinkedHashMap<>(newList, newMap);
} @yuriykulikov @ruslansennov I think we should overwrite an existing entry in each case, even if new value and old value are equal. The objects might be different, depending on the equals implementation! |
Idea: More Traversable.replace methods would be nice in order to simplify list.replace(list.find(...), ...):
But that is another issue, |
I see you have already implemented everything while I was forking/cloning :-) |
I have another optimization... please wait a minute |
We can fuse the get and the find operations (which are O(n)) by trading them with one Option instance: @Override
public LinkedHashMap<K, V> put(K key, V value) {
final Queue<Tuple2<K, V>> newList;
final Option<Tuple2<K, V>> currentEntry = get(key);
if (currentEntry.isDefined()) {
newList = list.replace(currentEntry.get(), Tuple.of(key, value));
} else {
newList = list.append(Tuple.of(key, value));
}
final HashMap<K, V> newMap = map.put(key, value);
return new LinkedHashMap<>(newList, newMap);
} |
One list traversal less, right? |
Yes |
No, both are O(1) because of underlying |
you can use wrap(newList, newMap) instead of new LinkedHashMap<>(newList, newMap) while you are at it:-) |
@ruslansennov but we save a find on the Queue |
list.replace is O(n) I believe, find() as well. My initial suggestion with index will also be O(n) since it is a linked list. It does not get faster than the last variant by Daniel. |
Yes |
Hi, can you please let me know how to achieve the opposite requirement of this? |
@anubliss remove first and then add? |
I noticed a difference between the java and javaslang 'put' implementations on LinkedHashMap
My assumption is that a 'put' should replace the existing entry and also maintain the original insertion order
The text was updated successfully, but these errors were encountered: