纹理和基元

在我最近的博客文章Arrays.hashCode()与 DZone联合版本的评论中提出了一个有趣的问题。 Objects.hash() “。 该评论的作者建立了一些示例,这些示例与我的博客文章中使用的示例相似,并且显示出与我看到的结果不同的结果。 感谢评论作者抽出宝贵的时间来发表这篇文章,因为它引起了Java的细微差别,我认为这很值得写博客。

评论作者显示了以下有效的Java语句:

int[] arr = new int[]{1,2,3,4};
System.out.println(Arrays.hashCode(arr));
System.out.println(Objects.hash(1,2,3,4));
System.out.println(Arrays.hashCode(new Integer[]{new Integer(1),new Integer(2),new Integer(3),new Integer(4)}));
System.out.println(Objects.hash(new Integer(1),new Integer(2),new Integer(3),new Integer(4)));

该评论的作者提到,对于所有四个语句,运行刚显示的代码的结果都完全相同。 这与我的示例不同,在示例中,在原始int值数组上调用Arrays.hashCode(int [])的结果与在同一原始int值数组上调用Objects.hash(Object…)的结果不同。

对原始反馈评论的一个答复准确地指出,不能保证在不同JVM上生成的哈希码是相同的。 实际上, Object.hashCode()方法的Javadoc注释指出(我强调了 ):

  • 只要在Java应用程序执行期间同一对象上多次调用它,hashCode方法就必须一致地返回相同的整数,前提是不修改该对象的equals比较中使用的信息。 从一个应用程序的执行到同一应用程序的另一执行,此整数不必保持一致。
  • 如果根据equals(Object)方法两个对象相等,则在两个对象中的每个对象上调用hashCode方法必须产生相同的整数结果。

陈述了所有这些内容之后,为整数计算的哈希码通常在每次运行之间都是一致的。 原始评论者示例的输出都具有完全相同的值也很有趣。 尽管我可能不希望这些值与示例的值相匹配,但令人惊讶的是,评论者提供的所有示例都具有相同的答案。

反馈注释中提供的示例与我的示例之间的区别在于注释者的示例如何为原始int值数组调用Objects.hash(Object...)与我的示例如何调用Objects.hash(Object...)用于原始int值的数组。 在我的示例中,我将相同的本地数组传递给所有方法调用。 该注释者的示例将原始int值的显式数组传递给Arrays.hashCode(int[]) ,但将各个int元素传递给Objects.hash(Object...)而不是将数组传递给该后一种方法。 当我向注释者的示例集中添加另一个示例,该示例确实将原始int值数组传递给Objects.hash(Object...)方法时,我得到的生成的哈希码与所有其他哈希码不同。 接下来显示该增强的代码。

final int[] arr = new int[]{1,2,3,4};
out.println("Arrays.hashCode(int[]):              " + Arrays.hashCode(arr));
out.println("Objects.hash(int, int, int, int):    " + Objects.hash(1,2,3,4));
out.println("Objects.hash(int[]):                 " + Objects.hash(arr));
out.println("Objects.hashCode(Object):            " + Objects.hashCode(arr));
out.println("int[].hashCode():                    " + arr.hashCode());
out.println("Arrays.hashCode(Int, Int, Int, Int): " + Arrays.hashCode(new Integer[]{1,2,3,4}));
out.println("Objects.hash(Int, Int, Int, Int):    " + Objects.hash(1,2,3,4));

运行注释器提供的代码的经过修改和增强的版本会导致此输出(突出显示我添加的示例):

Arrays.hashCode(int[]):              955331
Objects.hash(int, int, int, int):    955331
Objects.hash(int[]):                 897913763
Objects.hashCode(Object):            897913732
int[].hashCode():                    897913732
Arrays.hashCode(Int, Int, Int, Int): 955331
Objects.hash(Int, Int, Int, Int):    955331

将输出与生成它的代码进行比较,可以看出,当将int值数组的元素传递给Arrays.hashCode(int[]) ,它与Objects.hash(Object...)生成相同的哈希码值Objects.hash(Object...)方法作为单个元素。 但是,我们还可以看到,当完整地传递原始int值的数组(作为单个数组而不是作为数组的单个元素)时, Objects.hash(Object...)方法生成了完全不同的哈希码。 我添加的其他两个示例(突出显示)是通过直接在数组上调用.hashCode()或通过Objects.hashCode获得等效的结果来显示原始int值数组上的“直接”哈希码。 (对象) 。 [这不是巧合, Objects.hash(Object...)为原始int值数组生成的哈希码比为原始int值数组生成的“直接”哈希码正好大31。 ]

所有这些都指向这里的真正问题:通常最好不要将原语数组传递给接受可变参数 (通告省略号 )的方法。 SonarSource规则浏览器 ( Java )在RSPEC-3878中提供了有关此内容的更多详细信息。 与规则描述特别相关的是与歧义有关的问题:“数组应该是一个对象还是对象的集合?”

刚刚提出的问题的答案是,当将原始int值数组传递给接受方法Objects.hash(Object...)的变量参数时, 整个数组将被视为单个 Object 。 相反,当将引用对象的数组(例如Integer )传递给相同的方法时,它将其视为与传递给数组的元素相同数量的对象。 下一个代码清单和相关输出证明了这一点。

package dustin.examples.hashcodes;import static java.lang.System.out;/*** Demonstrates the difference in handling of arrays by methods that* accept variable arguments (ellipsis) when the arrays have primitive* elements and when arrays have reference object elements.*/
public class ArraysDemos
{private static void printEllipsisContents(final Object ... objects){out.println("==> Ellipsis Object... - Variable Arguments (" + objects.length + " elements): " + objects.getClass() + " - " + objects);}private static void printArrayContents(final Object[] objects){out.println("==> Array Object[] - Variable Arguments (" + objects.length + " elements): " + objects.getClass() + " - " + objects);}private static void printArrayContents(final int[] integers){out.println("==> Array int[] - Variable Arguments (" + integers.length + " elements): " + integers.getClass() + " - " + integers);}public static void main(final String[] arguments){final int[] primitiveIntegers = ArraysCreator.createArrayOfInts();final Integer[] referenceIntegers = ArraysCreator.createArrayOfIntegers();out.println("\nint[]");printEllipsisContents(primitiveIntegers);printArrayContents(primitiveIntegers);out.println("\nInteger[]");printEllipsisContents(referenceIntegers);printArrayContents(referenceIntegers);}
}
int[]
==> Ellipsis Object... - Variable Arguments (1 elements): class [Ljava.lang.Object; - [Ljava.lang.Object;@2752f6e2
==> Array int[] - Variable Arguments (10 elements): class [I - [I@1cd072a9Integer[]
==> Ellipsis Object... - Variable Arguments (10 elements): class [Ljava.lang.Integer; - [Ljava.lang.Integer;@7c75222b
==> Array Object[] - Variable Arguments (10 elements): class [Ljava.lang.Integer; - [Ljava.lang.Integer;@7c75222b

刚刚显示的示例代码和相关的输出说明,期望变量参数的方法将传递给它的原始值数组视为单个元素数组 。 另一方面,相同的方法将通过引用对象类型传递给它的数组视为具有相同元素数的数组。

考虑到这一点,请返回哈希码生成示例,由Objects.hash(Object...)为原始int值数组生成的哈希码与由Arrays.hashCode(int[])生成的哈希码不同。 类似地,我们现在可以解释为什么对象引用数组导致相同的哈希码,而不管调用了哪种方法。

我之前提到过,由Objects.hash(Object)生成的哈希码比整个数组的“直接”哈希码高31并非巧合。 这并不奇怪,因为Objects.hash(Object...)的OpenJDK实现将Arrays.hashCode(Object[]) Objects.hash(Object...)委托给Arrays.hashCode(Object[]) ,该数组使用素数乘以31 ,然后乘以计算出的哈希码中的每个元素。 考虑到上述观察,由Objects.hash(Object...)为原始int值数组提供的哈希码值似乎正是该方法的实现将导致我们期望的结果:整个数组的直接哈希值加上31个质数。 当该哈希码方法仅循环一个元素时(传递给需要可变参数的方法的基元数组就是这种情况),其计算本质上是31 * 1 + <directHashValueOfOverallArray>

值得注意的是,即使参考对象数组的哈希码计算得出的结果与将元素传递给接受变量参数的方法时的结果相同,还是最好避免将参考对象数组传递给这样的对象。方法。 当发生这种情况时, javac编译器会提供此警告:“警告:对最后一个参数使用不精确的参数类型的varargs方法的非varargs调用”,并添加了有关解决此问题的潜在方法的这些有用的细节:“为varargs调用广播到对象”或“广播到Object []以进行非可变参数调用并禁止显示此警告”。 当然,对于JDK 8和更高版本,在将数组提供给需要可变参数的方法之前,以多种其他方式处理数组是相当简单的。

我在原始帖子 (及其DZone联合版本 )中添加了最后一段,以尝试快速解决此问题,但是我已经使用此帖子来更详细地表达此信息。 此处总结的经验教训可以概括为“对原始数组使用适当的重载Arrays.hashCode方法,而不是使用Objects.hash(Object...) ”和“ Favor Arrays.hashCode(Object[])对于引用类型,而不是使用Objects.hash(Object...) 。” 如果调用的方法“看到”的元素数量无论如何都是重要的,则更通用的准则是要警惕将原始值数组传递给需要Object类型变量参数的方法,并且要警惕传递引用数组指向期望可变参数的方法的对象,以避免编译器警告和模棱两可的警告。

翻译自: https://www.javacodegeeks.com/2018/09/java-subtlety-with-arrays-of-primitives-and-variable-arguments.html

纹理和基元

纹理和基元_Java的精妙之处,包括基元和变量参数数组相关推荐

  1. Java的精妙之处,包括基元和变量参数数组

    在我最近的博客文章Arrays.hashCode()与 DZone联合版本的评论中提出了一个有趣的问题. Objects.hash() ". 该评论的作者建立了一些示例,这些示例与我的博客文 ...

  2. 运动基元_Java更快地对基元数组进行排序?

    运动基元 看来,在不久的将来,Java中的原语排序数组可能会提高性能. 弗拉基米尔·雅罗斯拉夫斯基(Vladimir Yaroslavskiy)已在core-libs-dev邮件列表中发布了一条消息 ...

  3. 运动基元_开发人员的新分布式基元

    运动基元 面向对象的基元(进程内基元) 作为Java开发人员,我非常熟悉面向对象的概念,例如类,对象,继承,封装,多态性等.除了面向对象的概念之外,我还非常熟悉Java运行时.它提供的功能,如何调整它 ...

  4. 【转】3.7(译)构建Async同步基元,Part 7 AsyncReaderWriterLock

    传送门:异步编程系列目录-- 最近在学习.NET4.5关于"并行任务"的使用."并行任务"有自己的同步机制,没有显示给出类似如旧版本的:事件等待句柄.信号量.l ...

  5. 【转】3.6(译)构建Async同步基元,Part 6 AsyncLock

    传送门:异步编程系列目录-- 最近在学习.NET4.5关于"并行任务"的使用."并行任务"有自己的同步机制,没有显示给出类似如旧版本的:事件等待句柄.信号量.l ...

  6. 【转】3.5(译)构建Async同步基元,Part 5 AsyncSemaphore

    传送门:异步编程系列目录-- 最近在学习.NET4.5关于"并行任务"的使用."并行任务"有自己的同步机制,没有显示给出类似如旧版本的:事件等待句柄.信号量.l ...

  7. 【转】3.4(译)构建Async同步基元,Part 4 AsyncBarrier

    传送门:异步编程系列目录-- 最近在学习.NET4.5关于"并行任务"的使用."并行任务"有自己的同步机制,没有显示给出类似如旧版本的:事件等待句柄.信号量.l ...

  8. 【转】3.3(译)构建Async同步基元,Part 3 AsyncCountdownEvent

    传送门:异步编程系列目录-- 最近在学习.NET4.5关于"并行任务"的使用."并行任务"有自己的同步机制,没有显示给出类似如旧版本的:事件等待句柄.信号量.l ...

  9. 【转】3.2(译)构建Async同步基元,Part 2 AsyncAutoResetEvent

    传送门:异步编程系列目录-- 最近在学习.NET4.5关于"并行任务"的使用."并行任务"有自己的同步机制,没有显示给出类似如旧版本的:事件等待句柄.信号量.l ...

最新文章

  1. 如何成为python 数据分析师_如何成为一名真正的数据分析师或者数据工程师
  2. 对数组中的数字 1 和 2 进行排序,使得数字 1、2 分别位于前、后部分
  3. MyBatis 3在XML文件中处理大于号小于号()的方法(转)
  4. ElementUI el-table 在flex下的宽度自适应问题
  5. php-fpm linux 权限,nginx/php-fpm及网站目录的权限设置
  6. 最长递增子序列Python解法
  7. 手把手教你部署VSAN见证虚拟设备 (Cormac)
  8. Visual Studio 和 Team Foundation Server 产品维护及周期策略
  9. 数据结构 - 多路搜索树(2-3树、b树、b+树、b*树)
  10. python调用java文件_Python程序中调用Java代码的实践
  11. 题目1022:游船出租(结构体使用)
  12. linux更改root密码_如何在Linux中更改root密码
  13. 用PLC和触摸屏设计用户可编程的控制项目
  14. 剩余电流动作继电器在浴室中的应用
  15. 2014最受欢迎的8款免费PSD线框图工具
  16. Kanzi学习-待继续更新
  17. candence pcb走线等长_PCB走线角度选择 - PCB Layout 跳坑指南 - 吴川斌的博客
  18. IE6 WEB开发调试插件:IE Developer Toolbar
  19. bzoj3165 segment 超哥线段树
  20. 未来感html5模板,天际ME5极具未来感设计效果图曝光

热门文章

  1. 8.13模拟:分治二分倍增快速幂
  2. P2048 [NOI2010] 超级钢琴(RMQ 贪心)
  3. P7962-[NOIP2021]方差【dp,差分】
  4. P5369-[PKUSC2018]最大前缀和【状压dp】
  5. P6222-「P6156 简单题」加强版【莫比乌斯反演】
  6. POJ2942-Knights of the Round Table【tarjan】
  7. jzoj100042-保留道路【最小生成树,图论】
  8. 照看小猫(nowcoder 217602)
  9. Network of Schools POJ - 1236 tarjan强连通分量缩点
  10. 10、java中文件的抽象表示