Collection

HashMap 和HashSet 的区别:

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

public V get(Object key) {
        Node<K,V> 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<K,V>[] tab; Node<K,V> 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<K,V> 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<K,V>)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 null
15         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<K,V> entry = (Entry<K,V>)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<String> 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<String> 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实现的Deque
1|2|2|3|4|
下面是由LinkedList实现的Deque
1|2|2|3|4|

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

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

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

@Test
    public void add() {
        //Map<key,value> 里面定义的value 不能是primitive(原始)类型,因此不能使用int、boolean 这些
        Map<Integer, String> 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<Integer, String> map = new HashMap<>();
        map.put(1, "james");
        map.put(2, "lily");
        map.put(3, "tom");

        //键集
        Set<Integer> integers = map.keySet();
        //键-值集
        Set<Map.Entry<Integer, String>> entries = map.entrySet();
        Iterator<Map.Entry<Integer, String>> iterator = entries.iterator();
        while (iterator.hasNext()){
            Map.Entry<Integer, String> next = iterator.next();
            System.out.println(next.getValue());
        }
        //值集
        Collection<String> values = map.values();
    }

 

posted on 2019-03-07 13:01  drafire  阅读(262)  评论(0编辑  收藏  举报