新手了解java 集合基础知识(二)

2021-07-05,,

目录
  • 三、Map
    • 1、HashMap
    • 2、TreeMap
    • 3.ConcurrentHashMap
  • 总结

    三、Map

    ​ 存储的双列元素,Key是无序的,不可重复,而Value是无序,可重复的。

    1、HashMap

    public class HashMapDemo {
        private Map map = null;
        public void init() {
            map = new HashMap();
            map.put("a", "aaa");
            map.put("b", "bbb");
            map.put("c", "ccc");
            System.out.println(map);
        }
        // 添加元素
        public void testPut() {
            // V put(K key, V value) :把指定的key和value添加到集合中
            map.put("a1", "aaa");
            map.put("b1", "bbb");
            map.put("c1", "ccc");
            System.out.println(map);
            // void putAll(Map<? extends K,? extends V>m) :把指定集合添加集合中
            Map map1 = new HashMap();
            map1.put("e", "eee");
            map1.put("f", "fff");
            map.putAll(map1);
            System.out.println(map);
            // default V putIfAbsent(K key, V value) :如果key不存在就添加
            map.putIfAbsent("a", "hello");
            System.out.println(map);
            map.putIfAbsent("g", "ggg");
            System.out.println(map);
        }
        // 修改元素
        public void testModify() {
            // V put(K key, V value) :把集合中指定key的值修改为指定的值
            map.put("a", "hello");
            map.put("a", "world");
            System.out.println(map);
            // 说明,当key相同时,后面的值会覆盖前面的值。
            // default V replace(K key, V value) :根据key来替换值,而不做增加操作
            Object replace = map.replace("b1", "java");
            System.out.println(replace);
            System.out.println(map);
            //default boolean replace(K key, V oldValue,V newValue)
        }
        // 删除元素
        public void testRemove() {
            // V remove(Object key) :根据指定key删除集合中对应的值
            Object c = map.remove("c");
            System.out.println(c);
            System.out.println(map);
            // default boolean remove(Object key, Objectvalue) :根据key和value进行删除
            map.remove("b", "bbb1");
            System.out.println(map);
            // void clear() :清空集合中所有元素
            map.clear();
            System.out.println(map);
        }
        // 判断元素
        public void testJudge() {
            // boolean isEmpty() :判断集合是否为空,如果是返回true,否则返回false
            System.out.println(map.isEmpty());
            // boolean containsKey(Object key) :判断集合中是否包含指定的key,包含返回true,否则返回false
            boolean flag = map.containsKey("a");
            System.out.println(flag); // true
            flag = map.containsKey("a1");
            System.out.println(flag); // false
            // boolean containsValue(Object value) :判断集合中是否包含指定的value,包含返回true,否则返回false
            flag = map.containsValue("aaa");
            System.out.println(flag); // true
            flag = map.containsValue("aaa1");
            System.out.println(flag); // false
        }
        // 获取元素
        public void testGet() {
            // int size() :返回集合的元素个数
            int size = map.size();
            System.out.println(size);
            // V get(Object key) :根据Key获取值,如果找到就返回对应的值,否则返回null
            Object val = map.get("a");
            System.out.println(val);
            val = map.get("a1");
            System.out.println(val); // null
            // default V getOrDefault(Object key, VdefaultValue) :根据Key获取值,如果key不存在,则返回默认值
            val = map.getOrDefault("a1", "hello");
            System.out.println(val);
            // Collection<V> values() :返回集合中所有的Value
            Collection values = map.values();
            for (Object value : values) {
                System.out.println(value);
            }
            // Set<K> keySet() :返回集合中所有的Key
            Set set = map.keySet();
            for (Object o : set) {
                System.out.println(o);
            }
        }
        // 迭代元素
        public void testIterator() {
            // 第一种:通过key获取值的方式
            Set keySet = map.keySet();
            Iterator it = keySet.iterator();
            while (it.hasNext()) {
                Object key = it.next();
                Object val = map.get(key);
                System.out.println(key + "=" + val);
            }
            System.out.println("------------------------ ");
            // 第二种:使用for循环
            for (Object key : map.keySet()) {
                System.out.println(key + "=" +
                                   map.get(key));
            }
            System.out.println("------------------------ ");
            // 第三种:使用Map接口中的内部类来完成,在框架中大量使用
            Set entrySet = map.entrySet();
            for (Object obj : entrySet) {
                Map.Entry entry = (Map.Entry) obj;
                System.out.println(entry.getKey() + "=" +
                                   entry.getValue());
            }
        }
    }
    

    说明:在HashMap中键-值允许为空,但键唯一,值可重复。hashMap不是线程安全的。

    2、TreeMap

    是一个有序的集合,默认使用的是自然排序方式。

    public class Person implements Comparable {
        private String name;
        private int age;
        @Override
        public int compareTo(Object o) {
        if (o instanceof Person) {
        Person p = (Person) o;
        return this.age - p.age;
        }
        return 0;
    }
        public Person() {}
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        @Override
        public String toString() {
            return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
        }
    }
    

    测试

    public class TeeMapDemo {
        @Test
        public void testInteger() {
            TreeMap tm = new TreeMap();
            tm.put(3, 333);
            tm.put(2, 222);
            tm.put(11, 111);
            tm.put(2, 222);
            System.out.println(tm);
        }
        @Test
        public void testString() {
            TreeMap tm = new TreeMap();
            tm.put("hello", "hello");
            tm.put("world", "world");
            tm.put("about", "");
            tm.put("abstract", "");
            System.out.println(tm);
        }
        @Test
        public void testPerson() {
            TreeMap tm = new TreeMap(new Comparator(){
                @Override
                public int compare(Object o1, Object o2) {
                    if (o1 instanceof Person && o2
                        instanceof Person) {
                        Person p1 = (Person) o1;
                        Person p2 = (Person) o2;
                        return p1.getAge() - p2.getAge();
                    }
                    return 0;
                }
            });
            tm.put(new Person("张三",18), null);
            tm.put(new Person("李四",17), null);
            System.out.println(tm);
        }
    }
    

    说明:从上面的代码可以发现,TreeMap的使用和TreeSet的使用非常相似,观察HashSet集合的源代码可以看出,当创建 HashSet集合时,其实是底层使用的是HashMap。

    public HashSet() {
    	map = new HashMap<>();
    }
    

    HashSet实际上存的是HashMap的Key。

    3.ConcurrentHashMap

    在Map集合中我们介绍了HashMapTreeMap,在多线程的情况下这些集合都不是线程安全的,因此可能出现线程安全的问题。

    在Java中Hashtable是一种线程安全的HashMapHashtable在方法上与HashMap并无区别,仅仅只是在方法使用了synchronized以此来达到线程安全的目的,我们观察Hashtable的源码。

        public synchronized V get(Object key) {
            Entry<?,?> tab[] = table;
            int hash = key.hashCode();
            int index = (hash & 0x7FFFFFFF) % tab.length;
            for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
                if ((e.hash == hash) && e.key.equals(key)) {
                    return (V)e.value;
                }
            }
            return null;
        }
    

    以上是Hashtable的get源码,可以看出它仅仅只是在方法上添加了锁,这大大降低了线程的执行效率,以牺牲效率的形式来达到目的,这显然不是我们在实际中想要的,因此我们需要一种既能在线程安全方面有保障,在效率上还可以的方法。

    ConcurrentHashMap采用的是分段锁的原理,我们观察源码。

     public V put(K key, V value) {
            return putVal(key, value, false);
        }
    final V putVal(K key, V value, boolean onlyIfAbsent) {
            if (key == null || value == null) throw new NullPointerException();
            int hash = spread(key.hashCode());
            int binCount = 0;
            for (Node<K,V>[] tab = table;;) {
                Node<K,V> f; int n, i, fh;
                if (tab == null || (n = tab.length) == 0)
                    tab = initTable();
                else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                    if (casTabAt(tab, i, null,
                                 new Node<K,V>(hash, key, value, null)))
                        break;                   // no lock when adding to empty bin
                }
                else if ((fh = f.hash) == MOVED)
                    tab = helpTransfer(tab, f);
                else {
                    V oldVal = null;
                    synchronized (f) {
                        if (tabAt(tab, i) == f) {
                            if (fh >= 0) {
                                binCount = 1;
                                for (Node<K,V> e = f;; ++binCount) {
                                    K ek;
                                    if (e.hash == hash &&
                                        ((ek = e.key) == key ||
                                         (ek != null && key.equals(ek)))) {
                                        oldVal = e.val;
                                        if (!onlyIfAbsent)
                                            e.val = value;
                                        break;
                                    }
                                    Node<K,V> pred = e;
                                    if ((e = e.next) == null) {
                                        pred.next = new Node<K,V>(hash, key,
                                                                  value, null);
                                        break;
                                    }
                                }
                            }
                            else if (f instanceof TreeBin) {
                                Node<K,V> p;
                                binCount = 2;
                                if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                               value)) != null) {
                                    oldVal = p.val;
                                    if (!onlyIfAbsent)
                                        p.val = value;
                                }
                            }
                        }
                    }
                    if (binCount != 0) {
                        if (binCount >= TREEIFY_THRESHOLD)
                            treeifyBin(tab, i);
                        if (oldVal != null)
                            return oldVal;
                        break;
                    }
                }
            }
            addCount(1L, binCount);
            return null;
        }
    

    从源码中可以看出ConcurrentHashMap仅仅是在当有线程去操作当前数据的时候添加了锁,因此效率大大提高了。

    在线程安全的情况下提高了效率。

    总结

    本篇文章就到这里了,希望能对你有所帮助,也希望您能够多多关注北冥有鱼的更多内容!

    《新手了解java 集合基础知识(二).doc》

    下载本文的Word格式文档,以方便收藏与打印。