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

目录
  • 三、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仅仅是在当有线程去操作当前数据的时候添加了锁,因此效率大大提高了。

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

总结

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

(0)

相关推荐

  • 新手了解java 多线程基础知识(一)

    目录 1.基本概念 2.多线程的创建 3.Thread类方法介绍 总结 1.基本概念 程序.进程.线程 程序(program)是为完成特定任务.用某种语言编写的一组指令的集 合.即指一段静态的代码,静态对象. 进程(process)是程序的一次执行过程,或是正在运行的一个程序.是 一个动态的过程:有它自身的产生.存在和消亡的过程--具有生命 周期.可以理解为一个正在运行的软件. 线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行 路径.可以理解为一个软件的功能. 多线程程序的

  • Java基础之集合Set详解

    一.概述 Set是Java中的集合类,提供了一种无顺序,不重复的集合.常用的子类包括HashSet, TreeSet等. HashSet底层使用HashMap实现,根据元素的hashCode和equals来判断是否为重复元素.当元素的hashCode相同且equals返回true时则认为是重复元素.因为使用了hash算法所以HashSet有很好的添加和访问性能.可以放入null但只能放一个null TreeSet底层使用红黑树实现,Set上的元素被放在一个自动排序的红黑树中.不能放入null 二

  • 新手了解java 多线程基础知识(二)

    目录 一.线程的生命周期 JDK中用Thread.State类定义了线程的几种状态: 二.线程同步 1.为什么要有线程同步 2.synchronized 2.1同步代码块 2.2同步方法 3.Lock锁 总结 一.线程的生命周期 JDK中用Thread.State类定义了线程的几种状态: 要想实现多线程,必须在主线程中创建新的线程对象.Java语言使用 Thread类及其子类的对象来表示线程,在它的一个完整的生命周期中通常 要经历如下的五种状态: 新建:当一个Thread类或其子类的对象被声明并

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

    目录 一.概述 Java集合体系结构: 二.collection 1.List 1)ArrayList 2)LinkedList 2.set 1)HashSet 2)TreeSet 1.实体类 2.测试类: 3.实体类 4.测试类 总结 一.概述 集合是一种长度可变,存储数据的数据结构多样,存储对象多样的一种数据容器.Java中集合可分为:List集合.Set集合.HashMap集合,等. Java集合体系结构: 二.collection collection是Java中所有值存储集合的顶级接口

  • Java基础之集合框架详解

    一.前言 本节学习到的内容有以下5类,不分先后顺序: 集合Collection体系结构 List子类 与集合结合使用的迭代器对象 集合与数组的区别? 常见的一般数据结构整理 二.集合的由来? Collection List ArrayList Vector LinkedList Set hashSet treeSet 在集合没有出现之前,使用对象数组来存储对象,但是,对象数组的长度一旦确定,则不可以发生变化,所以我们希望存在一个容器就像StringBuffer一样存储字符串,同时依据传入的值的个

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

    目录 三.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("

  • 新手了解java 集合基础知识

    目录 一.概述 Java集合体系结构: 二.collection 1.List 1)ArrayList 2)LinkedList 2.set 1)HashSet 2)TreeSet 1.实体类 2.测试类: 3.实体类 4.测试类 三.Map 1.HashMap 2.TreeMap 3.ConcurrentHashMap 总结 一.概述 集合是一种长度可变,存储数据的数据结构多样,存储对象多样的一种数据容器.Java中集合可分为:List集合.Set集合.HashMap集合,等. Java集合体

  • 新手了解java IO基础知识(二)

    目录 一.IO概念 1.什么是输入 2.什么输出(Output) 二.流的分类 1.InputStream(字节流) 2.OutputStream(字节流) 3.Reader(字符流) 4.Writer(字符流) 三.总结(1+2) 1. File类及方法的使用 2.IO流的分类 3.IO流的四个基本类 总结 一.IO概念 I/O 即输入Input/ 输出Output的缩写,其实就是计算机调度把各个存储中(包括内存和外部存储)的数据写入写出 java中用"流(stream)"来抽象表示

  • Java集合基础知识 List/Set/Map详解

    一.List Set 区别 List 有序,可重复: Set 无序,不重复: 二.List Set 实现类间区别及原理 Arraylist 底层实现使用Object[],数组查询效率高 扩容机制 1.6采用(capacity * 3)/ 2 + 1,默认容量为10: 1.7采用(capacity >> 2 + capacity)实现,位移动效率高于数学运算,右移一位等于乘以2倍: 读取速度快,写入会涉及到扩容,所以相对较慢. LinkedList底层采用双向链表,只记录 first 和 las

  • 新手了解java 反射基础知识

    目录 一.反射概述 二.常用api 三.创建Class对象的四种方式 总结 一.反射概述 Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意 对象的内部属性及方法. 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象 (一个类只有一个Class对象),这个对象就包含了完整的类的结构信 息.我们可以通过这个对象看到类的结构,这个对象就像一面镜子,透 过这个镜子看到类的结构,所以我们形象的

  • 新手了解java 异常处理基础知识

    目录 一.异常体系结构 1.什么是异常 2.异常的体系结构 二.异常处理 throw 与 throws 三.自定义异常 运行时异常 编译时异常 总结 一.异常体系结构 1.什么是异常 在java程序运行过程中,发生了一些意料之外的情况就是异常.在java中异常一颗分为两大类: (错误)Error 和 (异常)Exception. 对于(错误)Error来说,我们无法通过程序去解决所有的错误,仅仅可以去尝试捕获这些错误,但是无法处理,往往错误的发生对程序来说是重大的致命性问题,需要通过较大的调整去

  • 新手了解java IO基础知识

    目录 一.File类 1.简介 2.创建方式 3.常用方法 二.IO概念 1.什么是输入 2.什么输出(Output) 三.流的分类 1.InputStream(字节流) 2.OutputStream(字节流) 3.Reader(字符流) 4.Writer(字符流) 四.总结(1+2) 1. File类及方法的使用 2.IO流的分类 3.IO流的四个基本类 总结 一.File类 1.简介 java.io.File类:文件和文件目录路径的抽象表示形式,与平台无关 File 能新建.删除.重命名文件

  • 新手了解java 多线程基础知识

    目录 一.线程的生命周期 JDK中用Thread.State类定义了线程的几种状态: 二.线程同步 1.为什么要有线程同步 2.synchronized 2.1同步代码块 2.2同步方法 3.Lock锁 四.基本概念 五.多线程的创建 六.Thread类方法介绍 总结 一.线程的生命周期 JDK中用Thread.State类定义了线程的几种状态: 要想实现多线程,必须在主线程中创建新的线程对象.Java语言使用 Thread类及其子类的对象来表示线程,在它的一个完整的生命周期中通常 要经历如下的

随机推荐