纹理和基元_Java的精妙之处,包括基元和变量参数数组
紋理和基元
在我最近的博客文章Arrays.hashCode()與 DZone聯(lián)合版本的評論中提出了一個(gè)有趣的問題。 Objects.hash() “。 該評論的作者建立了一些示例,這些示例與我的博客文章中使用的示例相似,并且顯示出與我看到的結(jié)果不同的結(jié)果。 感謝評論作者抽出寶貴的時(shí)間來發(fā)表這篇文章,因?yàn)樗鹆薐ava的細(xì)微差別,我認(rèn)為這很值得寫博客。
評論作者顯示了以下有效的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)));該評論的作者提到,對于所有四個(gè)語句,運(yùn)行剛顯示的代碼的結(jié)果都完全相同。 這與我的示例不同,在示例中,在原始int值數(shù)組上調(diào)用Arrays.hashCode(int [])的結(jié)果與在同一原始int值數(shù)組上調(diào)用Objects.hash(Object…)的結(jié)果不同。
對原始反饋評論的一個(gè)答復(fù)準(zhǔn)確地指出,不能保證在不同JVM上生成的哈希碼是相同的。 實(shí)際上, Object.hashCode()方法的Javadoc注釋指出(我強(qiáng)調(diào)了 ):
- 只要在Java應(yīng)用程序執(zhí)行期間在同一對象上多次調(diào)用它,hashCode方法就必須一致地返回相同的整數(shù),前提是不修改該對象的equals比較中使用的信息。 從一個(gè)應(yīng)用程序的執(zhí)行到同一應(yīng)用程序的另一執(zhí)行,此整數(shù)不必保持一致。
- 如果根據(jù)equals(Object)方法兩個(gè)對象相等,則在兩個(gè)對象中的每個(gè)對象上調(diào)用hashCode方法必須產(chǎn)生相同的整數(shù)結(jié)果。
陳述了所有這些內(nèi)容之后,為整數(shù)計(jì)算的哈希碼通常在每次運(yùn)行之間都是一致的。 原始評論者示例的輸出都具有完全相同的值也很有趣。 盡管我可能不希望這些值與示例的值相匹配,但令人驚訝的是,評論者提供的所有示例都具有相同的答案。
反饋?zhàn)⑨屩刑峁┑氖纠c我的示例之間的區(qū)別在于注釋者的示例如何為原始int值數(shù)組調(diào)用Objects.hash(Object...)與我的示例如何調(diào)用Objects.hash(Object...)用于原始int值的數(shù)組。 在我的示例中,我將相同的本地?cái)?shù)組傳遞給所有方法調(diào)用。 該注釋者的示例將原始int值的顯式數(shù)組傳遞給Arrays.hashCode(int[]) ,但將各個(gè)int元素傳遞給Objects.hash(Object...)而不是將數(shù)組傳遞給該后一種方法。 當(dāng)我向注釋者的示例集中添加另一個(gè)示例,該示例確實(shí)將原始int值數(shù)組傳遞給Objects.hash(Object...)方法時(shí),我得到的生成的哈希碼與所有其他哈希碼不同。 接下來顯示該增強(qiáng)的代碼。
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));運(yùn)行注釋器提供的代碼的經(jīng)過修改和增強(qiáng)的版本會導(dǎo)致此輸出(突出顯示我添加的示例):
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將輸出與生成它的代碼進(jìn)行比較,可以看出,當(dāng)將int值數(shù)組的元素傳遞給Arrays.hashCode(int[]) ,它與Objects.hash(Object...)生成相同的哈希碼值Objects.hash(Object...)方法作為單個(gè)元素。 但是,我們還可以看到,當(dāng)完整地傳遞原始int值的數(shù)組(作為單個(gè)數(shù)組而不是作為數(shù)組的單個(gè)元素)時(shí), Objects.hash(Object...)方法生成了完全不同的哈希碼。 我添加的其他兩個(gè)示例(突出顯示)是通過直接在數(shù)組上調(diào)用.hashCode()或通過Objects.hashCode獲得等效的結(jié)果來顯示原始int值數(shù)組上的“直接”哈希碼。 (對象) 。 [這不是巧合, Objects.hash(Object...)為原始int值數(shù)組生成的哈希碼比為原始int值數(shù)組生成的“直接”哈希碼正好大31。 ]
所有這些都指向這里的真正問題:通常最好不要將原語數(shù)組傳遞給接受可變參數(shù) (通告省略號 )的方法。 SonarSource規(guī)則瀏覽器 ( Java )在RSPEC-3878中提供了有關(guān)此內(nèi)容的更多詳細(xì)信息。 與規(guī)則描述特別相關(guān)的是與歧義有關(guān)的問題:“數(shù)組應(yīng)該是一個(gè)對象還是對象的集合?”
剛剛提出的問題的答案是,當(dāng)將原始int值數(shù)組傳遞給接受方法Objects.hash(Object...)的變量參數(shù)時(shí), 整個(gè)數(shù)組將被視為單個(gè) Object 。 相反,當(dāng)將引用對象的數(shù)組(例如Integer )傳遞給相同的方法時(shí),它將其視為與傳遞給數(shù)組的元素相同數(shù)量的對象。 下一個(gè)代碼清單和相關(guān)輸出證明了這一點(diǎn)。
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剛剛顯示的示例代碼和相關(guān)的輸出說明,期望變量參數(shù)的方法將傳遞給它的原始值數(shù)組視為單個(gè)元素?cái)?shù)組 。 另一方面,相同的方法將通過引用對象類型傳遞給它的數(shù)組視為具有相同元素?cái)?shù)的數(shù)組。
考慮到這一點(diǎn),請返回哈希碼生成示例,由Objects.hash(Object...)為原始int值數(shù)組生成的哈希碼與由Arrays.hashCode(int[])生成的哈希碼不同。 類似地,我們現(xiàn)在可以解釋為什么對象引用數(shù)組導(dǎo)致相同的哈希碼,而不管調(diào)用了哪種方法。
我之前提到過,由Objects.hash(Object)生成的哈希碼比整個(gè)數(shù)組的“直接”哈希碼高31并非巧合。 這并不奇怪,因?yàn)镺bjects.hash(Object...)的OpenJDK實(shí)現(xiàn)將Arrays.hashCode(Object[]) Objects.hash(Object...)委托給Arrays.hashCode(Object[]) ,該數(shù)組使用素?cái)?shù)乘以31 ,然后乘以計(jì)算出的哈希碼中的每個(gè)元素。 考慮到上述觀察,由Objects.hash(Object...)為原始int值數(shù)組提供的哈希碼值似乎正是該方法的實(shí)現(xiàn)將導(dǎo)致我們期望的結(jié)果:整個(gè)數(shù)組的直接哈希值加上31個(gè)質(zhì)數(shù)。 當(dāng)該哈希碼方法僅循環(huán)一個(gè)元素時(shí)(傳遞給需要可變參數(shù)的方法的基元數(shù)組就是這種情況),其計(jì)算本質(zhì)上是31 * 1 + <directHashValueOfOverallArray> 。
值得注意的是,即使參考對象數(shù)組的哈希碼計(jì)算得出的結(jié)果與將元素傳遞給接受變量參數(shù)的方法時(shí)的結(jié)果相同,還是最好避免將參考對象數(shù)組傳遞給這樣的對象。方法。 當(dāng)發(fā)生這種情況時(shí), javac編譯器會提供此警告:“警告:對最后一個(gè)參數(shù)使用不精確的參數(shù)類型的varargs方法的非varargs調(diào)用”,并添加了有關(guān)解決此問題的潛在方法的這些有用的細(xì)節(jié):“為varargs調(diào)用廣播到對象”或“廣播到Object []以進(jìn)行非可變參數(shù)調(diào)用并禁止顯示此警告”。 當(dāng)然,對于JDK 8和更高版本,在將數(shù)組提供給需要可變參數(shù)的方法之前,以多種其他方式處理數(shù)組是相當(dāng)簡單的。
我在原始帖子 (及其DZone聯(lián)合版本 )中添加了最后一段,以嘗試快速解決此問題,但是我已經(jīng)使用此帖子來更詳細(xì)地表達(dá)此信息。 此處總結(jié)的經(jīng)驗(yàn)教訓(xùn)可以概括為“對原始數(shù)組使用適當(dāng)?shù)闹剌dArrays.hashCode方法,而不是使用Objects.hash(Object...) ”和“ Favor Arrays.hashCode(Object[])對于引用類型,而不是使用Objects.hash(Object...) 。” 如果調(diào)用的方法“看到”的元素?cái)?shù)量無論如何都是重要的,則更通用的準(zhǔn)則是要警惕將原始值數(shù)組傳遞給需要Object類型變量參數(shù)的方法,并且要警惕傳遞引用數(shù)組指向期望可變參數(shù)的方法的對象,以避免編譯器警告和模棱兩可的警告。
翻譯自: https://www.javacodegeeks.com/2018/09/java-subtlety-with-arrays-of-primitives-and-variable-arguments.html
紋理和基元
總結(jié)
以上是生活随笔為你收集整理的纹理和基元_Java的精妙之处,包括基元和变量参数数组的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑显示器5k(电脑显示器5个按键详解英
- 下一篇: javafx 调用java_Java,J