Author: tomwhite Date: Thu Feb 7 10:43:21 2013 New Revision: 1443395 URL: http://svn.apache.org/viewvc?rev=1443395&view=rev Log: HADOOP-9124. SortedMapWritable violates contract of Map interface for equals() and hashCode(). Contributed by Surenkumar Nihalani
Modified: hadoop/common/branches/branch-1/CHANGES.txt hadoop/common/branches/branch-1/src/core/org/apache/hadoop/io/SortedMapWritable.java hadoop/common/branches/branch-1/src/test/org/apache/hadoop/io/TestSortedMapWritable.java Modified: hadoop/common/branches/branch-1/CHANGES.txt URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1/CHANGES.txt?rev=1443395&r1=1443394&r2=1443395&view=diff ============================================================================== --- hadoop/common/branches/branch-1/CHANGES.txt (original) +++ hadoop/common/branches/branch-1/CHANGES.txt Thu Feb 7 10:43:21 2013 @@ -479,6 +479,9 @@ Release 1.2.0 - unreleased MAPREDUCE-4970. Child tasks (try to) create security audit log files. (sandyr via tucu) + HADOOP-9124. SortedMapWritable violates contract of Map interface for + equals() and hashCode(). (Surenkumar Nihalani via tomwhite) + Release 1.1.2 - Unreleased INCOMPATIBLE CHANGES Modified: hadoop/common/branches/branch-1/src/core/org/apache/hadoop/io/SortedMapWritable.java URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1/src/core/org/apache/hadoop/io/SortedMapWritable.java?rev=1443395&r1=1443394&r2=1443395&view=diff ============================================================================== --- hadoop/common/branches/branch-1/src/core/org/apache/hadoop/io/SortedMapWritable.java (original) +++ hadoop/common/branches/branch-1/src/core/org/apache/hadoop/io/SortedMapWritable.java Thu Feb 7 10:43:21 2013 @@ -203,4 +203,27 @@ public class SortedMapWritable extends A e.getValue().write(out); } } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj instanceof SortedMapWritable) { + Map map = (Map) obj; + if (size() != map.size()) { + return false; + } + + return entrySet().equals(map.entrySet()); + } + + return false; + } + + @Override + public int hashCode() { + return instance.hashCode(); + } } Modified: hadoop/common/branches/branch-1/src/test/org/apache/hadoop/io/TestSortedMapWritable.java URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1/src/test/org/apache/hadoop/io/TestSortedMapWritable.java?rev=1443395&r1=1443394&r2=1443395&view=diff ============================================================================== --- hadoop/common/branches/branch-1/src/test/org/apache/hadoop/io/TestSortedMapWritable.java (original) +++ hadoop/common/branches/branch-1/src/test/org/apache/hadoop/io/TestSortedMapWritable.java Thu Feb 7 10:43:21 2013 @@ -19,15 +19,21 @@ */ package org.apache.hadoop.io; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + import java.util.Map; -import junit.framework.TestCase; +import org.junit.Test; /** * Tests SortedMapWritable */ -public class TestSortedMapWritable extends TestCase { +public class TestSortedMapWritable { /** the test */ + @Test @SuppressWarnings("unchecked") public void testSortedMapWritable() { Text[] keys = { @@ -92,6 +98,7 @@ public class TestSortedMapWritable exten /** * Test that number of "unknown" classes is propagated across multiple copies. */ + @Test @SuppressWarnings("deprecation") public void testForeignClass() { SortedMapWritable inMap = new SortedMapWritable(); @@ -101,4 +108,60 @@ public class TestSortedMapWritable exten SortedMapWritable copyOfCopy = new SortedMapWritable(outMap); assertEquals(1, copyOfCopy.getNewClasses()); } + + /** + * Tests if equal and hashCode method still hold the contract. + */ + @Test + public void testEqualsAndHashCode() { + String failureReason; + SortedMapWritable mapA = new SortedMapWritable(); + SortedMapWritable mapB = new SortedMapWritable(); + + // Sanity checks + failureReason = "SortedMapWritable couldn't be initialized. Got null reference"; + assertNotNull(failureReason, mapA); + assertNotNull(failureReason, mapB); + + // Basic null check + assertFalse("equals method returns true when passed null", + mapA.equals(null)); + + // When entry set is empty, they should be equal + assertTrue("Two empty SortedMapWritables are no longer equal", + mapA.equals(mapB)); + + // Setup + Text[] keys = { new Text("key1"), new Text("key2") }; + + BytesWritable[] values = { new BytesWritable("value1".getBytes()), + new BytesWritable("value2".getBytes()) }; + + mapA.put(keys[0], values[0]); + mapB.put(keys[1], values[1]); + + // entrySets are different + failureReason = "Two SortedMapWritables with different data are now equal"; + assertTrue(failureReason, mapA.hashCode() != mapB.hashCode()); + assertTrue(failureReason, !mapA.equals(mapB)); + assertTrue(failureReason, !mapB.equals(mapA)); + + mapA.put(keys[1], values[1]); + mapB.put(keys[0], values[0]); + + // entrySets are now same + failureReason = "Two SortedMapWritables with same entry sets formed in different order are now different"; + assertEquals(failureReason, mapA.hashCode(), mapB.hashCode()); + assertTrue(failureReason, mapA.equals(mapB)); + assertTrue(failureReason, mapB.equals(mapA)); + + // Let's check if entry sets of same keys but different values + mapA.put(keys[0], values[1]); + mapA.put(keys[1], values[0]); + + failureReason = "Two SortedMapWritables with different content are now equal"; + assertTrue(failureReason, mapA.hashCode() != mapB.hashCode()); + assertTrue(failureReason, !mapA.equals(mapB)); + assertTrue(failureReason, !mapB.equals(mapA)); + } }