【JAVA基础】HashSet、LinkedHashSet、TreeSet使用区别
HashSet:哈希表是通过使用称为散列法的机制来存储信息的,元素并没有以某种特定顺序来存放;
LinkedHashSet:以元素插入的顺序来维护集合的链接表,允许以插入的顺序在集合中迭代;
TreeSet:提供一个使用树结构存储Set接口的实现,对象以升序顺序存储,访问和遍历的时间很快。
用例代码:
package com.test;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.TreeSet;
/**
* @description 几个set的比较
* HashSet:哈希表是通过使用称为散列法的机制来存储信息的,元素并没有以某种特定顺序来存放;
* LinkedHashSet:以元素插入的顺序来维护集合的链接表,允许以插入的顺序在集合中迭代;
* TreeSet:提供一个使用树结构存储Set接口的实现,对象以升序顺序存储,访问和遍历的时间很快。
* @author Zhou-Jingxian
*
*/
public class SetDemo {
public static void main(String[] args) {
HashSet<String> hs = new HashSet<String>();
hs.add("B");
hs.add("A");
hs.add("D");
hs.add("E");
hs.add("C");
hs.add("F");
System.out.println("HashSet 顺序:\n"+hs);
LinkedHashSet<String> lhs = new LinkedHashSet<String>();
lhs.add("B");
lhs.add("A");
lhs.add("D");
lhs.add("E");
lhs.add("C");
lhs.add("F");
System.out.println("LinkedHashSet 顺序:\n"+lhs);
TreeSet<String> ts = new TreeSet<String>();
ts.add("B");
ts.add("A");
ts.add("D");
ts.add("E");
ts.add("C");
ts.add("F");
System.out.println("TreeSet 顺序:\n"+ts);
}
}
输出效果:
HashSet 顺序:
[D, E, F, A, B, C]
LinkedHashSet 顺序:
[B, A, D, E, C, F]
TreeSet 顺序:
[A, B, C, D, E, F]
HashSet的输出结果分析:
hashset的实现,依据的是hashmap,
举个hashset源码的小例子:
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
所以我们分析hashmap的源码:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
最为重要的就是:hash的生成算法和indexfor确定的table[i]的下标:
static int hash(int h) {
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
/**
* Returns index for hash code h.
*/
static int indexFor(int h, int length) {
return h & (length-1);
}
这样,就解释了为什么hashset的输出如此不同,它是根据hash值的生成策略来存储在散列表中的。
hashmap的源码,是根据transient Entry[] table;以及生成entry内部类,来实现散列表的。
当然,我们在做应用时,也可以使用linkedList[],来实现散列表。
代码如下:
public class HashTest {
static final int tablesize = 13;
LinkedList<Integer>[] listTable;
String[] s = new String[12];
public void load(Iterator<Integer> it) {
listTable = (LinkedList<Integer>[])new LinkedList[tablesize];
while(it.hasNext()) {
Integer temp = it.next();
int n = hash(temp);
if(listTable[n] == null) {
listTable[n] = new LinkedList<Integer>();
}
listTable[n].add(temp);
}
}
private int hash(Integer temp) {
// int v = temp.hashCode(); //比较对象(字符串)时,取得hash值
int v = temp;
return v % tablesize;
}
private boolean search(Integer v) {
int h = hash(v);
LinkedList<Integer> ll = listTable[h];
if(ll == null) return false;
return ll.contains(v);
}
public static void main(String[] args) {
HashTest ht = new HashTest();
List<Integer> li = new ArrayList<Integer>();
for(int i=0;i<10000;i++) {
li.add(i);
}
ht.load(li.iterator());
int a = 10000;
if(ht.search(a)) {
System.out.println("yes");
} else {
System.out.println("no");
}
// HashSet<String> hs = new HashSet<String>();
// hs.add("D");
// hs.add("B");
// hs.add("E");
// hs.add("A");
// hs.add("F");
// hs.add("C");
// System.out.println(hs);
//
// System.out.println(17 & 16);
}
}