博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Collection
阅读量:5036 次
发布时间:2019-06-12

本文共 6689 字,大约阅读时间需要 22 分钟。

HashMap 和HashSet 的区别:

HashMap 是线程不安全的,而HashSet 是线程安全的。原因在于两个数据结构的set 和get方法的实现。HashMap代码如下:

public V get(Object key) {        Node
e; return (e = getNode(hash(key), key)) == null ? null : e.value; }final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node
[] tab; Node
p; int n, i; if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { Node
e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) e = ((TreeNode
)p).putTreeVal(this, tab, hash, key, value); else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null; }

而HashTable ,则是使用了synchronized 来保证线程安全,代码如下:

1   public synchronized V get(Object key) { 2         Entry
tab[] = table; 3 int hash = key.hashCode(); 4 int index = (hash & 0x7FFFFFFF) % tab.length; 5 for (Entry
e = tab[index] ; e != null ; e = e.next) { 6 if ((e.hash == hash) && e.key.equals(key)) { 7 return (V)e.value; 8 } 9 }10 return null;11 }12 13 public synchronized V put(K key, V value) {14 // Make sure the value is not null15 if (value == null) {16 throw new NullPointerException();17 }18 19 // Makes sure the key is not already in the hashtable.20 Entry
tab[] = table;21 int hash = key.hashCode();22 int index = (hash & 0x7FFFFFFF) % tab.length;23 @SuppressWarnings("unchecked")24 Entry
entry = (Entry
)tab[index];25 for(; entry != null ; entry = entry.next) {26 if ((entry.hash == hash) && entry.key.equals(key)) {27 V old = entry.value;28 entry.value = value;29 return old;30 }31 }32 33 addEntry(hash, key, value, index);34 return null;35 }

 

java 中,hash 散列表,是由链表数组实现的。但是在Java SE 8中,桶满时,hash 的数据结构,会从链表数组变为平衡二叉树

HashSet ,是Set 的实现类。hash 保证拥有相同的值的元素将会拥有相同的hashCode(),由于hashCode()的唯一性,所以相同的元素最多只能插入hashSet 中一次。这也是Set需要保证的。

TreeSet,也是Set的实现类。相对比HashSet ,TreeSet是一个有序的集合,插入数据会比HashSet慢,但是检索查找元素会比数组、链表的重复元素会快很多,它的数据结构是红黑树。使用TreeSet,TreeSet里面的元素必须实现Comparable接口,或者构造集合时候提供一个Comparator。

 如果两个变量使用equals() 相等,那么它们的hash值也必然是相等的。原因如下:

public final boolean equals(Object o) {            if (o == this)                return true;            if (o instanceof Map.Entry) {                Map.Entry
e = (Map.Entry
)o; if (Objects.equals(key, e.getKey()) && Objects.equals(value, e.getValue())) return true; } return false; }

因为equals的实现,就是判断key 和value 是否都相等。对于hash算法来说说,拥有着相同的key,必然也拥有相同hash值

-------------------------------------------------------------------------------------------------------

队列 ,有队列Queue 和双端队列Deque 的分别。Queue 和Deque  都可以指定容量Capacity,也可以不指定。Deque 中还提供了offer*()这样的系列方法。《Java核心技术》中提到的,Deque 的add() 如果超过Capacity 会抛出异常,而offer*()不会抛出异常,只会返回null,这个说法是错的。经过实践以及源码查看你,这两个方法都不会返回null。代码如下:

 
@Test public void addArrayDeque() {
System.out.println("下面是由ArrayDeque实现的Deque"); Deque
stringDeque = new ArrayDeque<>(3); stringDeque.add("1"); stringDeque.add("2"); stringDeque.add("2"); stringDeque.add("3"); stringDeque.offer("4"); for (String item : stringDeque) {
System.out.print(item + "|"); } } @Test public void addLinkedListQueue() {
System.out.println("下面是由LinkedList实现的Deque"); Deque
stringDeque = new LinkedList<>(); stringDeque.add("1"); stringDeque.add("2"); stringDeque.add("2"); stringDeque.add("3"); stringDeque.offer("4"); for (String item : stringDeque) {
System.out.print(item + "|"); } }
 

打印出来的结果如下:

下面是由ArrayDeque实现的Deque1|2|2|3|4|下面是由LinkedList实现的Deque1|2|2|3|4|

优先级队列 的数据结构是堆,本质上是一个二叉树。

--------------------------------------------------------------------------

Map不能对同一个key put 两次value。事实上,第二次的put的value 将会覆盖掉第一次的value,并返回第一次的value。代码如下:

@Test    public void add() {        //Map
里面定义的value 不能是primitive(原始)类型,因此不能使用int、boolean 这些 Map
map = new HashMap<>(); map.put(1, "james"); map.put(2, "lily"); map.put(3, "tom"); //Map不能对同一个key put 两次value。事实上,第二次的put的value 将会覆盖掉第一次的value,并返回第一次的value String oldValue = map.put(1, "tony"); System.out.println(map.get(1)); System.out.println(oldValue); //get 将返回null System.out.println(map.get(5)); //getOrDefault 如果为null,返回自定义的值 System.out.println(map.getOrDefault(5, "empty")); //Map循环 map.forEach((k, v) -> { System.out.println("{key=" + k + ",value=" + v + "}"); }); }

下面是map的集的一些代码,不过这个不是TreeSet,也不是HashSet。

@Test    public void set(){        Map
map = new HashMap<>(); map.put(1, "james"); map.put(2, "lily"); map.put(3, "tom"); //键集 Set
integers = map.keySet(); //键-值集 Set
> entries = map.entrySet(); Iterator
> iterator = entries.iterator(); while (iterator.hasNext()){ Map.Entry
next = iterator.next(); System.out.println(next.getValue()); } //值集 Collection
values = map.values(); }

 

转载于:https://www.cnblogs.com/drafire/p/10462144.html

你可能感兴趣的文章
2018.11.20
查看>>
word20161215
查看>>
12th week blog
查看>>
dijkstra (模板)
查看>>
python小记(3)
查看>>
编译Linux驱动程序 遇到的问题
查看>>
大型分布式网站架构技术总结
查看>>
HDU 1017[A Mathematical Curiosity]暴力,格式
查看>>
[算法之美] KMP算法的直观理解
查看>>
EntityFramework 性能优化
查看>>
Python学习-文件操作
查看>>
正则表达式()、[]、{}的区别
查看>>
第十二周作业
查看>>
Socket开发框架之框架设计及分析
查看>>
oracle.encode('gbk',errors='ignore').decode('gbk')
查看>>
kexec on openwrt - linux boots linux, kernel boots kernel on openwrt
查看>>
【练习赛2补题】zoj 2734 Exchange Cards 【DFS】
查看>>
【练习赛补题】问题 E: 花生采摘 【模拟】
查看>>
大叔程序员的第二天 @Fragment学习
查看>>
k8s应用首页临时改成升级维护页面
查看>>