Java 中的为什么SIZE仅为整数和长整数@Native?

我了解注释的用法。
指示可以引用定义常量值的字段
来自本机代码。注释可被以下工具用作提示:
生成本机头文件以确定头文件是否
必需的,如果是,它应该包含什么声明。
然而,在阅读Java源代码时,我注意到在类@NativeInteger中,Long常量是SIZE而不是浮点、字节、双、短和字符。
请注意,大小常量表示用于表示实际值的位数。

public static final int SIZE = 8;//Byte
public static final int SIZE = 16;//Character
public static final int SIZE = 16;//Short
public static final int SIZE = 32;//Float
@Native public static final int SIZE = 32;//Integer
@Native public static final int SIZE = 64;//Long
public static final int SIZE = 64;//Double

编辑:我刚注意到这也适用于同一类的@NativeMAX_VALUE
编辑2:我有空余时间对此做一些研究,并查看long、float等类的头文件。我希望找出常量不存在于其他头文件中,但不幸的是,它们存在。
static const jint SIZE = 8L;//java/lang/Byte.h
static const jint SIZE = 16L;//java/lang/Character.h
static const jint SIZE = 16L;//java/lang/Short.h
static const jint SIZE = 32L;//java/lang/Float.h
static const jint SIZE = 32L;//java/lang/Integer.h
static const jint SIZE = 64L;//java/lang/Double.h
static const jint SIZE = 64L;//java/lang/Long.h

为什么只有@native的整型和长型的大小常量?

 

 

最佳答案

TLDR:跳到结论
为什么只有@native的整型和长型的大小常量?
@Native
我在邮件列表上搜索了一下。我发现了一些有趣的东西。
一开始是注释(12javax.tools.annotation.ForceNativeHeader
被介绍给
在类上触发javah。
它由com.sun.tools.javac.processing.NativeapiVisitor使用。通过查看代码,我们可以看到,如果类声明了一些本机方法,或者如果类被注释了,则会生成本机头。
后来,此注释被重命名为@ForceNativeHeader12)。
然后(尤其是this annotation was added to several typesInteger)带有一个相互关联的注释:

/* No native methods here, but the constants are needed in the supporting JNI code */
@GenerateNativeHeader
public final class Long extends Number implements Comparable<Long> {...

但是通过添加这个注释,它可以将基础模块中的Long添加到包含javax.tools的模块中。因此注释从a problematic dependencyGenerateNativeHeader中删除,并且这些文件显式地Integer了,因为不再自动生成标题…Aadded to the build process
因此,一个新的注释用于"(hopefully temporary) hack"was created。注释被设置为Long
注释应该直接应用于需要导出的常量字段,而不是整个类。
这些东西的目的是:
javac可以为包含本机方法的类生成本机头。
这是java.lang.annotation.NativeTargetType FIELD
这是Integer的一部分:
javah将自动在包含本机方法的任何类上运行,生成的c-headerdir将放在(-h)headerdir中。新的注释@forceNativeHeader用于具有最终静态原语的类,这些原语需要导出到JNI,但没有本地方法。
基础实验
我在JDK上做了一个基本的实验。我克隆了开放的JDK林,并成功地构建了它。正如预期的那样,为IntegerLong生成的头文件(多亏了Integer)和Long生成的头文件(多亏了它们的本地方法),但不是为@NativeFloat生成的头文件。
    ls -l build/macosx-x86_64-normal-server-release/support/headers/java.base/java_lang_*
    ...
    java_lang_Double.h
    java_lang_Float.h
    java_lang_Integer.h
    java_lang_Long.h
    java_lang_Object.h
    java_lang_Package.h
    ...

然后我尝试从Double字段中删除Byte并尝试再次构建Short但得到一个错误:
jdk/src/java.base/unix/native/libnio/ch/FileChannelImpl.c:35:10: fatal error: 'java_lang_Integer.h' file not found
#include "java_lang_Integer.h"
         ^
1 error generated.

逻辑上,因为没有生成头。
我还确认了@Native包含在多个c和cpp文件中:
find .  \( -name "*.c" -o -name "*.cpp" \) -exec grep "java_lang_Integer.h" {} \; -print
#include "java_lang_Integer.h"
./jdk/src/java.base/unix/native/libnio/ch/FileChannelImpl.c
#include "java_lang_Integer.h"
./jdk/src/java.base/unix/native/libnio/ch/IOUtil.c
#include "java_lang_Integer.h"
./jdk/src/java.base/windows/native/libnet/TwoStacksPlainSocketImpl.c
#include "java_lang_Integer.h"
./jdk/src/java.base/windows/native/libnio/ch/FileChannelImpl.c
#include <java_lang_Integer.h>
./jdk/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp

喜欢Integer
find .  \( -name "*.c" -o -name "*.cpp" \) -exec grep "java_lang_Long.h" {} \; -print
#include "java_lang_Long.h"
./jdk/src/java.base/unix/native/libnio/ch/FileDispatcherImpl.c

喜欢jdk
find .  \( -name "*.c" -o -name "*.cpp" \) -exec grep "java_lang_Float.h" {} \; -print
#include "java_lang_Float.h"
./jdk/src/java.base/share/native/libjava/Float.c
#include "java_lang_Float.h"
./jdk/src/java.base/share/native/libjava/ObjectInputStream.c
#include "java_lang_Float.h"
./jdk/src/java.base/share/native/libjava/ObjectOutputStream.c

就像java_lang_Integer.h
find .  \( -name "*.c" -o -name "*.cpp" \) -exec grep "java_lang_Double.h" {} \; -print
#include "java_lang_Double.h"
./jdk/src/java.base/share/native/libjava/Double.c
#include "java_lang_Double.h"
./jdk/src/java.base/share/native/libjava/ObjectInputStream.c
#include "java_lang_Double.h"
./jdk/src/java.base/share/native/libjava/ObjectOutputStream.c

但两者都不是Long
find .  \( -name "*.c" -o -name "*.cpp" \) -exec grep "java_lang_Short.h" {} \; -print

也不是Float,也不是Double
结论
在所有这些类型中,JDK的本机源代码中只使用了ShortByteCharacterInteger
而且只有LongFloat字段被注释为Double,因为它们没有本地方法(与IntegerLong相反)

 

本文翻译自 https://stackoverflow.com/questions/28770822/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: hashmap的size之所以必须是2的整数次方,是因为hashmap内部使用了一个数组来存储元素,而数组的度必须是2的整数次方,才能在计算元素存储位置时得到更好的性能表现。这是由于hashmap的hash函数计算得到的哈希值是一个整数,当数组度为2的整数次方时,只需对哈希值进行与操作,就能得到元素在数组的索引,这个操作比取模运算要快很多。同时,度为2的整数次方的数组也具有更好的缓存性能,能够更充分地利用计算机硬件的优势。 ### 回答2: HashMap 是基于哈希表实现的键值对映射数据结构,其底层使用一个数组来存储键值对。在使用 HashMap 时,需要指定其初始容量和加载因子,其初始容量即数组的度,加载因子默认为 0.75。当哈希表的元素数量超过了容量乘以加载因子时,哈希表会自动动态扩容。此时,原来的数组将会被复制到新的度为原来两倍的数组上。在 HashMap 进行哈希映射时,会使用对象的 hashCode() 方法来计算其在哈希表的索引位置。而 hashCode() 方法返回的整型值需要将其模以数组度才能得到真正的索引位置。 因此,如果 HashMap 的初始容量不是 2 的整数次方,那么在动态扩容时计算新数组的度时就无法简单地进行位运算,而需要使用更加复杂的除法运算,从而降低了计算效率。同时,在使用取模操作计算哈希表的索引位置时,如果数组度不是 2 的整数次方,那么计算哈希值的低位部分和数组索引的低位部分就会出现重复情况,这可能会导致哈希表的性能下降,产生哈希冲突等问题。因此,为了避免这样的问题,HashMap 的初始容量必须是 2 的整数次方。 此外,因为数据存储在一段连续的内存空间,系统可以通过缓存预读来提高访问速度。而在计算数组的索引位置时,使用位运算相比于其他运算方式更快。因此,使用 2 的整数次方来作为数组的度可以充分利用计算机硬件的特性,提高 HashMap 的访问效率。 ### 回答3: 在Java,HashMap是一种散列映射表,它可以将键值对存储在bucket,通过键来快速定位值。HashMap的性能和内存空间使用率很大程度上依赖于HashMap的容量大小。HashMap的容量大小是指bucket的数量。在创建HashMap时,我们必须指定HashMap的初始容量大小。这个初始容量大小是HashMap的bucket数量。 HashMap内部的实现是将键值对分布在不同的bucket。当一个键值对需要加入到HashMap时,会根据其哈希值(hashCode)的高几位来选择其所属的bucket。为了加速哈希值的计算,Java的哈希表选择使用2的整数次幂作为哈希表的度,这样可以保证哈希值的高位分布更均匀。因为在计算哈希值时,常用的一种算法是“哈希值=HashCode(key)&(HashMap.size-1)”(其&表示与运算),如果HashMap的size不是2的整数次幂,那么HashMap.size-1的任何位都可能为1或0,而不是10000……这样的形式,这就使得用相同的HashCode映射到不同的bucket的概率增加了,可能导致性能下降,同时也增加了发生哈希冲突(多个键值对哈希值相同)的概率,引起内存的浪费。如果HashMap的size是2的整数次幂,那么HashMap.size-1的位上的数都是1,这样就可以保证HashCode&(HashMap.size-1)的结果落在[0,HashMap.size-1]的范围内,哈希函数相对均匀有效,同时内存的利用率也是最高的。 综上所述,HashMap的size必须是2的整数次幂,这样可以使得哈希函数更加均匀,性能更高,同时也能更好的利用内存。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值