zoukankan      html  css  js  c++  java
  • 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();
        }
  • 相关阅读:
    Linux内存管理1---内存寻址
    UML和模式应用5:细化阶段(10)---UML交互图
    AT91RM9200---电源管理控制器(PMC)介绍
    AT91RM9200---定时器简介
    [转]指针大小是由谁决定的?
    UML和模式应用5:细化阶段(9)---迈向对象设计
    UML和模式应用5:细化阶段(8)---逻辑架构和UML包图
    UML和模式应用5:细化阶段(7)---从需求到设计迭代进化
    UML和模式应用5:细化阶段(6)---操作契约
    VxWorks软件开发项目实例完全解析1-VxWorks简介
  • 原文地址:https://www.cnblogs.com/drafire/p/10462144.html
Copyright © 2011-2022 走看看