You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@avro.apache.org by "github-code-scanning[bot] (via GitHub)" <gi...@apache.org> on 2023/02/15 00:55:32 UTC

[GitHub] [avro] github-code-scanning[bot] commented on a diff in pull request #2090: [AVRO-3713]: [Java] Fix Map synchronization regression

github-code-scanning[bot] commented on code in PR #2090:
URL: https://github.com/apache/avro/pull/2090#discussion_r1106539893


##########
lang/java/avro/src/main/java/org/apache/avro/util/springframework/ObjectUtils.java:
##########
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2002-2021 the original author or authors.
+ *
+ * Licensed 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
+ *
+ *      https://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.
+ */
+
+package org.apache.avro.util.springframework;
+
+import org.apache.avro.reflect.Nullable;
+import org.apache.avro.util.ClassUtils;
+
+import java.util.Arrays;
+
+/**
+ * Miscellaneous object utility methods.
+ *
+ * <p>Mainly for internal use within the framework.
+ *
+ * <p>Thanks to Alex Ruiz for contributing several enhancements to this class!
+ *
+ * @author Juergen Hoeller
+ * @author Keith Donald
+ * @author Rod Johnson
+ * @author Rob Harrop
+ * @author Chris Beams
+ * @author Sam Brannen
+ * @see ClassUtils
+ * see CollectionUtils
+ * see StringUtils
+ * @since 19.03.2004
+ */
+class ObjectUtils {
+  private ObjectUtils() {
+  }
+
+  private static final int INITIAL_HASH = 7;
+  private static final int MULTIPLIER = 31;
+
+
+  /**
+   * Determine whether the given array is empty:
+   * i.e. {@code null} or of zero length.
+   *
+   * @param array the array to check
+   */
+  public static boolean isEmpty(@Nullable Object[] array) {
+    return (array == null || array.length == 0);
+  }
+
+
+  //---------------------------------------------------------------------
+  // Convenience methods for content-based equality/hash-code handling
+  //---------------------------------------------------------------------
+
+  /**
+   * Determine if the given objects are equal, returning {@code true} if
+   * both are {@code null} or {@code false} if only one is {@code null}.
+   * <p>Compares arrays with {@code Arrays.equals}, performing an equality
+   * check based on the array elements rather than the array reference.
+   *
+   * @param o1 first Object to compare
+   * @param o2 second Object to compare
+   * @return whether the given objects are equal
+   * @see Object#equals(Object)
+   * @see Arrays#equals
+   */
+  public static boolean nullSafeEquals(@Nullable Object o1, @Nullable Object o2) {
+    if (o1 == o2) {
+      return true;
+    }
+    if (o1 == null || o2 == null) {
+      return false;
+    }
+    if (o1.equals(o2)) {
+      return true;
+    }
+    if (o1.getClass().isArray() && o2.getClass().isArray()) {
+      return arrayEquals(o1, o2);
+    }
+    return false;
+  }
+
+  /**
+   * Compare the given arrays with {@code Arrays.equals}, performing an equality
+   * check based on the array elements rather than the array reference.
+   *
+   * @param o1 first array to compare
+   * @param o2 second array to compare
+   * @return whether the given objects are equal
+   * @see #nullSafeEquals(Object, Object)
+   * @see Arrays#equals
+   */
+  private static boolean arrayEquals(Object o1, Object o2) {
+    if (o1 instanceof Object[] && o2 instanceof Object[]) {
+      return Arrays.equals((Object[]) o1, (Object[]) o2);
+    }
+    if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
+      return Arrays.equals((boolean[]) o1, (boolean[]) o2);
+    }
+    if (o1 instanceof byte[] && o2 instanceof byte[]) {
+      return Arrays.equals((byte[]) o1, (byte[]) o2);
+    }
+    if (o1 instanceof char[] && o2 instanceof char[]) {
+      return Arrays.equals((char[]) o1, (char[]) o2);
+    }
+    if (o1 instanceof double[] && o2 instanceof double[]) {
+      return Arrays.equals((double[]) o1, (double[]) o2);
+    }
+    if (o1 instanceof float[] && o2 instanceof float[]) {
+      return Arrays.equals((float[]) o1, (float[]) o2);
+    }
+    if (o1 instanceof int[] && o2 instanceof int[]) {
+      return Arrays.equals((int[]) o1, (int[]) o2);
+    }
+    if (o1 instanceof long[] && o2 instanceof long[]) {
+      return Arrays.equals((long[]) o1, (long[]) o2);
+    }
+    if (o1 instanceof short[] && o2 instanceof short[]) {
+      return Arrays.equals((short[]) o1, (short[]) o2);
+    }
+    return false;
+  }
+
+  /**
+   * Return as hash code for the given object; typically the value of
+   * {@code Object#hashCode()}}. If the object is an array,
+   * this method will delegate to any of the {@code nullSafeHashCode}
+   * methods for arrays in this class. If the object is {@code null},
+   * this method returns 0.
+   *
+   * @see Object#hashCode()
+   * @see #nullSafeHashCode(Object[])
+   * @see #nullSafeHashCode(boolean[])
+   * @see #nullSafeHashCode(byte[])
+   * @see #nullSafeHashCode(char[])
+   * @see #nullSafeHashCode(double[])
+   * @see #nullSafeHashCode(float[])
+   * @see #nullSafeHashCode(int[])
+   * @see #nullSafeHashCode(long[])
+   * @see #nullSafeHashCode(short[])
+   */
+  public static int nullSafeHashCode(@Nullable Object obj) {
+    if (obj == null) {
+      return 0;
+    }
+    if (obj.getClass().isArray()) {
+      if (obj instanceof Object[]) {
+        return nullSafeHashCode((Object[]) obj);
+      }
+      if (obj instanceof boolean[]) {
+        return nullSafeHashCode((boolean[]) obj);
+      }
+      if (obj instanceof byte[]) {
+        return nullSafeHashCode((byte[]) obj);
+      }
+      if (obj instanceof char[]) {
+        return nullSafeHashCode((char[]) obj);
+      }
+      if (obj instanceof double[]) {
+        return nullSafeHashCode((double[]) obj);
+      }
+      if (obj instanceof float[]) {
+        return nullSafeHashCode((float[]) obj);
+      }
+      if (obj instanceof int[]) {
+        return nullSafeHashCode((int[]) obj);
+      }
+      if (obj instanceof long[]) {
+        return nullSafeHashCode((long[]) obj);
+      }
+      if (obj instanceof short[]) {
+        return nullSafeHashCode((short[]) obj);
+      }
+    }
+    return obj.hashCode();
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable Object[] array) {
+    if (array == null) {
+      return 0;
+    }
+    int hash = INITIAL_HASH;
+    for (Object element : array) {
+      hash = MULTIPLIER * hash + nullSafeHashCode(element);
+    }
+    return hash;
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable boolean[] array) {
+    if (array == null) {
+      return 0;
+    }
+    int hash = INITIAL_HASH;
+    for (boolean element : array) {
+      hash = MULTIPLIER * hash + Boolean.hashCode(element);
+    }
+    return hash;
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable byte[] array) {
+    if (array == null) {
+      return 0;
+    }
+    int hash = INITIAL_HASH;
+    for (byte element : array) {
+      hash = MULTIPLIER * hash + element;
+    }
+    return hash;
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable char[] array) {
+    if (array == null) {
+      return 0;
+    }
+    int hash = INITIAL_HASH;
+    for (char element : array) {
+      hash = MULTIPLIER * hash + element;
+    }
+    return hash;
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable double[] array) {
+    if (array == null) {
+      return 0;
+    }
+    int hash = INITIAL_HASH;
+    for (double element : array) {
+      hash = MULTIPLIER * hash + Double.hashCode(element);
+    }
+    return hash;
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable float[] array) {

Review Comment:
   ## Confusing overloading of methods
   
   Method ObjectUtils.nullSafeHashCode(..) could be confused with overloaded method [nullSafeHashCode](1), since dispatch depends on static types.
   
   [Show more details](https://github.com/apache/avro/security/code-scanning/2969)



##########
lang/java/avro/src/main/java/org/apache/avro/util/springframework/ObjectUtils.java:
##########
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2002-2021 the original author or authors.
+ *
+ * Licensed 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
+ *
+ *      https://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.
+ */
+
+package org.apache.avro.util.springframework;
+
+import org.apache.avro.reflect.Nullable;
+import org.apache.avro.util.ClassUtils;
+
+import java.util.Arrays;
+
+/**
+ * Miscellaneous object utility methods.
+ *
+ * <p>Mainly for internal use within the framework.
+ *
+ * <p>Thanks to Alex Ruiz for contributing several enhancements to this class!
+ *
+ * @author Juergen Hoeller
+ * @author Keith Donald
+ * @author Rod Johnson
+ * @author Rob Harrop
+ * @author Chris Beams
+ * @author Sam Brannen
+ * @see ClassUtils
+ * see CollectionUtils
+ * see StringUtils
+ * @since 19.03.2004
+ */
+class ObjectUtils {
+  private ObjectUtils() {
+  }
+
+  private static final int INITIAL_HASH = 7;
+  private static final int MULTIPLIER = 31;
+
+
+  /**
+   * Determine whether the given array is empty:
+   * i.e. {@code null} or of zero length.
+   *
+   * @param array the array to check
+   */
+  public static boolean isEmpty(@Nullable Object[] array) {
+    return (array == null || array.length == 0);
+  }
+
+
+  //---------------------------------------------------------------------
+  // Convenience methods for content-based equality/hash-code handling
+  //---------------------------------------------------------------------
+
+  /**
+   * Determine if the given objects are equal, returning {@code true} if
+   * both are {@code null} or {@code false} if only one is {@code null}.
+   * <p>Compares arrays with {@code Arrays.equals}, performing an equality
+   * check based on the array elements rather than the array reference.
+   *
+   * @param o1 first Object to compare
+   * @param o2 second Object to compare
+   * @return whether the given objects are equal
+   * @see Object#equals(Object)
+   * @see Arrays#equals
+   */
+  public static boolean nullSafeEquals(@Nullable Object o1, @Nullable Object o2) {
+    if (o1 == o2) {
+      return true;
+    }
+    if (o1 == null || o2 == null) {
+      return false;
+    }
+    if (o1.equals(o2)) {
+      return true;
+    }
+    if (o1.getClass().isArray() && o2.getClass().isArray()) {
+      return arrayEquals(o1, o2);
+    }
+    return false;
+  }
+
+  /**
+   * Compare the given arrays with {@code Arrays.equals}, performing an equality
+   * check based on the array elements rather than the array reference.
+   *
+   * @param o1 first array to compare
+   * @param o2 second array to compare
+   * @return whether the given objects are equal
+   * @see #nullSafeEquals(Object, Object)
+   * @see Arrays#equals
+   */
+  private static boolean arrayEquals(Object o1, Object o2) {
+    if (o1 instanceof Object[] && o2 instanceof Object[]) {
+      return Arrays.equals((Object[]) o1, (Object[]) o2);
+    }
+    if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
+      return Arrays.equals((boolean[]) o1, (boolean[]) o2);
+    }
+    if (o1 instanceof byte[] && o2 instanceof byte[]) {
+      return Arrays.equals((byte[]) o1, (byte[]) o2);
+    }
+    if (o1 instanceof char[] && o2 instanceof char[]) {
+      return Arrays.equals((char[]) o1, (char[]) o2);
+    }
+    if (o1 instanceof double[] && o2 instanceof double[]) {
+      return Arrays.equals((double[]) o1, (double[]) o2);
+    }
+    if (o1 instanceof float[] && o2 instanceof float[]) {
+      return Arrays.equals((float[]) o1, (float[]) o2);
+    }
+    if (o1 instanceof int[] && o2 instanceof int[]) {
+      return Arrays.equals((int[]) o1, (int[]) o2);
+    }
+    if (o1 instanceof long[] && o2 instanceof long[]) {
+      return Arrays.equals((long[]) o1, (long[]) o2);
+    }
+    if (o1 instanceof short[] && o2 instanceof short[]) {
+      return Arrays.equals((short[]) o1, (short[]) o2);
+    }
+    return false;
+  }
+
+  /**
+   * Return as hash code for the given object; typically the value of
+   * {@code Object#hashCode()}}. If the object is an array,
+   * this method will delegate to any of the {@code nullSafeHashCode}
+   * methods for arrays in this class. If the object is {@code null},
+   * this method returns 0.
+   *
+   * @see Object#hashCode()
+   * @see #nullSafeHashCode(Object[])
+   * @see #nullSafeHashCode(boolean[])
+   * @see #nullSafeHashCode(byte[])
+   * @see #nullSafeHashCode(char[])
+   * @see #nullSafeHashCode(double[])
+   * @see #nullSafeHashCode(float[])
+   * @see #nullSafeHashCode(int[])
+   * @see #nullSafeHashCode(long[])
+   * @see #nullSafeHashCode(short[])
+   */
+  public static int nullSafeHashCode(@Nullable Object obj) {
+    if (obj == null) {
+      return 0;
+    }
+    if (obj.getClass().isArray()) {
+      if (obj instanceof Object[]) {
+        return nullSafeHashCode((Object[]) obj);
+      }
+      if (obj instanceof boolean[]) {
+        return nullSafeHashCode((boolean[]) obj);
+      }
+      if (obj instanceof byte[]) {
+        return nullSafeHashCode((byte[]) obj);
+      }
+      if (obj instanceof char[]) {
+        return nullSafeHashCode((char[]) obj);
+      }
+      if (obj instanceof double[]) {
+        return nullSafeHashCode((double[]) obj);
+      }
+      if (obj instanceof float[]) {
+        return nullSafeHashCode((float[]) obj);
+      }
+      if (obj instanceof int[]) {
+        return nullSafeHashCode((int[]) obj);
+      }
+      if (obj instanceof long[]) {
+        return nullSafeHashCode((long[]) obj);
+      }
+      if (obj instanceof short[]) {
+        return nullSafeHashCode((short[]) obj);
+      }
+    }
+    return obj.hashCode();
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable Object[] array) {
+    if (array == null) {
+      return 0;
+    }
+    int hash = INITIAL_HASH;
+    for (Object element : array) {
+      hash = MULTIPLIER * hash + nullSafeHashCode(element);
+    }
+    return hash;
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable boolean[] array) {
+    if (array == null) {
+      return 0;
+    }
+    int hash = INITIAL_HASH;
+    for (boolean element : array) {
+      hash = MULTIPLIER * hash + Boolean.hashCode(element);
+    }
+    return hash;
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable byte[] array) {
+    if (array == null) {
+      return 0;
+    }
+    int hash = INITIAL_HASH;
+    for (byte element : array) {
+      hash = MULTIPLIER * hash + element;
+    }
+    return hash;
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable char[] array) {
+    if (array == null) {
+      return 0;
+    }
+    int hash = INITIAL_HASH;
+    for (char element : array) {
+      hash = MULTIPLIER * hash + element;
+    }
+    return hash;
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable double[] array) {
+    if (array == null) {
+      return 0;
+    }
+    int hash = INITIAL_HASH;
+    for (double element : array) {
+      hash = MULTIPLIER * hash + Double.hashCode(element);
+    }
+    return hash;
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable float[] array) {
+    if (array == null) {
+      return 0;
+    }
+    int hash = INITIAL_HASH;
+    for (float element : array) {
+      hash = MULTIPLIER * hash + Float.hashCode(element);
+    }
+    return hash;
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable int[] array) {

Review Comment:
   ## Confusing overloading of methods
   
   Method ObjectUtils.nullSafeHashCode(..) could be confused with overloaded method [nullSafeHashCode](1), since dispatch depends on static types.
   
   [Show more details](https://github.com/apache/avro/security/code-scanning/2968)



##########
lang/java/avro/src/main/java/org/apache/avro/util/springframework/ObjectUtils.java:
##########
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2002-2021 the original author or authors.
+ *
+ * Licensed 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
+ *
+ *      https://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.
+ */
+
+package org.apache.avro.util.springframework;
+
+import org.apache.avro.reflect.Nullable;
+import org.apache.avro.util.ClassUtils;
+
+import java.util.Arrays;
+
+/**
+ * Miscellaneous object utility methods.
+ *
+ * <p>Mainly for internal use within the framework.
+ *
+ * <p>Thanks to Alex Ruiz for contributing several enhancements to this class!
+ *
+ * @author Juergen Hoeller
+ * @author Keith Donald
+ * @author Rod Johnson
+ * @author Rob Harrop
+ * @author Chris Beams
+ * @author Sam Brannen
+ * @see ClassUtils
+ * see CollectionUtils
+ * see StringUtils
+ * @since 19.03.2004
+ */
+class ObjectUtils {
+  private ObjectUtils() {
+  }
+
+  private static final int INITIAL_HASH = 7;
+  private static final int MULTIPLIER = 31;
+
+
+  /**
+   * Determine whether the given array is empty:
+   * i.e. {@code null} or of zero length.
+   *
+   * @param array the array to check
+   */
+  public static boolean isEmpty(@Nullable Object[] array) {
+    return (array == null || array.length == 0);
+  }
+
+
+  //---------------------------------------------------------------------
+  // Convenience methods for content-based equality/hash-code handling
+  //---------------------------------------------------------------------
+
+  /**
+   * Determine if the given objects are equal, returning {@code true} if
+   * both are {@code null} or {@code false} if only one is {@code null}.
+   * <p>Compares arrays with {@code Arrays.equals}, performing an equality
+   * check based on the array elements rather than the array reference.
+   *
+   * @param o1 first Object to compare
+   * @param o2 second Object to compare
+   * @return whether the given objects are equal
+   * @see Object#equals(Object)
+   * @see Arrays#equals
+   */
+  public static boolean nullSafeEquals(@Nullable Object o1, @Nullable Object o2) {
+    if (o1 == o2) {
+      return true;
+    }
+    if (o1 == null || o2 == null) {
+      return false;
+    }
+    if (o1.equals(o2)) {
+      return true;
+    }
+    if (o1.getClass().isArray() && o2.getClass().isArray()) {
+      return arrayEquals(o1, o2);
+    }
+    return false;
+  }
+
+  /**
+   * Compare the given arrays with {@code Arrays.equals}, performing an equality
+   * check based on the array elements rather than the array reference.
+   *
+   * @param o1 first array to compare
+   * @param o2 second array to compare
+   * @return whether the given objects are equal
+   * @see #nullSafeEquals(Object, Object)
+   * @see Arrays#equals
+   */
+  private static boolean arrayEquals(Object o1, Object o2) {
+    if (o1 instanceof Object[] && o2 instanceof Object[]) {
+      return Arrays.equals((Object[]) o1, (Object[]) o2);
+    }
+    if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
+      return Arrays.equals((boolean[]) o1, (boolean[]) o2);
+    }
+    if (o1 instanceof byte[] && o2 instanceof byte[]) {
+      return Arrays.equals((byte[]) o1, (byte[]) o2);
+    }
+    if (o1 instanceof char[] && o2 instanceof char[]) {
+      return Arrays.equals((char[]) o1, (char[]) o2);
+    }
+    if (o1 instanceof double[] && o2 instanceof double[]) {
+      return Arrays.equals((double[]) o1, (double[]) o2);
+    }
+    if (o1 instanceof float[] && o2 instanceof float[]) {
+      return Arrays.equals((float[]) o1, (float[]) o2);
+    }
+    if (o1 instanceof int[] && o2 instanceof int[]) {
+      return Arrays.equals((int[]) o1, (int[]) o2);
+    }
+    if (o1 instanceof long[] && o2 instanceof long[]) {
+      return Arrays.equals((long[]) o1, (long[]) o2);
+    }
+    if (o1 instanceof short[] && o2 instanceof short[]) {
+      return Arrays.equals((short[]) o1, (short[]) o2);
+    }
+    return false;
+  }
+
+  /**
+   * Return as hash code for the given object; typically the value of
+   * {@code Object#hashCode()}}. If the object is an array,
+   * this method will delegate to any of the {@code nullSafeHashCode}
+   * methods for arrays in this class. If the object is {@code null},
+   * this method returns 0.
+   *
+   * @see Object#hashCode()
+   * @see #nullSafeHashCode(Object[])
+   * @see #nullSafeHashCode(boolean[])
+   * @see #nullSafeHashCode(byte[])
+   * @see #nullSafeHashCode(char[])
+   * @see #nullSafeHashCode(double[])
+   * @see #nullSafeHashCode(float[])
+   * @see #nullSafeHashCode(int[])
+   * @see #nullSafeHashCode(long[])
+   * @see #nullSafeHashCode(short[])
+   */
+  public static int nullSafeHashCode(@Nullable Object obj) {
+    if (obj == null) {
+      return 0;
+    }
+    if (obj.getClass().isArray()) {
+      if (obj instanceof Object[]) {
+        return nullSafeHashCode((Object[]) obj);
+      }
+      if (obj instanceof boolean[]) {
+        return nullSafeHashCode((boolean[]) obj);
+      }
+      if (obj instanceof byte[]) {
+        return nullSafeHashCode((byte[]) obj);
+      }
+      if (obj instanceof char[]) {
+        return nullSafeHashCode((char[]) obj);
+      }
+      if (obj instanceof double[]) {
+        return nullSafeHashCode((double[]) obj);
+      }
+      if (obj instanceof float[]) {
+        return nullSafeHashCode((float[]) obj);
+      }
+      if (obj instanceof int[]) {
+        return nullSafeHashCode((int[]) obj);
+      }
+      if (obj instanceof long[]) {
+        return nullSafeHashCode((long[]) obj);
+      }
+      if (obj instanceof short[]) {
+        return nullSafeHashCode((short[]) obj);
+      }
+    }
+    return obj.hashCode();
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable Object[] array) {
+    if (array == null) {
+      return 0;
+    }
+    int hash = INITIAL_HASH;
+    for (Object element : array) {
+      hash = MULTIPLIER * hash + nullSafeHashCode(element);
+    }
+    return hash;
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable boolean[] array) {

Review Comment:
   ## Confusing overloading of methods
   
   Method ObjectUtils.nullSafeHashCode(..) could be confused with overloaded method [nullSafeHashCode](1), since dispatch depends on static types.
   
   [Show more details](https://github.com/apache/avro/security/code-scanning/2973)



##########
lang/java/avro/src/main/java/org/apache/avro/util/springframework/ConcurrentReferenceHashMap.java:
##########
@@ -0,0 +1,1112 @@
+/*
+ * Copyright 2002-2021 the original author or authors.
+ *
+ * Licensed 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
+ *
+ *      https://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.
+ */
+
+package org.apache.avro.util.springframework;
+
+import org.apache.avro.reflect.Nullable;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Array;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * A {@link ConcurrentHashMap} that uses {@link ReferenceType#SOFT soft} or
+ * {@linkplain ReferenceType#WEAK weak} references for both {@code keys} and {@code values}.
+ *
+ * <p>This class can be used as an alternative to
+ * {@code Collections.synchronizedMap(new WeakHashMap<K, Reference<V>>())} in order to
+ * support better performance when accessed concurrently. This implementation follows the
+ * same design constraints as {@link ConcurrentHashMap} with the exception that
+ * {@code null} values and {@code null} keys are supported.
+ *
+ * <p><b>NOTE:</b> The use of references means that there is no guarantee that items
+ * placed into the map will be subsequently available. The garbage collector may discard
+ * references at any time, so it may appear that an unknown thread is silently removing
+ * entries.
+ *
+ * <p>If not explicitly specified, this implementation will use
+ * {@linkplain SoftReference soft entry references}.
+ *
+ * @param <K> the key type
+ * @param <V> the value type
+ * @author Phillip Webb
+ * @author Juergen Hoeller
+ * @since 3.2
+ */
+public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> {
+
+  private static final int DEFAULT_INITIAL_CAPACITY = 16;
+
+  private static final float DEFAULT_LOAD_FACTOR = 0.75f;
+
+  private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
+
+  private static final ReferenceType DEFAULT_REFERENCE_TYPE = ReferenceType.SOFT;
+
+  private static final int MAXIMUM_CONCURRENCY_LEVEL = 1 << 16;
+
+  private static final int MAXIMUM_SEGMENT_SIZE = 1 << 30;
+
+
+  /**
+   * Array of segments indexed using the high order bits from the hash.
+   */
+  private final Segment[] segments;
+
+  /**
+   * When the average number of references per table exceeds this value resize will be attempted.
+   */
+  private final float loadFactor;
+
+  /**
+   * The reference type: SOFT or WEAK.
+   */
+  private final ReferenceType referenceType;
+
+  /**
+   * The shift value used to calculate the size of the segments array and an index from the hash.
+   */
+  private final int shift;
+
+  /**
+   * Late binding entry set.
+   */
+  @Nullable
+  private volatile Set<Map.Entry<K, V>> entrySet;
+
+
+  /**
+   * Create a new {@code ConcurrentReferenceHashMap} instance.
+   */
+  public ConcurrentReferenceHashMap() {
+    this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL, DEFAULT_REFERENCE_TYPE);
+  }
+
+  /**
+   * Create a new {@code ConcurrentReferenceHashMap} instance.
+   *
+   * @param initialCapacity the initial capacity of the map
+   */
+  public ConcurrentReferenceHashMap(int initialCapacity) {
+    this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL, DEFAULT_REFERENCE_TYPE);
+  }
+
+  /**
+   * Create a new {@code ConcurrentReferenceHashMap} instance.
+   *
+   * @param initialCapacity the initial capacity of the map
+   * @param loadFactor      the load factor. When the average number of references per table
+   *                        exceeds this value resize will be attempted
+   */
+  public ConcurrentReferenceHashMap(int initialCapacity, float loadFactor) {
+    this(initialCapacity, loadFactor, DEFAULT_CONCURRENCY_LEVEL, DEFAULT_REFERENCE_TYPE);
+  }
+
+  /**
+   * Create a new {@code ConcurrentReferenceHashMap} instance.
+   *
+   * @param initialCapacity  the initial capacity of the map
+   * @param concurrencyLevel the expected number of threads that will concurrently
+   *                         write to the map
+   */
+  public ConcurrentReferenceHashMap(int initialCapacity, int concurrencyLevel) {
+    this(initialCapacity, DEFAULT_LOAD_FACTOR, concurrencyLevel, DEFAULT_REFERENCE_TYPE);
+  }
+
+  /**
+   * Create a new {@code ConcurrentReferenceHashMap} instance.
+   *
+   * @param initialCapacity the initial capacity of the map
+   * @param referenceType   the reference type used for entries (soft or weak)
+   */
+  public ConcurrentReferenceHashMap(int initialCapacity, ReferenceType referenceType) {
+    this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL, referenceType);
+  }
+
+  /**
+   * Create a new {@code ConcurrentReferenceHashMap} instance.
+   *
+   * @param initialCapacity  the initial capacity of the map
+   * @param loadFactor       the load factor. When the average number of references per
+   *                         table exceeds this value, resize will be attempted.
+   * @param concurrencyLevel the expected number of threads that will concurrently
+   *                         write to the map
+   */
+  public ConcurrentReferenceHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) {
+    this(initialCapacity, loadFactor, concurrencyLevel, DEFAULT_REFERENCE_TYPE);
+  }
+
+  /**
+   * Create a new {@code ConcurrentReferenceHashMap} instance.
+   *
+   * @param initialCapacity  the initial capacity of the map
+   * @param loadFactor       the load factor. When the average number of references per
+   *                         table exceeds this value, resize will be attempted.
+   * @param concurrencyLevel the expected number of threads that will concurrently
+   *                         write to the map
+   * @param referenceType    the reference type used for entries (soft or weak)
+   */
+  @SuppressWarnings("unchecked")
+  public ConcurrentReferenceHashMap(
+    int initialCapacity, float loadFactor, int concurrencyLevel, ReferenceType referenceType) {
+
+    Assert.isTrue(initialCapacity >= 0, "Initial capacity must not be negative");
+    Assert.isTrue(loadFactor > 0f, "Load factor must be positive");
+    Assert.isTrue(concurrencyLevel > 0, "Concurrency level must be positive");
+    Assert.notNull(referenceType, "Reference type must not be null");
+    this.loadFactor = loadFactor;
+    this.shift = calculateShift(concurrencyLevel, MAXIMUM_CONCURRENCY_LEVEL);
+    int size = 1 << this.shift;
+    this.referenceType = referenceType;
+    int roundedUpSegmentCapacity = (int) ((initialCapacity + size - 1L) / size);
+    int initialSize = 1 << calculateShift(roundedUpSegmentCapacity, MAXIMUM_SEGMENT_SIZE);
+    Segment[] segments = (Segment[]) Array.newInstance(Segment.class, size);
+    int resizeThreshold = (int) (initialSize * getLoadFactor());
+    for (int i = 0; i < segments.length; i++) {
+      segments[i] = new Segment(initialSize, resizeThreshold);
+    }
+    this.segments = segments;
+  }
+
+
+  protected final float getLoadFactor() {
+    return this.loadFactor;
+  }
+
+  protected final int getSegmentsSize() {
+    return this.segments.length;
+  }
+
+  protected final Segment getSegment(int index) {
+    return this.segments[index];
+  }
+
+  /**
+   * Factory method that returns the {@link ReferenceManager}.
+   * This method will be called once for each {@link Segment}.
+   *
+   * @return a new reference manager
+   */
+  protected ReferenceManager createReferenceManager() {
+    return new ReferenceManager();
+  }
+
+  /**
+   * Get the hash for a given object, apply an additional hash function to reduce
+   * collisions. This implementation uses the same Wang/Jenkins algorithm as
+   * {@link ConcurrentHashMap}. Subclasses can override to provide alternative hashing.
+   *
+   * @param o the object to hash (may be null)
+   * @return the resulting hash code
+   */
+  protected int getHash(@Nullable Object o) {
+    int hash = (o != null ? o.hashCode() : 0);
+    hash += (hash << 15) ^ 0xffffcd7d;
+    hash ^= (hash >>> 10);
+    hash += (hash << 3);
+    hash ^= (hash >>> 6);
+    hash += (hash << 2) + (hash << 14);
+    hash ^= (hash >>> 16);
+    return hash;
+  }
+
+  @Override
+  @Nullable
+  public V get(@Nullable Object key) {
+    Reference<K, V> ref = getReference(key, Restructure.WHEN_NECESSARY);
+    Entry<K, V> entry = (ref != null ? ref.get() : null);
+    return (entry != null ? entry.getValue() : null);
+  }
+
+  @Override
+  @Nullable
+  public V getOrDefault(@Nullable Object key, @Nullable V defaultValue) {
+    Reference<K, V> ref = getReference(key, Restructure.WHEN_NECESSARY);
+    Entry<K, V> entry = (ref != null ? ref.get() : null);
+    return (entry != null ? entry.getValue() : defaultValue);
+  }
+
+  @Override
+  public boolean containsKey(@Nullable Object key) {
+    Reference<K, V> ref = getReference(key, Restructure.WHEN_NECESSARY);
+    Entry<K, V> entry = (ref != null ? ref.get() : null);
+    return (entry != null && ObjectUtils.nullSafeEquals(entry.getKey(), key));
+  }
+
+  /**
+   * Return a {@link Reference} to the {@link Entry} for the specified {@code key},
+   * or {@code null} if not found.
+   *
+   * @param key         the key (can be {@code null})
+   * @param restructure types of restructure allowed during this call
+   * @return the reference, or {@code null} if not found
+   */
+  @Nullable
+  protected final Reference<K, V> getReference(@Nullable Object key, Restructure restructure) {
+    int hash = getHash(key);
+    return getSegmentForHash(hash).getReference(key, hash, restructure);
+  }
+
+  @Override
+  @Nullable
+  public V put(@Nullable K key, @Nullable V value) {
+    return put(key, value, true);
+  }
+
+  @Override
+  @Nullable
+  public V putIfAbsent(@Nullable K key, @Nullable V value) {
+    return put(key, value, false);
+  }
+
+  @Nullable
+  private V put(@Nullable final K key, @Nullable final V value, final boolean overwriteExisting) {
+    return doTask(key, new Task<V>(TaskOption.RESTRUCTURE_BEFORE, TaskOption.RESIZE) {
+      @Override
+      @Nullable
+      protected V execute(@Nullable Reference<K, V> ref, @Nullable Entry<K, V> entry, @Nullable Entries<V> entries) {
+        if (entry != null) {
+          V oldValue = entry.getValue();
+          if (overwriteExisting) {
+            entry.setValue(value);
+          }
+          return oldValue;
+        }
+        Assert.state(entries != null, "No entries segment");
+        entries.add(value);
+        return null;
+      }
+    });
+  }
+
+  @Override
+  @Nullable
+  public V remove(@Nullable Object key) {
+    return doTask(key, new Task<V>(TaskOption.RESTRUCTURE_AFTER, TaskOption.SKIP_IF_EMPTY) {
+      @Override
+      @Nullable
+      protected V execute(@Nullable Reference<K, V> ref, @Nullable Entry<K, V> entry) {
+        if (entry != null) {
+          if (ref != null) {
+            ref.release();
+          }
+          return entry.value;
+        }
+        return null;
+      }
+    });
+  }
+
+  @Override
+  public boolean remove(@Nullable Object key, final @Nullable Object value) {
+    Boolean result = doTask(key, new Task<Boolean>(TaskOption.RESTRUCTURE_AFTER, TaskOption.SKIP_IF_EMPTY) {
+      @Override
+      protected Boolean execute(@Nullable Reference<K, V> ref, @Nullable Entry<K, V> entry) {
+        if (entry != null && ObjectUtils.nullSafeEquals(entry.getValue(), value)) {
+          if (ref != null) {
+            ref.release();
+          }
+          return true;
+        }
+        return false;
+      }
+    });
+    return (Boolean.TRUE.equals(result));
+  }
+
+  @Override
+  public boolean replace(@Nullable K key, final @Nullable V oldValue, final @Nullable V newValue) {
+    Boolean result = doTask(key, new Task<Boolean>(TaskOption.RESTRUCTURE_BEFORE, TaskOption.SKIP_IF_EMPTY) {
+      @Override
+      protected Boolean execute(@Nullable Reference<K, V> ref, @Nullable Entry<K, V> entry) {
+        if (entry != null && ObjectUtils.nullSafeEquals(entry.getValue(), oldValue)) {
+          entry.setValue(newValue);
+          return true;
+        }
+        return false;
+      }
+    });
+    return (Boolean.TRUE.equals(result));
+  }
+
+  @Override
+  @Nullable
+  public V replace(@Nullable K key, final @Nullable V value) {
+    return doTask(key, new Task<V>(TaskOption.RESTRUCTURE_BEFORE, TaskOption.SKIP_IF_EMPTY) {
+      @Override
+      @Nullable
+      protected V execute(@Nullable Reference<K, V> ref, @Nullable Entry<K, V> entry) {
+        if (entry != null) {
+          V oldValue = entry.getValue();
+          entry.setValue(value);
+          return oldValue;
+        }
+        return null;
+      }
+    });
+  }
+
+  @Override
+  public void clear() {
+    for (Segment segment : this.segments) {
+      segment.clear();
+    }
+  }
+
+  /**
+   * Remove any entries that have been garbage collected and are no longer referenced.
+   * Under normal circumstances garbage collected entries are automatically purged as
+   * items are added or removed from the Map. This method can be used to force a purge,
+   * and is useful when the Map is read frequently but updated less often.
+   */
+  public void purgeUnreferencedEntries() {
+    for (Segment segment : this.segments) {
+      segment.restructureIfNecessary(false);
+    }
+  }
+
+
+  @Override
+  public int size() {
+    int size = 0;
+    for (Segment segment : this.segments) {
+      size += segment.getCount();
+    }
+    return size;
+  }
+
+  @Override
+  public boolean isEmpty() {
+    for (Segment segment : this.segments) {
+      if (segment.getCount() > 0) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  @Override
+  public Set<Map.Entry<K, V>> entrySet() {
+    Set<Map.Entry<K, V>> entrySet = this.entrySet;
+    if (entrySet == null) {
+      entrySet = new EntrySet();
+      this.entrySet = entrySet;
+    }
+    return entrySet;
+  }
+
+  @Nullable
+  private <T> T doTask(@Nullable Object key, Task<T> task) {
+    int hash = getHash(key);
+    return getSegmentForHash(hash).doTask(hash, key, task);
+  }
+
+  private Segment getSegmentForHash(int hash) {
+    return this.segments[(hash >>> (32 - this.shift)) & (this.segments.length - 1)];
+  }
+
+  /**
+   * Calculate a shift value that can be used to create a power-of-two value between
+   * the specified maximum and minimum values.
+   *
+   * @param minimumValue the minimum value
+   * @param maximumValue the maximum value
+   * @return the calculated shift (use {@code 1 << shift} to obtain a value)
+   */
+  protected static int calculateShift(int minimumValue, int maximumValue) {
+    int shift = 0;
+    int value = 1;
+    while (value < minimumValue && value < maximumValue) {
+      value <<= 1;
+      shift++;
+    }
+    return shift;
+  }
+
+
+  /**
+   * Various reference types supported by this map.
+   */
+  public enum ReferenceType {
+
+    /**
+     * Use {@link SoftReference SoftReferences}.
+     */
+    SOFT,
+
+    /**
+     * Use {@link WeakReference WeakReferences}.
+     */
+    WEAK
+  }
+
+
+  /**
+   * A single segment used to divide the map to allow better concurrent performance.
+   */
+  @SuppressWarnings("serial")
+  protected final class Segment extends ReentrantLock {
+
+    private final ReferenceManager referenceManager;
+
+    private final int initialSize;
+
+    /**
+     * Array of references indexed using the low order bits from the hash.
+     * This property should only be set along with {@code resizeThreshold}.
+     */
+    private volatile Reference<K, V>[] references;
+
+    /**
+     * The total number of references contained in this segment. This includes chained
+     * references and references that have been garbage collected but not purged.
+     */
+    private final AtomicInteger count = new AtomicInteger();
+
+    /**
+     * The threshold when resizing of the references should occur. When {@code count}
+     * exceeds this value references will be resized.
+     */
+    private int resizeThreshold;
+
+    public Segment(int initialSize, int resizeThreshold) {
+      this.referenceManager = createReferenceManager();
+      this.initialSize = initialSize;
+      this.references = createReferenceArray(initialSize);
+      this.resizeThreshold = resizeThreshold;
+    }
+
+    @Nullable
+    public Reference<K, V> getReference(@Nullable Object key, int hash, Restructure restructure) {
+      if (restructure == Restructure.WHEN_NECESSARY) {
+        restructureIfNecessary(false);
+      }
+      if (this.count.get() == 0) {
+        return null;
+      }
+      // Use a local copy to protect against other threads writing
+      Reference<K, V>[] references = this.references;
+      int index = getIndex(hash, references);
+      Reference<K, V> head = references[index];
+      return findInChain(head, key, hash);
+    }
+
+    /**
+     * Apply an update operation to this segment.
+     * The segment will be locked during the update.
+     *
+     * @param hash the hash of the key
+     * @param key  the key
+     * @param task the update operation
+     * @return the result of the operation
+     */
+    @Nullable
+    public <T> T doTask(final int hash, @Nullable final Object key, final Task<T> task) {
+      boolean resize = task.hasOption(TaskOption.RESIZE);
+      if (task.hasOption(TaskOption.RESTRUCTURE_BEFORE)) {
+        restructureIfNecessary(resize);
+      }
+      if (task.hasOption(TaskOption.SKIP_IF_EMPTY) && this.count.get() == 0) {
+        return task.execute(null, null, null);
+      }
+      lock();
+      try {
+        final int index = getIndex(hash, this.references);
+        final Reference<K, V> head = this.references[index];
+        Reference<K, V> ref = findInChain(head, key, hash);
+        Entry<K, V> entry = (ref != null ? ref.get() : null);
+        Entries<V> entries = value -> {
+          @SuppressWarnings("unchecked")
+          Entry<K, V> newEntry = new Entry<>((K) key, value);
+          Reference<K, V> newReference = Segment.this.referenceManager.createReference(newEntry, hash, head);
+          Segment.this.references[index] = newReference;
+          Segment.this.count.incrementAndGet();
+        };
+        return task.execute(ref, entry, entries);
+      } finally {
+        unlock();
+        if (task.hasOption(TaskOption.RESTRUCTURE_AFTER)) {
+          restructureIfNecessary(resize);
+        }
+      }
+    }
+
+    /**
+     * Clear all items from this segment.
+     */
+    public void clear() {
+      if (this.count.get() == 0) {
+        return;
+      }
+      lock();
+      try {
+        this.references = createReferenceArray(this.initialSize);
+        this.resizeThreshold = (int) (this.references.length * getLoadFactor());
+        this.count.set(0);
+      } finally {
+        unlock();
+      }
+    }
+
+    /**
+     * Restructure the underlying data structure when it becomes necessary. This
+     * method can increase the size of the references table as well as purge any
+     * references that have been garbage collected.
+     *
+     * @param allowResize if resizing is permitted
+     */
+    private void restructureIfNecessary(boolean allowResize) {
+      int currCount = this.count.get();
+      boolean needsResize = allowResize && (currCount > 0 && currCount >= this.resizeThreshold);
+      Reference<K, V> ref = this.referenceManager.pollForPurge();
+      if (ref != null || (needsResize)) {
+        restructure(allowResize, ref);
+      }
+    }
+
+    private void restructure(boolean allowResize, @Nullable Reference<K, V> ref) {
+      boolean needsResize;
+      lock();
+      try {
+        int countAfterRestructure = this.count.get();
+        Set<Reference<K, V>> toPurge = Collections.emptySet();
+        if (ref != null) {
+          toPurge = new HashSet<>();
+          while (ref != null) {
+            toPurge.add(ref);
+            ref = this.referenceManager.pollForPurge();
+          }
+        }
+        countAfterRestructure -= toPurge.size();
+
+        // Recalculate taking into account count inside lock and items that
+        // will be purged
+        needsResize = (countAfterRestructure > 0 && countAfterRestructure >= this.resizeThreshold);
+        boolean resizing = false;
+        int restructureSize = this.references.length;
+        if (allowResize && needsResize && restructureSize < MAXIMUM_SEGMENT_SIZE) {
+          restructureSize <<= 1;
+          resizing = true;
+        }
+
+        // Either create a new table or reuse the existing one
+        Reference<K, V>[] restructured =
+          (resizing ? createReferenceArray(restructureSize) : this.references);
+
+        // Restructure
+        for (int i = 0; i < this.references.length; i++) {
+          ref = this.references[i];
+          if (!resizing) {
+            restructured[i] = null;
+          }
+          while (ref != null) {
+            if (!toPurge.contains(ref)) {
+              Entry<K, V> entry = ref.get();
+              if (entry != null) {
+                int index = getIndex(ref.getHash(), restructured);
+                restructured[index] = this.referenceManager.createReference(
+                  entry, ref.getHash(), restructured[index]);
+              }
+            }
+            ref = ref.getNext();
+          }
+        }
+
+        // Replace volatile members
+        if (resizing) {
+          this.references = restructured;
+          this.resizeThreshold = (int) (this.references.length * getLoadFactor());
+        }
+        this.count.set(Math.max(countAfterRestructure, 0));
+      } finally {
+        unlock();
+      }
+    }
+
+    @Nullable
+    private Reference<K, V> findInChain(Reference<K, V> ref, @Nullable Object key, int hash) {
+      Reference<K, V> currRef = ref;
+      while (currRef != null) {
+        if (currRef.getHash() == hash) {
+          Entry<K, V> entry = currRef.get();
+          if (entry != null) {
+            K entryKey = entry.getKey();
+            if (ObjectUtils.nullSafeEquals(entryKey, key)) {
+              return currRef;
+            }
+          }
+        }
+        currRef = currRef.getNext();
+      }
+      return null;
+    }
+
+    @SuppressWarnings({"unchecked"})
+    private Reference<K, V>[] createReferenceArray(int size) {
+      return new Reference[size];
+    }
+
+    private int getIndex(int hash, Reference<K, V>[] references) {
+      return (hash & (references.length - 1));
+    }
+
+    /**
+     * Return the size of the current references array.
+     */
+    public int getSize() {
+      return this.references.length;
+    }
+
+    /**
+     * Return the total number of references in this segment.
+     */
+    public int getCount() {
+      return this.count.get();
+    }
+  }
+
+
+  /**
+   * A reference to an {@link Entry} contained in the map. Implementations are usually
+   * wrappers around specific Java reference implementations (e.g., {@link SoftReference}).
+   *
+   * @param <K> the key type
+   * @param <V> the value type
+   */
+  protected interface Reference<K, V> {
+
+    /**
+     * Return the referenced entry, or {@code null} if the entry is no longer available.
+     */
+    @Nullable
+    Entry<K, V> get();
+
+    /**
+     * Return the hash for the reference.
+     */
+    int getHash();
+
+    /**
+     * Return the next reference in the chain, or {@code null} if none.
+     */
+    @Nullable
+    Reference<K, V> getNext();
+
+    /**
+     * Release this entry and ensure that it will be returned from
+     * {@code ReferenceManager#pollForPurge()}.
+     */
+    void release();
+  }
+
+
+  /**
+   * A single map entry.
+   *
+   * @param <K> the key type
+   * @param <V> the value type
+   */
+  protected static final class Entry<K, V> implements Map.Entry<K, V> {
+
+    @Nullable
+    private final K key;
+
+    @Nullable
+    private volatile V value;
+
+    public Entry(@Nullable K key, @Nullable V value) {
+      this.key = key;
+      this.value = value;
+    }
+
+    @Override
+    @Nullable
+    public K getKey() {
+      return this.key;
+    }
+
+    @Override
+    @Nullable
+    public V getValue() {
+      return this.value;
+    }
+
+    @Override
+    @Nullable
+    public V setValue(@Nullable V value) {
+      V previous = this.value;
+      this.value = value;
+      return previous;
+    }
+
+    @Override
+    public String toString() {
+      return (this.key + "=" + this.value);
+    }
+
+    @Override
+    @SuppressWarnings("rawtypes")
+    public boolean equals(@Nullable Object other) {
+      if (this == other) {
+        return true;
+      }
+      if (!(other instanceof Map.Entry)) {
+        return false;
+      }
+      Map.Entry otherEntry = (Map.Entry) other;
+      return (ObjectUtils.nullSafeEquals(getKey(), otherEntry.getKey()) &&
+        ObjectUtils.nullSafeEquals(getValue(), otherEntry.getValue()));
+    }
+
+    @Override
+    public int hashCode() {
+      return (ObjectUtils.nullSafeHashCode(this.key) ^ ObjectUtils.nullSafeHashCode(this.value));
+    }
+  }
+
+
+  /**
+   * A task that can be {@link Segment#doTask run} against a {@link Segment}.
+   */
+  private abstract class Task<T> {

Review Comment:
   ## Inner class could be static
   
   Task should be made static, since the enclosing instance is not used.
   
   [Show more details](https://github.com/apache/avro/security/code-scanning/2967)



##########
lang/java/avro/src/main/java/org/apache/avro/util/springframework/ObjectUtils.java:
##########
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2002-2021 the original author or authors.
+ *
+ * Licensed 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
+ *
+ *      https://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.
+ */
+
+package org.apache.avro.util.springframework;
+
+import org.apache.avro.reflect.Nullable;
+import org.apache.avro.util.ClassUtils;
+
+import java.util.Arrays;
+
+/**
+ * Miscellaneous object utility methods.
+ *
+ * <p>Mainly for internal use within the framework.
+ *
+ * <p>Thanks to Alex Ruiz for contributing several enhancements to this class!
+ *
+ * @author Juergen Hoeller
+ * @author Keith Donald
+ * @author Rod Johnson
+ * @author Rob Harrop
+ * @author Chris Beams
+ * @author Sam Brannen
+ * @see ClassUtils
+ * see CollectionUtils
+ * see StringUtils
+ * @since 19.03.2004
+ */
+class ObjectUtils {
+  private ObjectUtils() {
+  }
+
+  private static final int INITIAL_HASH = 7;
+  private static final int MULTIPLIER = 31;
+
+
+  /**
+   * Determine whether the given array is empty:
+   * i.e. {@code null} or of zero length.
+   *
+   * @param array the array to check
+   */
+  public static boolean isEmpty(@Nullable Object[] array) {
+    return (array == null || array.length == 0);
+  }
+
+
+  //---------------------------------------------------------------------
+  // Convenience methods for content-based equality/hash-code handling
+  //---------------------------------------------------------------------
+
+  /**
+   * Determine if the given objects are equal, returning {@code true} if
+   * both are {@code null} or {@code false} if only one is {@code null}.
+   * <p>Compares arrays with {@code Arrays.equals}, performing an equality
+   * check based on the array elements rather than the array reference.
+   *
+   * @param o1 first Object to compare
+   * @param o2 second Object to compare
+   * @return whether the given objects are equal
+   * @see Object#equals(Object)
+   * @see Arrays#equals
+   */
+  public static boolean nullSafeEquals(@Nullable Object o1, @Nullable Object o2) {
+    if (o1 == o2) {
+      return true;
+    }
+    if (o1 == null || o2 == null) {
+      return false;
+    }
+    if (o1.equals(o2)) {
+      return true;
+    }
+    if (o1.getClass().isArray() && o2.getClass().isArray()) {
+      return arrayEquals(o1, o2);
+    }
+    return false;
+  }
+
+  /**
+   * Compare the given arrays with {@code Arrays.equals}, performing an equality
+   * check based on the array elements rather than the array reference.
+   *
+   * @param o1 first array to compare
+   * @param o2 second array to compare
+   * @return whether the given objects are equal
+   * @see #nullSafeEquals(Object, Object)
+   * @see Arrays#equals
+   */
+  private static boolean arrayEquals(Object o1, Object o2) {
+    if (o1 instanceof Object[] && o2 instanceof Object[]) {
+      return Arrays.equals((Object[]) o1, (Object[]) o2);
+    }
+    if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
+      return Arrays.equals((boolean[]) o1, (boolean[]) o2);
+    }
+    if (o1 instanceof byte[] && o2 instanceof byte[]) {
+      return Arrays.equals((byte[]) o1, (byte[]) o2);
+    }
+    if (o1 instanceof char[] && o2 instanceof char[]) {
+      return Arrays.equals((char[]) o1, (char[]) o2);
+    }
+    if (o1 instanceof double[] && o2 instanceof double[]) {
+      return Arrays.equals((double[]) o1, (double[]) o2);
+    }
+    if (o1 instanceof float[] && o2 instanceof float[]) {
+      return Arrays.equals((float[]) o1, (float[]) o2);
+    }
+    if (o1 instanceof int[] && o2 instanceof int[]) {
+      return Arrays.equals((int[]) o1, (int[]) o2);
+    }
+    if (o1 instanceof long[] && o2 instanceof long[]) {
+      return Arrays.equals((long[]) o1, (long[]) o2);
+    }
+    if (o1 instanceof short[] && o2 instanceof short[]) {
+      return Arrays.equals((short[]) o1, (short[]) o2);
+    }
+    return false;
+  }
+
+  /**
+   * Return as hash code for the given object; typically the value of
+   * {@code Object#hashCode()}}. If the object is an array,
+   * this method will delegate to any of the {@code nullSafeHashCode}
+   * methods for arrays in this class. If the object is {@code null},
+   * this method returns 0.
+   *
+   * @see Object#hashCode()
+   * @see #nullSafeHashCode(Object[])
+   * @see #nullSafeHashCode(boolean[])
+   * @see #nullSafeHashCode(byte[])
+   * @see #nullSafeHashCode(char[])
+   * @see #nullSafeHashCode(double[])
+   * @see #nullSafeHashCode(float[])
+   * @see #nullSafeHashCode(int[])
+   * @see #nullSafeHashCode(long[])
+   * @see #nullSafeHashCode(short[])
+   */
+  public static int nullSafeHashCode(@Nullable Object obj) {
+    if (obj == null) {
+      return 0;
+    }
+    if (obj.getClass().isArray()) {
+      if (obj instanceof Object[]) {
+        return nullSafeHashCode((Object[]) obj);
+      }
+      if (obj instanceof boolean[]) {
+        return nullSafeHashCode((boolean[]) obj);
+      }
+      if (obj instanceof byte[]) {
+        return nullSafeHashCode((byte[]) obj);
+      }
+      if (obj instanceof char[]) {
+        return nullSafeHashCode((char[]) obj);
+      }
+      if (obj instanceof double[]) {
+        return nullSafeHashCode((double[]) obj);
+      }
+      if (obj instanceof float[]) {
+        return nullSafeHashCode((float[]) obj);
+      }
+      if (obj instanceof int[]) {
+        return nullSafeHashCode((int[]) obj);
+      }
+      if (obj instanceof long[]) {
+        return nullSafeHashCode((long[]) obj);
+      }
+      if (obj instanceof short[]) {
+        return nullSafeHashCode((short[]) obj);
+      }
+    }
+    return obj.hashCode();
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable Object[] array) {
+    if (array == null) {
+      return 0;
+    }
+    int hash = INITIAL_HASH;
+    for (Object element : array) {
+      hash = MULTIPLIER * hash + nullSafeHashCode(element);
+    }
+    return hash;
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable boolean[] array) {
+    if (array == null) {
+      return 0;
+    }
+    int hash = INITIAL_HASH;
+    for (boolean element : array) {
+      hash = MULTIPLIER * hash + Boolean.hashCode(element);
+    }
+    return hash;
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable byte[] array) {
+    if (array == null) {
+      return 0;
+    }
+    int hash = INITIAL_HASH;
+    for (byte element : array) {
+      hash = MULTIPLIER * hash + element;
+    }
+    return hash;
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable char[] array) {

Review Comment:
   ## Confusing overloading of methods
   
   Method ObjectUtils.nullSafeHashCode(..) could be confused with overloaded method [nullSafeHashCode](1), since dispatch depends on static types.
   
   [Show more details](https://github.com/apache/avro/security/code-scanning/2971)



##########
lang/java/avro/src/main/java/org/apache/avro/util/springframework/ObjectUtils.java:
##########
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2002-2021 the original author or authors.
+ *
+ * Licensed 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
+ *
+ *      https://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.
+ */
+
+package org.apache.avro.util.springframework;
+
+import org.apache.avro.reflect.Nullable;
+import org.apache.avro.util.ClassUtils;
+
+import java.util.Arrays;
+
+/**
+ * Miscellaneous object utility methods.
+ *
+ * <p>Mainly for internal use within the framework.
+ *
+ * <p>Thanks to Alex Ruiz for contributing several enhancements to this class!
+ *
+ * @author Juergen Hoeller
+ * @author Keith Donald
+ * @author Rod Johnson
+ * @author Rob Harrop
+ * @author Chris Beams
+ * @author Sam Brannen
+ * @see ClassUtils
+ * see CollectionUtils
+ * see StringUtils
+ * @since 19.03.2004
+ */
+class ObjectUtils {
+  private ObjectUtils() {
+  }
+
+  private static final int INITIAL_HASH = 7;
+  private static final int MULTIPLIER = 31;
+
+
+  /**
+   * Determine whether the given array is empty:
+   * i.e. {@code null} or of zero length.
+   *
+   * @param array the array to check
+   */
+  public static boolean isEmpty(@Nullable Object[] array) {
+    return (array == null || array.length == 0);
+  }
+
+
+  //---------------------------------------------------------------------
+  // Convenience methods for content-based equality/hash-code handling
+  //---------------------------------------------------------------------
+
+  /**
+   * Determine if the given objects are equal, returning {@code true} if
+   * both are {@code null} or {@code false} if only one is {@code null}.
+   * <p>Compares arrays with {@code Arrays.equals}, performing an equality
+   * check based on the array elements rather than the array reference.
+   *
+   * @param o1 first Object to compare
+   * @param o2 second Object to compare
+   * @return whether the given objects are equal
+   * @see Object#equals(Object)
+   * @see Arrays#equals
+   */
+  public static boolean nullSafeEquals(@Nullable Object o1, @Nullable Object o2) {
+    if (o1 == o2) {
+      return true;
+    }
+    if (o1 == null || o2 == null) {
+      return false;
+    }
+    if (o1.equals(o2)) {
+      return true;
+    }
+    if (o1.getClass().isArray() && o2.getClass().isArray()) {
+      return arrayEquals(o1, o2);
+    }
+    return false;
+  }
+
+  /**
+   * Compare the given arrays with {@code Arrays.equals}, performing an equality
+   * check based on the array elements rather than the array reference.
+   *
+   * @param o1 first array to compare
+   * @param o2 second array to compare
+   * @return whether the given objects are equal
+   * @see #nullSafeEquals(Object, Object)
+   * @see Arrays#equals
+   */
+  private static boolean arrayEquals(Object o1, Object o2) {
+    if (o1 instanceof Object[] && o2 instanceof Object[]) {
+      return Arrays.equals((Object[]) o1, (Object[]) o2);
+    }
+    if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
+      return Arrays.equals((boolean[]) o1, (boolean[]) o2);
+    }
+    if (o1 instanceof byte[] && o2 instanceof byte[]) {
+      return Arrays.equals((byte[]) o1, (byte[]) o2);
+    }
+    if (o1 instanceof char[] && o2 instanceof char[]) {
+      return Arrays.equals((char[]) o1, (char[]) o2);
+    }
+    if (o1 instanceof double[] && o2 instanceof double[]) {
+      return Arrays.equals((double[]) o1, (double[]) o2);
+    }
+    if (o1 instanceof float[] && o2 instanceof float[]) {
+      return Arrays.equals((float[]) o1, (float[]) o2);
+    }
+    if (o1 instanceof int[] && o2 instanceof int[]) {
+      return Arrays.equals((int[]) o1, (int[]) o2);
+    }
+    if (o1 instanceof long[] && o2 instanceof long[]) {
+      return Arrays.equals((long[]) o1, (long[]) o2);
+    }
+    if (o1 instanceof short[] && o2 instanceof short[]) {
+      return Arrays.equals((short[]) o1, (short[]) o2);
+    }
+    return false;
+  }
+
+  /**
+   * Return as hash code for the given object; typically the value of
+   * {@code Object#hashCode()}}. If the object is an array,
+   * this method will delegate to any of the {@code nullSafeHashCode}
+   * methods for arrays in this class. If the object is {@code null},
+   * this method returns 0.
+   *
+   * @see Object#hashCode()
+   * @see #nullSafeHashCode(Object[])
+   * @see #nullSafeHashCode(boolean[])
+   * @see #nullSafeHashCode(byte[])
+   * @see #nullSafeHashCode(char[])
+   * @see #nullSafeHashCode(double[])
+   * @see #nullSafeHashCode(float[])
+   * @see #nullSafeHashCode(int[])
+   * @see #nullSafeHashCode(long[])
+   * @see #nullSafeHashCode(short[])
+   */
+  public static int nullSafeHashCode(@Nullable Object obj) {
+    if (obj == null) {
+      return 0;
+    }
+    if (obj.getClass().isArray()) {
+      if (obj instanceof Object[]) {
+        return nullSafeHashCode((Object[]) obj);
+      }
+      if (obj instanceof boolean[]) {
+        return nullSafeHashCode((boolean[]) obj);
+      }
+      if (obj instanceof byte[]) {
+        return nullSafeHashCode((byte[]) obj);
+      }
+      if (obj instanceof char[]) {
+        return nullSafeHashCode((char[]) obj);
+      }
+      if (obj instanceof double[]) {
+        return nullSafeHashCode((double[]) obj);
+      }
+      if (obj instanceof float[]) {
+        return nullSafeHashCode((float[]) obj);
+      }
+      if (obj instanceof int[]) {
+        return nullSafeHashCode((int[]) obj);
+      }
+      if (obj instanceof long[]) {
+        return nullSafeHashCode((long[]) obj);
+      }
+      if (obj instanceof short[]) {
+        return nullSafeHashCode((short[]) obj);
+      }
+    }
+    return obj.hashCode();
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable Object[] array) {
+    if (array == null) {
+      return 0;
+    }
+    int hash = INITIAL_HASH;
+    for (Object element : array) {
+      hash = MULTIPLIER * hash + nullSafeHashCode(element);
+    }
+    return hash;
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable boolean[] array) {
+    if (array == null) {
+      return 0;
+    }
+    int hash = INITIAL_HASH;
+    for (boolean element : array) {
+      hash = MULTIPLIER * hash + Boolean.hashCode(element);
+    }
+    return hash;
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable byte[] array) {
+    if (array == null) {
+      return 0;
+    }
+    int hash = INITIAL_HASH;
+    for (byte element : array) {
+      hash = MULTIPLIER * hash + element;
+    }
+    return hash;
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable char[] array) {
+    if (array == null) {
+      return 0;
+    }
+    int hash = INITIAL_HASH;
+    for (char element : array) {
+      hash = MULTIPLIER * hash + element;
+    }
+    return hash;
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable double[] array) {

Review Comment:
   ## Confusing overloading of methods
   
   Method ObjectUtils.nullSafeHashCode(..) could be confused with overloaded method [nullSafeHashCode](1), since dispatch depends on static types.
   
   [Show more details](https://github.com/apache/avro/security/code-scanning/2970)



##########
lang/java/avro/src/main/java/org/apache/avro/util/springframework/ObjectUtils.java:
##########
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2002-2021 the original author or authors.
+ *
+ * Licensed 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
+ *
+ *      https://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.
+ */
+
+package org.apache.avro.util.springframework;
+
+import org.apache.avro.reflect.Nullable;
+import org.apache.avro.util.ClassUtils;
+
+import java.util.Arrays;
+
+/**
+ * Miscellaneous object utility methods.
+ *
+ * <p>Mainly for internal use within the framework.
+ *
+ * <p>Thanks to Alex Ruiz for contributing several enhancements to this class!
+ *
+ * @author Juergen Hoeller
+ * @author Keith Donald
+ * @author Rod Johnson
+ * @author Rob Harrop
+ * @author Chris Beams
+ * @author Sam Brannen
+ * @see ClassUtils
+ * see CollectionUtils
+ * see StringUtils
+ * @since 19.03.2004
+ */
+class ObjectUtils {
+  private ObjectUtils() {
+  }
+
+  private static final int INITIAL_HASH = 7;
+  private static final int MULTIPLIER = 31;
+
+
+  /**
+   * Determine whether the given array is empty:
+   * i.e. {@code null} or of zero length.
+   *
+   * @param array the array to check
+   */
+  public static boolean isEmpty(@Nullable Object[] array) {
+    return (array == null || array.length == 0);
+  }
+
+
+  //---------------------------------------------------------------------
+  // Convenience methods for content-based equality/hash-code handling
+  //---------------------------------------------------------------------
+
+  /**
+   * Determine if the given objects are equal, returning {@code true} if
+   * both are {@code null} or {@code false} if only one is {@code null}.
+   * <p>Compares arrays with {@code Arrays.equals}, performing an equality
+   * check based on the array elements rather than the array reference.
+   *
+   * @param o1 first Object to compare
+   * @param o2 second Object to compare
+   * @return whether the given objects are equal
+   * @see Object#equals(Object)
+   * @see Arrays#equals
+   */
+  public static boolean nullSafeEquals(@Nullable Object o1, @Nullable Object o2) {
+    if (o1 == o2) {
+      return true;
+    }
+    if (o1 == null || o2 == null) {
+      return false;
+    }
+    if (o1.equals(o2)) {
+      return true;
+    }
+    if (o1.getClass().isArray() && o2.getClass().isArray()) {
+      return arrayEquals(o1, o2);
+    }
+    return false;
+  }
+
+  /**
+   * Compare the given arrays with {@code Arrays.equals}, performing an equality
+   * check based on the array elements rather than the array reference.
+   *
+   * @param o1 first array to compare
+   * @param o2 second array to compare
+   * @return whether the given objects are equal
+   * @see #nullSafeEquals(Object, Object)
+   * @see Arrays#equals
+   */
+  private static boolean arrayEquals(Object o1, Object o2) {
+    if (o1 instanceof Object[] && o2 instanceof Object[]) {
+      return Arrays.equals((Object[]) o1, (Object[]) o2);
+    }
+    if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
+      return Arrays.equals((boolean[]) o1, (boolean[]) o2);
+    }
+    if (o1 instanceof byte[] && o2 instanceof byte[]) {
+      return Arrays.equals((byte[]) o1, (byte[]) o2);
+    }
+    if (o1 instanceof char[] && o2 instanceof char[]) {
+      return Arrays.equals((char[]) o1, (char[]) o2);
+    }
+    if (o1 instanceof double[] && o2 instanceof double[]) {
+      return Arrays.equals((double[]) o1, (double[]) o2);
+    }
+    if (o1 instanceof float[] && o2 instanceof float[]) {
+      return Arrays.equals((float[]) o1, (float[]) o2);
+    }
+    if (o1 instanceof int[] && o2 instanceof int[]) {
+      return Arrays.equals((int[]) o1, (int[]) o2);
+    }
+    if (o1 instanceof long[] && o2 instanceof long[]) {
+      return Arrays.equals((long[]) o1, (long[]) o2);
+    }
+    if (o1 instanceof short[] && o2 instanceof short[]) {
+      return Arrays.equals((short[]) o1, (short[]) o2);
+    }
+    return false;
+  }
+
+  /**
+   * Return as hash code for the given object; typically the value of
+   * {@code Object#hashCode()}}. If the object is an array,
+   * this method will delegate to any of the {@code nullSafeHashCode}
+   * methods for arrays in this class. If the object is {@code null},
+   * this method returns 0.
+   *
+   * @see Object#hashCode()
+   * @see #nullSafeHashCode(Object[])
+   * @see #nullSafeHashCode(boolean[])
+   * @see #nullSafeHashCode(byte[])
+   * @see #nullSafeHashCode(char[])
+   * @see #nullSafeHashCode(double[])
+   * @see #nullSafeHashCode(float[])
+   * @see #nullSafeHashCode(int[])
+   * @see #nullSafeHashCode(long[])
+   * @see #nullSafeHashCode(short[])
+   */
+  public static int nullSafeHashCode(@Nullable Object obj) {
+    if (obj == null) {
+      return 0;
+    }
+    if (obj.getClass().isArray()) {
+      if (obj instanceof Object[]) {
+        return nullSafeHashCode((Object[]) obj);
+      }
+      if (obj instanceof boolean[]) {
+        return nullSafeHashCode((boolean[]) obj);
+      }
+      if (obj instanceof byte[]) {
+        return nullSafeHashCode((byte[]) obj);
+      }
+      if (obj instanceof char[]) {
+        return nullSafeHashCode((char[]) obj);
+      }
+      if (obj instanceof double[]) {
+        return nullSafeHashCode((double[]) obj);
+      }
+      if (obj instanceof float[]) {
+        return nullSafeHashCode((float[]) obj);
+      }
+      if (obj instanceof int[]) {
+        return nullSafeHashCode((int[]) obj);
+      }
+      if (obj instanceof long[]) {
+        return nullSafeHashCode((long[]) obj);
+      }
+      if (obj instanceof short[]) {
+        return nullSafeHashCode((short[]) obj);
+      }
+    }
+    return obj.hashCode();
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable Object[] array) {
+    if (array == null) {
+      return 0;
+    }
+    int hash = INITIAL_HASH;
+    for (Object element : array) {
+      hash = MULTIPLIER * hash + nullSafeHashCode(element);
+    }
+    return hash;
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable boolean[] array) {
+    if (array == null) {
+      return 0;
+    }
+    int hash = INITIAL_HASH;
+    for (boolean element : array) {
+      hash = MULTIPLIER * hash + Boolean.hashCode(element);
+    }
+    return hash;
+  }
+
+  /**
+   * Return a hash code based on the contents of the specified array.
+   * If {@code array} is {@code null}, this method returns 0.
+   */
+  public static int nullSafeHashCode(@Nullable byte[] array) {

Review Comment:
   ## Confusing overloading of methods
   
   Method ObjectUtils.nullSafeHashCode(..) could be confused with overloaded method [nullSafeHashCode](1), since dispatch depends on static types.
   
   [Show more details](https://github.com/apache/avro/security/code-scanning/2972)



##########
lang/java/avro/src/main/java/org/apache/avro/util/springframework/ObjectUtils.java:
##########
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2002-2021 the original author or authors.
+ *
+ * Licensed 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
+ *
+ *      https://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.
+ */
+
+package org.apache.avro.util.springframework;
+
+import org.apache.avro.reflect.Nullable;
+import org.apache.avro.util.ClassUtils;
+
+import java.util.Arrays;
+
+/**
+ * Miscellaneous object utility methods.
+ *
+ * <p>Mainly for internal use within the framework.
+ *
+ * <p>Thanks to Alex Ruiz for contributing several enhancements to this class!
+ *
+ * @author Juergen Hoeller
+ * @author Keith Donald
+ * @author Rod Johnson
+ * @author Rob Harrop
+ * @author Chris Beams
+ * @author Sam Brannen
+ * @see ClassUtils
+ * see CollectionUtils
+ * see StringUtils
+ * @since 19.03.2004
+ */
+class ObjectUtils {
+  private ObjectUtils() {
+  }
+
+  private static final int INITIAL_HASH = 7;
+  private static final int MULTIPLIER = 31;
+
+
+  /**
+   * Determine whether the given array is empty:
+   * i.e. {@code null} or of zero length.
+   *
+   * @param array the array to check
+   */
+  public static boolean isEmpty(@Nullable Object[] array) {
+    return (array == null || array.length == 0);
+  }
+
+
+  //---------------------------------------------------------------------
+  // Convenience methods for content-based equality/hash-code handling
+  //---------------------------------------------------------------------
+
+  /**
+   * Determine if the given objects are equal, returning {@code true} if
+   * both are {@code null} or {@code false} if only one is {@code null}.
+   * <p>Compares arrays with {@code Arrays.equals}, performing an equality
+   * check based on the array elements rather than the array reference.
+   *
+   * @param o1 first Object to compare
+   * @param o2 second Object to compare
+   * @return whether the given objects are equal
+   * @see Object#equals(Object)
+   * @see Arrays#equals
+   */
+  public static boolean nullSafeEquals(@Nullable Object o1, @Nullable Object o2) {
+    if (o1 == o2) {
+      return true;
+    }
+    if (o1 == null || o2 == null) {
+      return false;
+    }
+    if (o1.equals(o2)) {
+      return true;
+    }
+    if (o1.getClass().isArray() && o2.getClass().isArray()) {
+      return arrayEquals(o1, o2);
+    }
+    return false;
+  }
+
+  /**
+   * Compare the given arrays with {@code Arrays.equals}, performing an equality
+   * check based on the array elements rather than the array reference.
+   *
+   * @param o1 first array to compare
+   * @param o2 second array to compare
+   * @return whether the given objects are equal
+   * @see #nullSafeEquals(Object, Object)
+   * @see Arrays#equals
+   */
+  private static boolean arrayEquals(Object o1, Object o2) {
+    if (o1 instanceof Object[] && o2 instanceof Object[]) {
+      return Arrays.equals((Object[]) o1, (Object[]) o2);
+    }
+    if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
+      return Arrays.equals((boolean[]) o1, (boolean[]) o2);
+    }
+    if (o1 instanceof byte[] && o2 instanceof byte[]) {
+      return Arrays.equals((byte[]) o1, (byte[]) o2);
+    }
+    if (o1 instanceof char[] && o2 instanceof char[]) {
+      return Arrays.equals((char[]) o1, (char[]) o2);
+    }
+    if (o1 instanceof double[] && o2 instanceof double[]) {
+      return Arrays.equals((double[]) o1, (double[]) o2);
+    }
+    if (o1 instanceof float[] && o2 instanceof float[]) {
+      return Arrays.equals((float[]) o1, (float[]) o2);
+    }
+    if (o1 instanceof int[] && o2 instanceof int[]) {
+      return Arrays.equals((int[]) o1, (int[]) o2);
+    }
+    if (o1 instanceof long[] && o2 instanceof long[]) {
+      return Arrays.equals((long[]) o1, (long[]) o2);
+    }
+    if (o1 instanceof short[] && o2 instanceof short[]) {
+      return Arrays.equals((short[]) o1, (short[]) o2);
+    }
+    return false;
+  }
+
+  /**
+   * Return as hash code for the given object; typically the value of
+   * {@code Object#hashCode()}}. If the object is an array,
+   * this method will delegate to any of the {@code nullSafeHashCode}
+   * methods for arrays in this class. If the object is {@code null},
+   * this method returns 0.
+   *
+   * @see Object#hashCode()
+   * @see #nullSafeHashCode(Object[])
+   * @see #nullSafeHashCode(boolean[])
+   * @see #nullSafeHashCode(byte[])
+   * @see #nullSafeHashCode(char[])
+   * @see #nullSafeHashCode(double[])
+   * @see #nullSafeHashCode(float[])
+   * @see #nullSafeHashCode(int[])
+   * @see #nullSafeHashCode(long[])
+   * @see #nullSafeHashCode(short[])
+   */
+  public static int nullSafeHashCode(@Nullable Object obj) {

Review Comment:
   ## Confusing overloading of methods
   
   Method ObjectUtils.nullSafeHashCode(..) could be confused with overloaded method [nullSafeHashCode](1), since dispatch depends on static types.
   Method ObjectUtils.nullSafeHashCode(..) could be confused with overloaded method [nullSafeHashCode](2), since dispatch depends on static types.
   Method ObjectUtils.nullSafeHashCode(..) could be confused with overloaded method [nullSafeHashCode](3), since dispatch depends on static types.
   
   [Show more details](https://github.com/apache/avro/security/code-scanning/2974)



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@avro.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org