亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定

關于老師視頻中Set集合輸出無序,而自己重復碼了老師的代碼卻輸出有序的問題

轉自知乎

看到《Thinking in Java》中有這么一段代碼,書中給出的Output是無序的,可是我實際運行出來是有序的,就是從0遞增到29,這是為什么呢?
public class SetOfInteger {
? ?public static void main(String[] args){
? ? ? ?Random rand=new Random(47);
? ? ? ?Set<Integer> intset=new HashSet<Integer>();
? ? ? ?for (int i=0;i<10000;i++){
? ? ? ? ? ?intset.add(rand.nextInt(30));
? ? ? ?}
? ? ? ?Iterator<Integer> iterator=intset.iterator();
? ? ? ?while (iterator.hasNext()){
? ? ? ? ? ?System.out.print(iterator.next()+" ");
? ? ? ?}
? ?}
}


首先 @趙劼 大大的答案就是正解了?!安槐WC有序”和“保證無序”不等價,HashSet的iterator是前者而不是后者,所以在一次運行中看到有序的結果也是正常的,但不能依賴這個有序行為。
況且HashSet并不關心key的“排序”,就算其iterator“有序”通常也是說“按元素插入順序”(LinkedHashSet就支持插入順序遍歷)。題主在此看到的所謂“有序”純粹是個巧合。

然后我復制粘貼了題主的代碼運行了一次:

$ java SetOfInteger
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 17 16 19 18 21 20 23 22 25 24 27 26 29 28
$ java -version
java version "1.7.0-internal-zing_99.99.99.99.dev"
Zing Runtime Environment for Java Applications (build 1.7.0-internal-zing_99.99.99.99.dev-b65)
Zing 64-Bit Tiered VM (build 1.7.0-zing_99.99.99.99.dev-b870-product-azlinuxM-X86_64, mixed mode)

(Zing JDK7的開發版)
就不是有序的嘛。同樣在Oracle JDK7u51上也是如此:

$ java SetOfInteger
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 17 16 19 18 21 20 23 22 25 24 27 26 29 28
$ java -version
java version "1.7.0_51"
Java(TM) SE Runtime Environment (build 1.7.0_51-b13)
Java HotSpot(TM) 64-Bit Server VM (build 24.51-b03, mixed mode)


換到Zing JDK8:

$ java SetOfInteger
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
$ java -version
java version "1.8.0-internal-zing_99.99.99.99.dev"
Zing Runtime Environment for Java Applications (build 1.8.0-internal-zing_99.99.99.99.dev-b65)
Zing 64-Bit Tiered VM (build 1.8.0-zing_99.99.99.99.dev-b870-product-azlinuxM-X86_64, mixed mode)

再換到Oracle JDK8u25:

$ java SetOfInteger
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
$ java -version
java version "1.8.0_25"
Java(TM) SE Runtime Environment (build 1.8.0_25-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)

就看到了題主說的有序行為。

JDK8的HashSet實現變了,導致元素插入的位置發生了變化;iterator自身實現的順序倒沒變,還是按照內部插入的位置順序來遍歷,于是題主就看到了JDK7和JDK8的結果不一樣。具體來說,是JDK7與JDK8的java.util.HashMap的hash算法以及HashMap的數據布局發生了變化。

題主插入HashSet的是Integer,其hashCode()實現就返回int值本身。所以在對象hashCode這一步引入了巧合的“按大小排序”。
然后HashMap.hash(Object)獲取了對象的hashCode()之后會嘗試進一步混淆。
JDK8版java.util.HashMap內的hash算法比JDK7版的混淆程度低;在[0, 2^32-1]范圍內經過HashMap.hash()之后還是得到自己。題主的例子正好落入這個范圍內。外加load factor正好在此例中讓這個HashMap沒有hash沖突,這就導致例中元素正好按大小順序插入在HashMap的開放式哈希表里。
根據它的實現特征,把題主的例子稍微修改一下的話:

$ cat SetOfInteger.java
import java.util.*;

public class SetOfInteger {
? ?public static void main(String[] args){
? ? ? ?Random rand=new Random(47);
? ? ? ?Set<Integer> intset=new HashSet<Integer>();
? ? ? ?for (int i=0;i<10000;i++){
? ? ? ? ? ?intset.add(rand.nextInt(30) + (1 << 16));
? ? ? ?}
? ? ? ?Iterator<Integer> iterator=intset.iterator();
? ? ? ?while (iterator.hasNext()){
? ? ? ? ? ?System.out.print((iterator.next() - (1 << 16)) +" ");
? ? ? ?}
? ?}
}
$ java SetOfInteger
1 0 3 2 5 4 7 6 9 8 11 10 13 12 15 14 17 16 19 18 21 20 23 22 25 24 27 26 29 28
$ java -version
java version "1.8.0_25"
Java(TM) SE Runtime Environment (build 1.8.0_25-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)

就可以看到順序不一樣了。修改的內容就是把插入的數字先加上2的16次方,然后拿出來之后再減去2的16次方,而已 ^_^



作者:RednaxelaFX
鏈接:https://www.zhihu.com/question/28414001/answer/40733996
來源:知乎
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


這里我看的不是很明白,他說的意思是JDK1.8版之后HashSet實現類的方法改變了,導致能夠產生無序的最小范圍擴大了嗎?還是其他什么?

至于原HashSet實現類具體是怎么樣我還沒學不知道是什么樣子,看樣子好像是原來是沒有這個范圍的?不管是輸入多少輸出之后都是無序的,像視頻里一樣?懇請各位大大幫忙解答。


正在回答

1 回答

他的意思應該是在[0, 2^32-1]范圍內經過HashMap.hash()之后還是得到自己,就是說在這個范圍內默認是按哈希表里的順序也就是從小到大排列??????? 在講解中不是說先加2的16次方然后再減去2的16次方??? 也就是只要跳出那個范圍就是正常無序的了??? (我的一點見解,不保證一定喔)

0 回復 有任何疑惑可以回復我~
#1

未來的先驅者 提問者

額,我想知道的是我們輸出了有序的Set集合是不是也是因為他說的這個原因呢?
2016-03-23 回復 有任何疑惑可以回復我~

舉報

0/150
提交
取消

關于老師視頻中Set集合輸出無序,而自己重復碼了老師的代碼卻輸出有序的問題

我要回答 關注問題
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號