You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by jb...@apache.org on 2021/02/03 16:59:44 UTC

[geode-native] branch develop updated: GEODE-8910: Corrects hashing between .NET and Java (#740)

This is an automated email from the ASF dual-hosted git repository.

jbarrett pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode-native.git


The following commit(s) were added to refs/heads/develop by this push:
     new 798aafb  GEODE-8910: Corrects hashing between .NET and Java (#740)
798aafb is described below

commit 798aafb8e43536a07a809940a05cdd846d94cc93
Author: Jacob Barrett <jb...@pivotal.io>
AuthorDate: Wed Feb 3 08:59:36 2021 -0800

    GEODE-8910: Corrects hashing between .NET and Java (#740)
    
    * Adds Objects util similar to Java's Objects util.
    * Adds test to validate consistency with Java.
---
 clicache/integration-test2/PositionKey.cs |   4 +-
 clicache/src/CMakeLists.txt               |   2 +
 clicache/src/CacheableDate.cpp            |   6 +-
 clicache/src/Objects.cpp                  | 162 +++++++++++++++
 clicache/src/Objects.hpp                  | 178 +++++++++++++++++
 clicache/test2/CMakeLists.txt             |  11 +-
 clicache/test2/ObjectsTests.cs            | 316 ++++++++++++++++++++++++++++++
 tests/javaobject/cli/PositionKey.java     |   7 +-
 8 files changed, 669 insertions(+), 17 deletions(-)

diff --git a/clicache/integration-test2/PositionKey.cs b/clicache/integration-test2/PositionKey.cs
index 02c6163..3c1c21b 100644
--- a/clicache/integration-test2/PositionKey.cs
+++ b/clicache/integration-test2/PositionKey.cs
@@ -92,9 +92,7 @@ namespace Apache.Geode.Client.IntegrationTests
 
 	public override int GetHashCode()
 	{
-	  int hash = 11;
-	  hash = 31 * hash + (int)m_positionId;
-	  return hash;
+	  return Objects.Hash(m_positionId);
 	}
   }
 }
diff --git a/clicache/src/CMakeLists.txt b/clicache/src/CMakeLists.txt
index 8ad3b32..eddf595 100644
--- a/clicache/src/CMakeLists.txt
+++ b/clicache/src/CMakeLists.txt
@@ -152,6 +152,8 @@ add_library(Apache.Geode SHARED
   native_conditional_unique_ptr.hpp
   native_shared_ptr.hpp
   native_unique_ptr.hpp
+  Objects.cpp
+  Objects.hpp
   PdxIdentityFieldAttribute.hpp
   Pool.cpp
   Pool.hpp
diff --git a/clicache/src/CacheableDate.cpp b/clicache/src/CacheableDate.cpp
index c2f2064..9421489 100644
--- a/clicache/src/CacheableDate.cpp
+++ b/clicache/src/CacheableDate.cpp
@@ -20,6 +20,7 @@
 #include "DataInput.hpp"
 #include "DataOutput.hpp"
 #include "Log.hpp"
+#include "Objects.hpp"
 
 using namespace System;
 
@@ -82,10 +83,7 @@ namespace Apache
       System::Int32 CacheableDate::GetHashCode()
       {
         if (m_hashcode == 0) {
-          TimeSpan epochSpan = m_dateTime - EpochTime;
-          System::Int64 millitime =
-            epochSpan.Ticks / TimeSpan::TicksPerMillisecond;
-          m_hashcode = (int)millitime ^ (int)((System::Int64)millitime >> 32);
+          m_hashcode = Objects::GetHashCode(m_dateTime);
         }
         return m_hashcode;
       }
diff --git a/clicache/src/Objects.cpp b/clicache/src/Objects.cpp
new file mode 100644
index 0000000..ee587df
--- /dev/null
+++ b/clicache/src/Objects.cpp
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Objects.hpp"
+
+#include "CacheableDate.hpp"
+#include "ICacheableKey.hpp"
+
+using namespace System;
+using namespace System::Collections;
+using namespace Apache::Geode::Client;
+
+namespace Apache {
+namespace Geode {
+
+Int32 Objects::Hash(... array<Object^>^ values) {
+ return Objects::GetHashCode(values);
+}
+
+Int32 Objects::GetHashCode(Object^ value) {
+  if (nullptr == value) {
+    return 0;
+  } else if (auto s = dynamic_cast<String^>(value)) {
+    return GetHashCode(s);
+  } else if (auto i = dynamic_cast<Int32^>(value)) {
+    return GetHashCode(*i);
+  } else if (auto l = dynamic_cast<Int64^>(value)) {
+    return GetHashCode(*l);
+  } else if (auto s = dynamic_cast<Int16^>(value)) {
+    return GetHashCode(*s);
+  } else if (auto s = dynamic_cast<Char^>(value)) {
+    return GetHashCode(*s);
+  } else if (auto d = dynamic_cast<DateTime^>(value)) {
+    return GetHashCode(*d);
+  } else if (auto b = dynamic_cast<SByte^>(value)) {
+    return GetHashCode(*b);
+  } else if (auto s = dynamic_cast<Single^>(value)) {
+    return GetHashCode(*s);
+  } else if (auto d = dynamic_cast<Double^>(value)) {
+    return GetHashCode(*d);
+  } else if (auto b = dynamic_cast<Boolean^>(value)) {
+    return GetHashCode(*b);
+  } else if (auto k = dynamic_cast<ICacheableKey^>(value)) {
+    return k->GetHashCode();
+  } else if (auto c = dynamic_cast<IDictionary^>(value)) {
+    return GetHashCode(c);
+  } else if (auto c = dynamic_cast<ICollection^>(value)) {
+    return GetHashCode(c);
+  }
+
+  return value->GetHashCode();
+}
+
+Int32 Objects::GetHashCode(String^ value) {
+  Int32 hash = 0;
+  for each(auto c in value) {
+    hash = 31 * hash + c;
+  }
+  return hash;
+}
+
+Int32 Objects::GetHashCode(Char value) { return value; }
+
+Int32 Objects::GetHashCode(Boolean value) { return value ? 1231 : 1237; }
+
+Int32 Objects::GetHashCode(SByte value) { return value; }
+
+Int32 Objects::GetHashCode(Int16 value) { return value; }
+
+Int32 Objects::GetHashCode(Int32 value) { return value; }
+
+Int32 Objects::GetHashCode(Int64 value) {
+  return static_cast<Int32>(value ^ (value >> 32));
+}
+
+union float_int64_t {
+  float f;
+  int32_t u;
+};
+
+constexpr auto kJavaFloatNaN = 0x7fc00000;
+
+Int32 Objects::GetHashCode(Single value) { 
+  float_int64_t v;
+  if (Single::IsNaN(value)) {
+    // .NET and Java don't aggree on NaN encoding
+    v.u = kJavaFloatNaN;
+  } else {
+    v.f = value;
+  }
+  return GetHashCode(v.u); 
+}
+
+union double_int64_t {
+  double d;
+  int64_t u;
+};
+
+constexpr auto kJavaDoubleNaN = 0x7ff8000000000000L;
+
+Int32 Objects::GetHashCode(Double value) {
+  double_int64_t v;
+  if (Double::IsNaN(value)) {
+    // .NET and Java don't aggree on NaN encoding
+    v.u = kJavaDoubleNaN;
+  } else {
+    v.d = value;
+  }
+  return GetHashCode(v.u); 
+}
+
+Int32 Objects::GetHashCode(DateTime^ value) {
+  if (value == nullptr) {
+    return 0;
+  }
+
+  return GetHashCode(*value);
+}
+
+Int32 Objects::GetHashCode(DateTime value) {
+  auto timeSpanSinceEpoch = value - CacheableDate::EpochTime;
+  auto milliseconds = timeSpanSinceEpoch.Ticks / TimeSpan::TicksPerMillisecond;
+  return GetHashCode(milliseconds);
+}
+
+Int32 Objects::GetHashCode(ICollection^ value) {
+  if (value == nullptr) {
+    return 0;
+  }
+
+  int result = 1;
+  for each (auto element in value) {
+    result = 31 * result + Objects::GetHashCode(element);
+  }
+  return result;
+}
+
+Int32 Objects::GetHashCode(System::Collections::IDictionary^ dictionary) {
+  int h = 0;
+  for each(System::Collections::DictionaryEntry^ entry in dictionary)
+  {
+    h = h + (GetHashCode(entry->Key) ^ GetHashCode(entry->Value));
+  }
+  return h;
+}
+
+}  // namespace Geode
+}  // namespace Apache
diff --git a/clicache/src/Objects.hpp b/clicache/src/Objects.hpp
new file mode 100644
index 0000000..01d9ad8
--- /dev/null
+++ b/clicache/src/Objects.hpp
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+using namespace System;
+
+namespace Apache {
+namespace Geode {
+
+/// <summary>
+/// Provides hash code functions similar to those used by Geode server in 
+/// Java's java.util.Objects and java.util.Arrays classes.
+///
+/// Example:
+/// <pre>
+/// class CustomKey
+/// {
+///   private int a;
+///   private double b;
+///   private String c;
+///
+///   public CustomKey(int a, double b, String c)
+///   {
+///     this.a = a;
+///     this.b = b;
+///     this.c = c;
+///   }
+///
+///   override public int GetHashCode()
+///   {
+///     return Objects.Hash(a, b, c);
+///   }
+/// };
+/// </pre>
+/// </summary>
+ public ref class Objects {
+ public:
+  /// <summary>
+  /// Hashes consistent with java.util.Objects.hash(Object ...).
+  /// </summary>
+  /// <param name="values">
+  /// Variable arguments to combine into hash.
+  /// </param>
+  static Int32 Hash(... array<Object^>^ values);
+
+  
+  /// <summary>
+  /// Hashes consistent with java.util.Objects.hashCode(Object).
+  /// </summary>
+  /// <param name="value">
+  /// Object to hash.
+  /// </param>
+  static Int32 GetHashCode(Object^ value);
+
+
+  /// <summary>
+  /// Hashes consistent with java.lang.String.hashCode().
+  /// </summary>
+  /// <param name="value">
+  /// String to hash.
+  /// </param>
+  static Int32 GetHashCode(String^ value);
+
+  /// <summary>
+  /// Hashes consistent with java.lang.Character.hashCode().
+  /// </summary>
+  /// <param name="value">
+  /// Character to hash.
+  /// </param>
+  static Int32 GetHashCode(Char value);
+
+  /// <summary>
+  /// Hashes consistent with java.lang.Boolean.hashCode().
+  /// </summary>
+  /// <param name="value">
+  /// Boolean to hash.
+  /// </param>
+  static Int32 GetHashCode(Boolean value);
+
+  /// <summary>
+  /// Hashes consistent with java.lang.Byte.hashCode().
+  /// </summary>
+  /// <param name="value">
+  /// Byte to hash.
+  /// </param>
+  static Int32 GetHashCode(SByte value);
+
+  /// <summary>
+  /// Hashes consistent with java.lang.Short.hashCode().
+  /// </summary>
+  /// <param name="value">
+  /// Short to hash.
+  /// </param>
+  static Int32 GetHashCode(Int16 value);
+
+  /// <summary>
+  /// Hashes consistent with java.lang.Integer.hashCode().
+  /// </summary>
+  /// <param name="value">
+  /// Integer to hash.
+  /// </param>
+  static Int32 GetHashCode(Int32 value);
+
+  /// <summary>
+  /// Hashes consistent with java.lang.Long.hashCode().
+  /// </summary>
+  /// <param name="value">
+  /// Long to hash.
+  /// </param>
+  static Int32 GetHashCode(Int64 value);
+
+  /// <summary>
+  /// Hashes consistent with java.lang.Float.hashCode().
+  /// </summary>
+  /// <param name="value">
+  /// FLoat to hash.
+  /// </param>
+  static Int32 GetHashCode(Single value);
+
+  /// <summary>
+  /// Hashes consistent with java.lang.Double.hashCode().
+  /// </summary>
+  /// <param name="value">
+  /// Double to hash.
+  /// </param>
+  static Int32 GetHashCode(Double value);
+
+  /// <summary>
+  /// Hashes consistent with java.util.Date.hashCode().
+  /// </summary>
+  /// <param name="value">
+  /// Date to hash.
+  /// </param>
+  static Int32 GetHashCode(DateTime^ value);
+
+  /// <summary>
+  /// Hashes consistent with java.lang.Date.hashCode().
+  /// </summary>
+  /// <param name="value">
+  /// Date to hash.
+  /// </param>
+  static Int32 GetHashCode(DateTime value);
+
+  /// <summary>
+  /// Hashes consistent with java.util.Arrays.hashCode(Object[]) or
+  /// java.util.List.hashCode().
+  /// </summary>
+  /// <param name="value">
+  /// Array or List like collection to hash.
+  /// </param>
+  static Int32 GetHashCode(System::Collections::ICollection^ collection);
+
+  /// <summary>
+  /// Hashes consistent with java.util.Map.hashCode().
+  /// </summary>
+  /// <param name="dictionary">
+  /// Map to hash.
+  /// </param>
+  static Int32 GetHashCode(System::Collections::IDictionary^ dictionary);
+};
+
+}  // namespace Geode
+}  // namespace Apache
diff --git a/clicache/test2/CMakeLists.txt b/clicache/test2/CMakeLists.txt
index cd022c6..5a316df 100644
--- a/clicache/test2/CMakeLists.txt
+++ b/clicache/test2/CMakeLists.txt
@@ -20,8 +20,9 @@ set(CMAKE_CSharp_FLAGS "/langversion:5")
 
 configure_file(${CMAKE_CURRENT_SOURCE_DIR}/packages.config ${CMAKE_CURRENT_BINARY_DIR}/packages.config COPYONLY)
 
-add_library( ${PROJECT_NAME} SHARED
+add_library(Apache.Geode.Tests2 SHARED
     Tests2.cs
+	ObjectsTests.cs
     xunit.runner.json
     packages.config
 )
@@ -31,12 +32,12 @@ set_source_files_properties(cache.xml xunit.runner.json geode.properties PROPERT
   VS_TOOL_OVERRIDE "None"
 )
 
-target_link_libraries(${PROJECT_NAME}
+target_link_libraries(Apache.Geode.Tests2
   PUBLIC
     Apache.Geode
 )
 
-set_target_properties( ${PROJECT_NAME} PROPERTIES
+set_target_properties(Apache.Geode.Tests2 PROPERTIES
   COMMON_LANGUAGE_RUNTIME "" 
   VS_GLOBAL_ROOTNAMESPACE ${PROJECT_NAME}
   VS_GLOBAL_TreatWarningsAsErrors True
@@ -54,10 +55,10 @@ set_target_properties( ${PROJECT_NAME} PROPERTIES
 )
 
 if(NOT "${STRONG_NAME_KEY}" STREQUAL "")
-  set_target_properties( ${PROJECT_NAME} PROPERTIES
+  set_target_properties(Apache.Geode.Tests2 PROPERTIES
     VS_GLOBAL_SignAssembly "true"
     VS_GLOBAL_AssemblyOriginatorKeyFile ${STRONG_NAME_KEY}
   )
 endif()
 
-add_dependencies(${PROJECT_NAME} nuget-restore)
+add_dependencies(Apache.Geode.Tests2 nuget-restore)
diff --git a/clicache/test2/ObjectsTests.cs b/clicache/test2/ObjectsTests.cs
new file mode 100644
index 0000000..6859491
--- /dev/null
+++ b/clicache/test2/ObjectsTests.cs
@@ -0,0 +1,316 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Collections;
+using Xunit;
+
+namespace Apache.Geode
+{
+
+
+  /// <summary>
+  /// Asserts consistent hash results with Java server.
+  /// 
+  /// See Java class org.apache.geode.cache.util.ObjectsTest for test parity.
+  /// </summary>
+  public class ObjectsTests
+  {
+
+    [Fact]
+    public void GetHashCodeNullObjectIsZero()
+    {
+      Assert.Equal(0, Objects.GetHashCode((Object)null));
+    }
+
+    [Fact]
+    public void GetHashCodeOfEmptyArrayIs1()
+    {
+      Assert.Equal(1, Objects.GetHashCode(new Object[0]));
+    }
+
+    [Fact]
+    public void HashCodeEmptyArgsIs1()
+    {
+      Assert.Equal(1, Objects.Hash());
+    }
+
+    [Fact]
+    public void HashOfString()
+    {
+      Assert.Equal(0, Objects.GetHashCode(""));
+      Assert.Equal(48, Objects.GetHashCode("0"));
+      Assert.Equal(57, Objects.GetHashCode("9"));
+      Assert.Equal(97, Objects.GetHashCode("a"));
+      Assert.Equal(122, Objects.GetHashCode("z"));
+      Assert.Equal(65, Objects.GetHashCode("A"));
+      Assert.Equal(90, Objects.GetHashCode("Z"));
+
+      Assert.Equal(1077910243, Objects.GetHashCode("supercalifragilisticexpialidocious"));
+
+      Assert.Equal(1544552287, Objects.GetHashCode("You had me at meat tornad\u00F6!\U000F0000"));
+
+      Assert.Equal(701776767, Objects.GetHashCode("You had me at\0meat tornad\u00F6!\U000F0000"));
+
+      Assert.Equal(
+          512895612,
+          Objects.GetHashCode("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do "
+               + "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
+               + "enim ad minim veniam, quis nostrud exercitation ullamco laboris "
+               + "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in "
+               + "reprehenderit in voluptate velit esse cillum dolore eu fugiat "
+               + "nulla pariatur. Excepteur sint occaecat cupidatat non proident, "
+               + "sunt in culpa qui officia deserunt mollit anim id est laborum."));
+
+      Assert.Equal(
+          -1425027716,
+          Objects.GetHashCode("\u16bb\u16d6\u0020\u16b3\u16b9\u16ab\u16a6\u0020\u16a6\u16ab"
+               + "\u16cf\u0020\u16bb\u16d6\u0020\u16d2\u16a2\u16de\u16d6\u0020"
+               + "\u16a9\u16be\u0020\u16a6\u16ab\u16d7\u0020\u16da\u16aa\u16be"
+               + "\u16de\u16d6\u0020\u16be\u16a9\u16b1\u16a6\u16b9\u16d6\u16aa"
+               + "\u16b1\u16de\u16a2\u16d7\u0020\u16b9\u16c1\u16a6\u0020\u16a6"
+               + "\u16aa\u0020\u16b9\u16d6\u16e5\u16ab"));
+    }
+
+    [Fact]
+    public void HashOf1String()
+    {
+      Assert.Equal(121, Objects.Hash("Z"));
+    }
+
+    [Fact]
+    public void GetHashCodeOfChar()
+    {
+      Assert.Equal(48, Objects.GetHashCode('0'));
+      Assert.Equal(57, Objects.GetHashCode('9'));
+      Assert.Equal(97, Objects.GetHashCode('a'));
+      Assert.Equal(122, Objects.GetHashCode('z'));
+      Assert.Equal(65, Objects.GetHashCode('A'));
+      Assert.Equal(90, Objects.GetHashCode('Z'));
+    }
+
+    [Fact]
+    public void HashOf1Char()
+    {
+      Assert.Equal(121, Objects.Hash('Z'));
+    }
+
+    [Fact]
+    public void GetHashCodeOfBoolean()
+    {
+      Assert.Equal(1237, Objects.GetHashCode(false));
+      Assert.Equal(1231, Objects.GetHashCode(true));
+    }
+
+    [Fact]
+    public void HashOf1Boolean()
+    {
+      Assert.Equal(1262, Objects.Hash(true));
+    }
+
+    [Fact]
+    public void GetHashCodeOfByte()
+    {
+      Assert.Equal(0, Objects.GetHashCode((SByte)0));
+      Assert.Equal(1, Objects.GetHashCode((SByte)1));
+      Assert.Equal(-1, Objects.GetHashCode((SByte)(-1)));
+      Assert.Equal(SByte.MaxValue, Objects.GetHashCode(SByte.MaxValue));
+      Assert.Equal(SByte.MinValue, Objects.GetHashCode(SByte.MinValue));
+    }
+
+    [Fact]
+    public void HashOf1Byte()
+    {
+      Assert.Equal(158, Objects.Hash(SByte.MaxValue));
+    }
+
+    [Fact]
+    public void GetHashCodeOfInt16()
+    {
+      Assert.Equal(0, Objects.GetHashCode((Int16)0));
+      Assert.Equal(1, Objects.GetHashCode((Int16)1));
+      Assert.Equal(-1, Objects.GetHashCode((Int16)(-1)));
+      Assert.Equal(Int16.MaxValue, Objects.GetHashCode(Int16.MaxValue));
+      Assert.Equal(Int16.MinValue, Objects.GetHashCode(Int16.MinValue));
+    }
+
+    [Fact]
+    public void HashOf1Int16()
+    {
+      Assert.Equal(32798, Objects.Hash(Int16.MaxValue));
+    }
+
+    [Fact]
+    public void GetHashCodeOfInt32()
+    {
+      Assert.Equal(0, Objects.GetHashCode(0));
+      Assert.Equal(1, Objects.GetHashCode(1));
+      Assert.Equal(-1, Objects.GetHashCode(-1));
+      Assert.Equal(Int32.MaxValue, Objects.GetHashCode(Int32.MaxValue));
+      Assert.Equal(Int32.MinValue, Objects.GetHashCode(Int32.MinValue));
+    }
+
+    [Fact]
+    public void HashOf1Int32()
+    {
+      Assert.Equal(-2147483618, Objects.Hash(Int32.MaxValue));
+    }
+
+    [Fact]
+    public void GetHashCodeOfInt64()
+    {
+      Assert.Equal(0, Objects.GetHashCode(0L));
+      Assert.Equal(1, Objects.GetHashCode(1L));
+      Assert.Equal(0, Objects.GetHashCode(-1L));
+      Assert.Equal(-2147483648, Objects.GetHashCode(((Int64)Int32.MaxValue) + 1));
+      Assert.Equal(-2147483648, Objects.GetHashCode(((Int64)Int32.MinValue) - 1));
+      Assert.Equal(-2147483648, Objects.GetHashCode(Int64.MaxValue));
+      Assert.Equal(-2147483648, Objects.GetHashCode(Int64.MinValue));
+      Assert.Equal(-1073741824, Objects.GetHashCode(Int64.MaxValue >> 1));
+      Assert.Equal(-1073741824, Objects.GetHashCode(Int64.MinValue >> 1));
+      Assert.Equal(-536870912, Objects.GetHashCode(Int64.MaxValue >> 2));
+      Assert.Equal(-536870912, Objects.GetHashCode(Int64.MinValue >> 2));
+      Assert.Equal(0, Objects.GetHashCode(-9223372034707292160L));
+    }
+
+    [Fact]
+    public void HashOf1Int64()
+    {
+      Assert.Equal(-2147483617, Objects.Hash(Int64.MaxValue));
+    }
+
+    [Fact]
+    public void GetHashCodeOfFloat()
+    {
+      Assert.Equal(0, Objects.GetHashCode(0.0f));
+      Assert.Equal(-2147483648, Objects.GetHashCode(-0.0f));
+      Assert.Equal(1065353216, Objects.GetHashCode(1.0f));
+      Assert.Equal(-1082130432, Objects.GetHashCode(-1.0f));
+      Assert.Equal(2139095039, Objects.GetHashCode(Single.MaxValue));
+      Assert.Equal(1, Objects.GetHashCode(Single.Epsilon));
+      Assert.Equal(-8388609, Objects.GetHashCode(Single.MinValue));
+      Assert.Equal(2139095040, Objects.GetHashCode(Single.PositiveInfinity));
+      Assert.Equal(-8388608, Objects.GetHashCode(Single.NegativeInfinity));
+      Assert.Equal(8388608, Objects.GetHashCode(1.17549435E-38f));
+      Assert.Equal(2143289344, Objects.GetHashCode(Single.NaN));
+    }
+
+    [Fact]
+    public void HashOf1Float()
+    {
+      Assert.Equal(2139095070, Objects.Hash(Single.MaxValue));
+    }
+
+    [Fact]
+    public void GetHashCodeOfDouble()
+    {
+      Assert.Equal(0, Objects.GetHashCode(0.0d));
+      Assert.Equal(-2147483648, Objects.GetHashCode(-0.0d));
+      Assert.Equal(1072693248, Objects.GetHashCode(1.0d));
+      Assert.Equal(-1074790400, Objects.GetHashCode(-1.0d));
+      Assert.Equal(-2146435072, Objects.GetHashCode(Double.MaxValue));
+      Assert.Equal(1, Objects.GetHashCode(Double.Epsilon));
+      Assert.Equal(1048576, Objects.GetHashCode(Double.MinValue));
+      Assert.Equal(2146435072, Objects.GetHashCode(Double.PositiveInfinity));
+      Assert.Equal(-1048576, Objects.GetHashCode(Double.NegativeInfinity));
+      Assert.Equal(1048576, Objects.GetHashCode(2.2250738585072014E-308d));
+      Assert.Equal(2146959360, Objects.GetHashCode(Double.NaN));
+    }
+
+    [Fact]
+    public void HashOf1Double()
+    {
+      Assert.Equal(-2146435041, Objects.Hash(Double.MaxValue));
+    }
+
+
+    [Fact]
+    public void GetHashCodeOfDate()
+    {
+      Assert.Equal(0, Objects.GetHashCode(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)));
+      Assert.Equal(1583802735, Objects.GetHashCode(new DateTime(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc)));
+      Assert.Equal(-927080926, Objects.GetHashCode(new DateTime(3020, 1, 1, 0, 0, 0, DateTimeKind.Utc)));
+      Assert.Equal(1670202000, Objects.GetHashCode(new DateTime(1920, 1, 1, 0, 0, 0, DateTimeKind.Utc)));
+      Assert.Equal(542840753, Objects.GetHashCode(new DateTime(1820, 1, 1, 0, 0, 0, DateTimeKind.Utc)));
+    }
+
+    [Fact]
+    public void HashOf1DateTime()
+    {
+      Assert.Equal(31, Objects.Hash(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)));
+    }
+
+    [Fact]
+    public void GetHashCodeOfArrays()
+    {
+      Assert.Equal(955331, Objects.GetHashCode(new SByte[] { 1, 2, 3, 4 }));
+      Assert.Equal(955331, Objects.GetHashCode(new Int16[] { 1, 2, 3, 4 }));
+      Assert.Equal(955331, Objects.GetHashCode(new Int32[] { 1, 2, 3, 4 }));
+      Assert.Equal(955331, Objects.GetHashCode(new Int64[] { 1L, 2L, 3L, 4L }));
+      Assert.Equal(265164673, Objects.GetHashCode(new Single[] { 0.0f, 1.0f, -1.0f, Single.NaN }));
+      Assert.Equal(-1039788159, Objects.GetHashCode(new Double[] { 0.0d, 1.0d, -1.0d, Double.NaN }));
+      Assert.Equal(3910595, Objects.GetHashCode(new String[] { "a", "b", "c", "d"}));
+      Assert.Equal(3910595, Objects.GetHashCode(new Char[] { 'a', 'b', 'c', 'd' }));
+    }
+
+
+    [Fact]
+    public void GetHashCodeOfList()
+    {
+      Assert.Equal(955331, Objects.GetHashCode(new List<int> { 1, 2, 3, 4 }));
+      Assert.Equal(955331, Objects.GetHashCode(new ArrayList { 1, 2, 3, 4 }));
+      Assert.Equal(955331, Objects.GetHashCode(new LinkedList<int>(new int[] { 1, 2, 3, 4 })));
+      Assert.Equal(955331, Objects.GetHashCode(new Stack<int>(new int[] { 4, 3, 2, 1 })));
+      Assert.Equal(955331, Objects.GetHashCode(new Stack(new int[] { 4, 3, 2, 1 })));
+    }
+
+    [Fact]
+    public void GetHashCodeOfMap()
+    {
+      Assert.Equal(10, Objects.GetHashCode(new Hashtable { { 1, 2 }, { 3, 4 } }));
+      Assert.Equal(10, Objects.GetHashCode(new Dictionary<int, int> { { 1, 2 }, { 3, 4 } }));
+    }
+
+    class CustomKey
+    {
+      private int a;
+      private double b;
+      private String c;
+
+      public CustomKey(int a, double b, String c)
+      {
+        this.a = a;
+        this.b = b;
+        this.c = c;
+      }
+
+      override public int GetHashCode()
+      {
+        return Objects.Hash(a, b, c);
+      }
+    };
+
+    [Fact]
+    public void hashOfCustomKey()
+    {
+      Assert.Equal(-1073604993, Objects.GetHashCode(new CustomKey(1, 2.0, "key")));
+    }
+
+  }
+
+};
diff --git a/tests/javaobject/cli/PositionKey.java b/tests/javaobject/cli/PositionKey.java
index 2a0ce2b..9921ad0 100644
--- a/tests/javaobject/cli/PositionKey.java
+++ b/tests/javaobject/cli/PositionKey.java
@@ -52,11 +52,8 @@ public class PositionKey implements DataSerializable {
     out.writeLong(this.positionId);
   } 
   
-  public int hashCode()
-  {
-      final int prime = 31;
-      int result = prime * (int)positionId;
-      return result;
+  public int hashCode() {
+    return Objects.hash(positionId);
   }
 
   public boolean equals(final Object obj)