-
-
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(existing key) should keep order #1838 #1983
Conversation
newMap = newMap.remove(key); | ||
final Queue<Tuple2<K, V>> newList; | ||
final Option<V> currentEntry = get(key); | ||
if (currentEntry.isDefined()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe we should check that oldValue.equals(newValue)
is true
and in this case do nothing (?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not necessary not to add the entry if the old value/new value are equal, instead we will act like java.util.HashMap
. It will be important when implementing the MapView
later.
public class Test {
public static void main(String[] args) {
A x = new A(1, 1);
A y = new A(1, 2);
// true
System.out.println(x.equals(y));
java.util.Map<Integer, A> map = new java.util.HashMap<>();
map.put(1, x);
map.put(1, y);
// true
System.out.println(map.get(1) == y);
}
static class A {
final int a;
final int b;
A(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (o instanceof A) {
final A that = (A) o;
return this.a == that.a;
} else {
return false;
}
}
@Override
public int hashCode() {
return a;
}
@Override
public String toString() {
return "A(a:" + a + ", b:" + b + ")";
}
}
}
Codecov Report
@@ Coverage Diff @@
## master #1983 +/- ##
===========================================
- Coverage 98.01% 98% -0.01%
+ Complexity 5175 5173 -2
===========================================
Files 91 91
Lines 11788 11788
Branches 1530 1530
===========================================
- Hits 11554 11553 -1
Misses 116 116
- Partials 118 119 +1
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Ruslan, thank you!
While doing the review I realized that this change is not correct. We should close the PR.
What do you think?
Greets,
Daniel
@@ -181,18 +181,23 @@ public void shouldReturnModifiedKeysMapWithNonUniqueMapperAndPredictableOrder() | |||
// -- put | |||
|
|||
@Test | |||
public void shouldAppendToTheEndWhenPuttingAnExistingKeyAndNonExistingValue() { | |||
public void shouldIgnoreOrderOfEntriesWhenComparingForEquality() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add an @Override
here
assertThat(actualMap.size()).isEqualTo(expectedMap.size()); | ||
actualMap.forEach((k, v) -> assertThat(v).isEqualTo(expectedMap.get(k))); | ||
return this; | ||
if (actual instanceof LinkedHashMap && obj instanceof LinkedHashMap) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please override the assertThat(Iterable<T> actual)
method in LinkedHashMapTest
test instead. A super
call does inherently rely on the type hierarchy, which is not necessary and error prone.
Instead LinkedHashMapTest
should defined what equality means (even if it looks like duplicate code at a first glance).
@Override
protected <T> IterableAssert<T> assertThat(Iterable<T> actual) {
...
}
newMap = newMap.remove(key); | ||
final Queue<Tuple2<K, V>> newList; | ||
final Option<V> currentEntry = get(key); | ||
if (currentEntry.isDefined()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not necessary not to add the entry if the old value/new value are equal, instead we will act like java.util.HashMap
. It will be important when implementing the MapView
later.
public class Test {
public static void main(String[] args) {
A x = new A(1, 1);
A y = new A(1, 2);
// true
System.out.println(x.equals(y));
java.util.Map<Integer, A> map = new java.util.HashMap<>();
map.put(1, x);
map.put(1, y);
// true
System.out.println(map.get(1) == y);
}
static class A {
final int a;
final int b;
A(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (o instanceof A) {
final A that = (A) o;
return this.a == that.a;
} else {
return false;
}
}
@Override
public int hashCode() {
return a;
}
@Override
public String toString() {
return "A(a:" + a + ", b:" + b + ")";
}
}
}
return Collections.equals(this.list, ((LinkedHashMap<?, ?>) o).list); | ||
} else { | ||
return Collections.equals(this, o); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We changed the equals behavior.
This is not correct according to the Traversable.equals()
javadoc. We act like Scala, which is:
scala> import scala.collection.immutable._
import scala.collection.immutable._
scala> val map1 = ListMap((1, 'a'), (2, 'b'))
map1: scala.collection.immutable.ListMap[Int,Char] = ListMap(1 -> a, 2 -> b)
scala> val map2 = ListMap((2, 'b'), (1, 'a'))
map2: scala.collection.immutable.ListMap[Int,Char] = ListMap(2 -> b, 1 -> a)
scala> map1 == map2
res0: Boolean = true
Note: The immutable LinkedHashMap is called ListMap in Scala
Note: We still are able to use |
This PR (and issue) is very problemastic. It's OK for me to keep all things as is. I'm offline now, please close both. |
I will keep it open for a while. Maybe there is some discussion...
Does it break s.th. for you? |
@ruslansennov Just one more word on this: this PR would break the Liskov Substitution Principle. The following should be true for Maps that contain final Map<Integer, String> map1 = ...
final Map<Integer, String> map2 = ...
assert map1.equals(map2); |
@ruslansennov oh noes, my review was 💩 Could you please make a PR for the put() fix, only? We will take it into the 0.9.0 release. Nearly all relevant 0.9.0 issues are finished now! |
Also equality of
LinkedHashMap
withLinkedHashMap
fixed (see #1555)