(JAVA)HashSet里面保存的数据怎么变成有顺序的了?

顺应天意了却凡尘 发布于 02/27 15:25
阅读 947
收藏 0

@tcxu 你好,想跟你请教个问题:

package jiu;
/**
 *@ClassName  HashSetDemo
 *
 * @Description 测试HashMapSet集合类
 *
 * @date  2014-3-3  上午8:45:26
 * 
 */

import java.util.HashSet;

public class HashSetDemo {
    public static void main(String args[]) {
        // 创建HashSet对象
        HashSet<String> hs = new HashSet<String>();
        // 加入元素到HastSet中
        hs.add("B");
        hs.add("A");
        hs.add("D");
        hs.add("E");
        hs.add("C");
        hs.add("F");
        System.out.println(hs);
    }
}

不是说HashSet是无序的吗?怎么输出结果却有序了?

加载中
1
wy65
wy65

可以把所有字母全部加入,然后在看看还是不是有序的

0
韦小仇
韦小仇

HashSet的原理是:计算对象的hash值,并映射到数组的下标。再看一下String的hashCode()方法的源码

哈希值是通过char对应的int值计算出来的,即:char对应的int值越大,hash值就越大。

而在拉丁字母A、B、C、D的char字符对应的int值恰巧是递增的,所以他们的hash值也恰巧是递增的,所以最终映射到HashSet内的数组的位置也是顺序的。

0
tcxu
tcxu

    是的,HashSet 元素的存储(地址)是无序的。一般来讲,通过进行迭代的迭代器也是依次将各个元素(字符串),无序地输出。

    楼主所示的"有序结果",其实是一种极其特殊案例:每个元素都仅是一(单)个字符,或都是数字字符,或都是小写英文字符,或都是大写英文字符。

    元素的存储地址,视这个元素的哈希值而定。由于元素仅有一个字符,其哈希值的计算结果,就会是这个给定字符的 ascii 码(参见: 哈希算法)的值。结果, 打印出的 HashSet 列表,就会是貌似经过排序后的结果。但要注意构成这种案例的条件:元素必须 都是单个数字字符、或都是单个英文小写字符、或都是单个英文大写字符。

    下列代码显示每个元素的哈希值,以及最终调用 toString() 输出 HashSet hs 的结果。输出结果显示,单个字符元素的哈希值,就是它的 ASCII值。

import java.util.HashSet;

public class HashSetDemo {
 static void showHashSet(String[] s){
	HashSet<String> hs = new HashSet<String>();	
	for (int i=0;i<s.length;i++){
		hs.add(s[i]);
		System.out.println(s[i] + "的哈希值:" +s[i].hashCode()+ " ");
		}		
	System.out.println("toString的结果:" + hs);	
	}
 public static void main(String args[]) {
  	String data[][]=new String[5][6];
  	String dat[]={
  		"B","A","D","E","C","F", // 可以打印出貌似排序的结果
  		"6","4","3","1","2","5", // 可以打印出貌似排序的结果 
  		"BB","AA","DD","EE","CC","FF",
  		"42","41","45","46","44","43",
  		"Base","Alberta","Done","End","Coding","Factory"};
  	for (int i=0;i<dat.length;i++)
  		data[i/6][i%6]=dat[i];
  	for (int i=0;i<data.length;i++)
  		showHashSet(data[i]);
     }
 }

输出:

B的哈希值:66
A的哈希值:65
D的哈希值:68
E的哈希值:69
C的哈希值:67
F的哈希值:70
toString的结果:[A, B, C, D, E, F]
6的哈希值:54
4的哈希值:52
3的哈希值:51
1的哈希值:49
2的哈希值:50
5的哈希值:53
toString的结果:[1, 2, 3, 4, 5, 6]
BB的哈希值:2112
AA的哈希值:2080
DD的哈希值:2176
EE的哈希值:2208
CC的哈希值:2144
FF的哈希值:2240
toString的结果:[BB, AA, DD, EE, CC, FF]
42的哈希值:1662
41的哈希值:1661
45的哈希值:1665
46的哈希值:1666
44的哈希值:1664
43的哈希值:1663
toString的结果:[44, 45, 46, 41, 42, 43]
Base的哈希值:2063089
Alberta的哈希值:743772625
Done的哈希值:2135970
End的哈希值:69819
Coding的哈希值:2023747466
Factory的哈希值:572770538
toString的结果:[Done, Alberta, Coding, Factory, End, Base]

 

0
tcxu
tcxu

    hash 是无须的,或者说是不保证有序的。
    但多次重新运行下列代码,亦可看出:每个 Integer 元素的哈希值,就是它的整形数的值。而且 hashset 的输出结果都是进行排序了的,那hashset 是怎么做到对结果的有顺序输出的呢?

import java.util.HashSet;
import java.lang.Integer;
import java.util.Random;

public class SetOfInteger {
    public static void main(String[] args) {
        Random random=new Random(47);
        HashSet<Integer> intset=new HashSet<Integer>();
        for(int i=0;i<1000;i++){
        	int n = random.nextInt(50);
        	System.out.print( n + " hashCode: " + new Integer(n).hashCode() + ", ");
            intset.add(n);
        }
        System.out.println("\n" + intset);
    }
}

输出:

    回答是: 虽然 hash 有它自己的顺序, 但这个顺序是hash的顺序.不一样的哈希算法,顺序也是不一样的。而且有序输出跟有序存储是两码事。
    看来 正如 HashSet能对结果进行排序吗 的讨论指出的那样, "你需要百度下:数据结构、存储结构、还有hash键值对。总的来说你缺的太多,别一下直接想这么深的问题,先从数据结构开始学" 。

0
tcxu
tcxu

引用来自“wy65”的评论

可以把所有字母全部加入,然后在看看还是不是有序的

试了一下:将所有的单个大写、部分小写(a-g)、和部分数字字符(1-6), 作为长度为1的字符串,加起来。结果是"大写字符、小写字符、和 数字字符,分别升序排列输出"。只是数字的字符串最后出现。

import java.util.HashSet;

public class HashSetDemo_1 {
     public static void main(String args[]) {
         // 创建HashSet对象
        HashSet<String> hs = new HashSet<String>();
         // 加入元素到HastSet中
		 hs.add("3");
         hs.add("6");
         hs.add("5");
         hs.add("4");
         hs.add("2");
         hs.add("1");
         
         hs.add("b");
         hs.add("g");
         hs.add("d");
         hs.add("e");
         hs.add("c");
         hs.add("f");
         
         hs.add("B");
         hs.add("A");
         hs.add("D");
         hs.add("E");
         hs.add("C");
         hs.add("F");
         hs.add("X");
         hs.add("Y");
         hs.add("S");
         hs.add("Z");
         hs.add("T");
         hs.add("V");
         hs.add("U");
         hs.add("J");
         hs.add("I");
         hs.add("K");
         hs.add("I");
         hs.add("M");
         hs.add("L");
         hs.add("A");
         hs.add("Q");
         hs.add("P");
         hs.add("O");
         hs.add("N");
         hs.add("R");
         hs.add("N");
         hs.add("R");
         hs.add("a");
         hs.add("G");
         hs.add("H");
         System.out.println(hs);
     }
 }

0
wy65
wy65

试试下面的代码段:

        HashSet<String> hs = new HashSet<String>();
		for (int i = 0; i < 26; i++) {
			hs.clear();
			hs.add(String.valueOf((char)('A' + i)));
			hs.add(String.valueOf((char)('A' + 25 - i)));
			System.out.println(hs.toString());
		}

输出:

[A, Z]
[B, Y]
[C, X]
[D, W]
[E, V]
[U, F]
[T, G]
[S, H]
[R, I]
[Q, J]
[P, K]
[L, O]
[M, N]
[M, N]
[L, O]
[P, K]
[Q, J]
[R, I]
[S, H]
[T, G]
[U, F]
[E, V]
[D, W]
[C, X]
[B, Y]
[A, Z]
[A, Z]

 

wy65
wy65
回复 @tcxu : 一组总共就两个,比较升序降序没意义。总之,按顺序加入的,打印出来有升序,有降序,说明不是有序的
tcxu
tcxu
回复 @wy65 : 就字符串比较而言,前5行,即 [A,Z] ... [E,V], 为升序, 从第6行[U,F]起,变为降序。....
wy65
wy65
回复 @tcxu : 看 [U, F] 这一行,就知道不是有序的
tcxu
tcxu
输出应当显示 26 行 (for 循环进行了 26 次),可你显示的输出共 27 行。最后一行 "[A, Z]"是多出来的、是不应当出现的(可能是你复制粘贴时的错误)。
0
gitOpen_1
gitOpen_1

你对无序这个概念有误差啊

0
gitOpen_1
gitOpen_1

hashSet的无序是指不能以写入的顺序或倒序输出

0
laohng1995
laohng1995

我记得现在jdk8 版本的hashSet底层是调用HaseMap 的K来实现无序的,无序的概念并不是说你输入什么都会打乱顺序,他会按照HashCode进行插入到Hash 中,最后会用hash表存的顺序输出!

返回顶部
顶部