美文网首页程序员java基础入门
Java基础——集合体系Map详解

Java基础——集合体系Map详解

作者: 阿Q说代码 | 来源:发表于2019-02-27 11:47 被阅读19次

    Java基础——集合体系Map详解

    上文中我们了解了集合体系中的单列集合:Java基础——集合以及Java集合——Set详解,接下来呢就让阿Q带大家一起学习一下双列集合map的使用吧。

    集合体系

    单列集合顶层接口 遍历方式:增强for,Iterator,集合转数组
    Collection
        |-List 存取有序,元素可以重复,有序就有索引,有索引就可以通过索引操作元素。遍历方式:普通for,增强for,Iterator,ListIterator,集合转数组
            |-ArrayList 不安全,效率高,数组结构:增删慢;查询快
            |-LinkedList不安全,效率高,链表结构:增删快;查询慢
            |-Vector    数组结构 安全 效率低
        |-Set 存取无序,元素唯一。遍历方式:增强for,Iterator,集合转数组
            |-HashSet    哈希算法 哈希结构 存取无序 元素唯一
                |-LinkedHashSet
            |-TreeSet   二叉树算法可以排序
    双列集合
    Map双列集合,键唯一,值可以重复,遍历方式:根据键找值,根据键值对找键和值
        |-HashMap底层的哈希结构 保证键的唯一
            |-LinkedHashMap:存入和取出的顺序相同,同时键也是通过哈希算法保证元素唯一性的
        |-TreeMap底层的二叉树结构 保证键的排序和唯一
    

    Map:双列集合

    特点:Map双列集合,Collection是单列集合;键不可以重复,值可以重复;数据结构针对键有效。

    方法:

    • 添加功能:V put(K key,V value):添加元素,如果键是第一次存储,就直接存储元素,返回null;如果键不是第一次存在,就用值把以前的值替换掉,返回以前的值。
    • 删除功能:void clear():移除所有的键值对元素;V remove(Object key):根据键删除键值对元素,并把值返回。
    • 判断功能:boolean containsKey(Object key):判断集合是否包含指定的键;boolean containsValue(Object value):判断集合是否包含指定的值;boolean isEmpty():判断集合是否为空
    • 获取功能:Set<Map.Entry<K,V>> entrySet():获取所有的键值对的集合;V get(Object key):根据键获取值;Set<K> keySet():获取集合中所有键的集合;Collection<V> values():获取集合中所有值的集合
    • 长度功能:int size():返回集合中的键值对的个数

    代码演示:

    public static void demo1() {
        Map<String, Integer> map = new HashMap<>();
        Integer i1 = map.put("张三", 23); //如果键是第一次存储,就直接存储元素,返回null
        Integer i2= map.put("李四", 24);
        Integer i3 = map.put("王五", 25);
        Integer i4 = map.put("赵六", 26);
        Integer i5 = map.put("张三", 26); //相同的键不存储,值覆盖,把被覆盖的值返回
    
        System.out.println(map);        //{赵六=26, 张三=26, 李四=24, 王五=25}
    
        System.out.println(i1);         //null
        System.out.println(i2);         //null
        System.out.println(i3);         //null
        System.out.println(i4);         //null
        System.out.println(i5);         //23如果键不是第一次存在,就用值把以前的值替换掉,返回以前的值
    }
    
    public static void demo2() {
        Map<String, Integer> map = new HashMap<>();
        map.put("张三", 23);
        map.put("李四", 24);
        map.put("王五", 25);
        map.put("赵六", 26);
    
        Integer value = map.remove("张三");   //根据键删除元素,返回键对应的值
        System.out.println(value);          //23
        System.out.println(map.containsKey("张三"));      //false 判断是否包含传入的键
        System.out.println(map.containsValue(100));     //false 判断是否包含传入的值            
        System.out.println(map);                //{赵六=26, 李四=24, 王五=25}
    }
    
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("张三", 23);
        map.put("李四", 24);
        map.put("王五", 25);
        map.put("赵六", 26);
    
        Collection<Integer> c = map.values();           //获取集合中所有值的集合
        System.out.println(c);                  //[26, 23, 24, 25]
        System.out.println(map.size());             //4
    }
    

    遍历
    方式一:通过keySet()获取所有键的集合,然后遍历键的集合,获取到每一个键,然后通过getKey()方法获取每一个值,这样把键和值一一对应的找出来了,就遍历了集合。需要用到的方法:V get(Object key):根据键获取值,
    Set<K> keySet():获取集合中所有键的集合。

    代码演示:

    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("张三", 23);
        map.put("李四", 24);
        map.put("王五", 25);
        map.put("赵六", 26);
    
        //获取所有的键
        /*
        Set<String> keySet = map.keySet();      //获取所有键的集合
        Iterator<String> it = keySet.iterator();    //获取迭代器
        while(it.hasNext()) {               //判断集合中是否有元素
            String key = it.next();         //获取每一个键
            Integer value = map.get(key);       //根据键获取值
            System.out.println(key + "=" + value);
        }
        */
    
        //使用增强for循环遍历
        for(String key : map.keySet()) {        //map.keySet()是所有键的集合
            System.out.println(key + "=" + map.get(key));
        }
    }
    

    方式二:通过entrySet()获取所有的键值对的集合,然后遍历键值对的集合,获取每一个键值对,然后根据键值对来获取每一个键和值,这样把键和值一一对应的找出来了,就遍历了集合。需要用到的方法:Set<Map.Entry<K,V>> entrySet():获取所有的键值对的集合,Map.Entry接口里面的K getKey():获取键值对中的键,V getValue():获取键值对中的值。

    代码演示:

    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("张三", 23);
        map.put("李四", 24);
        map.put("王五", 25);
        map.put("赵六", 26);
    
        //Map.Entry说明Entry是Map的内部接口,将键和值的键值对封装成了Entry对象,并存储在Set集合中
        /*
        Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
        //获取每一个 键值对 对象
        Iterator<Map.Entry<String, Integer>> it = entrySet.iterator();
        while(it.hasNext()) {
            //获取每一个键值对对象
            Map.Entry<String, Integer> en = it.next();  //父类引用指向子类对象
            //Entry<String, Integer> en = it.next();    //直接获取的是子类对象
            String key = en.getKey();           //根据键值对对象获取键
            Integer value = en.getValue();          //根据键值对对象获取值
            System.out.println(key + "=" + value);
        }
        */
    
        for(Entry<String, Integer> en : map.entrySet()) {
            System.out.println(en.getKey() + "=" + en.getValue());
        }
    }
    

    Map.Entry原理:Entry是Map接口里面的一个内部接口, 他的实现类是Map子类里面的一个静态的内部类

    HashMap

    键是如何保证唯一性的呢? 通过哈希算法。 HashSet底层就是通过HashMap的键来完成的,所以说HashMap保证键的唯一性必须要让键的元素所在的类去重写 hashCode()和 equals()方法。

    代码演示:

    public static void main(String[] args) {
        HashMap<Student, String> hm = new HashMap<>();
        //Student作为键,必须重写equals和hashCode() 才能让HashMap保证键的唯一
        hm.put(new Student("张三", 23), "北京");    
        hm.put(new Student("张三", 23), "上海");
        hm.put(new Student("李四", 24), "广州");
        hm.put(new Student("王五", 25), "深圳");    
    
        System.out.println(hm); //{Student [name=张三, age=23]=上海, Student [name=李四, age=24]=广州, Student [name=王五, age=25]=深圳}  去掉了重复的 "张三", 23
    }   
    public class Student{       //重写了equals和HashCode的 Student类
        private String name;
        private int age;
        public Student() {
            super();            
        }
        public Student(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }
        @Override
        public String toString() {
            return "Student [name=" + name + ", age=" + age + "]";
        }
        @Override
        public int hashCode() {             //重写hashCode方法
            final int prime = 31;
            int result = 1;
            result = prime * result + age;
            result = prime * result + ((name == null) ? 0 : name.hashCode());
            return result;
        }
        @Override
        public boolean equals(Object obj) {     //重写equals方法
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Student other = (Student) obj;
            if (age != other.age)
                return false;
            if (name == null) {
                if (other.name != null)
                    return false;
            } else if (!name.equals(other.name))
                return false;
            return true;
        }
    }
    

    LinkedHashMap

    存入和取出的顺序相同,同时键也是通过哈希算法保证元素唯一性的。

    public static void main(String[] args) {
        LinkedHashMap<String, Integer> lhm = new LinkedHashMap<>();
        lhm.put("张三", 23);
        lhm.put("李四", 24);
        lhm.put("赵六", 26);
        lhm.put("王五", 25);
    
        System.out.println(lhm);//{张三=23, 李四=24, 赵六=26, 王五=25}  存取有序
    }
    

    TreeMap

    键是如何保证排序和唯一的呢?通过二叉树算法,TreeSet的底层就是通过TreeMap的键来完成的。

    方式一:所以TreeMap保证键的唯一和排序需要让 TreeMap的键的元素所在的类去实现自然排序Comparable接口;

    方式二:TreeMap保证键的唯一和排序 需要调用TreeMap的有参构造,传入一个比较器 Comparator

    代码演示:

        public static void main(String[] args) {
            TreeMap<Student, String> tm = new TreeMap<>(new Comparator<Student>() { 
                //用TreeMap的有参构造方法 传入比较器
                @Override
                public int compare(Student s1, Student s2) {
                    int num = s1.getName().compareTo(s2.getName());     //按照姓名比较
                    return num == 0 ? s1.getAge() - s2.getAge() : num;
                }
            });
            tm.put(new Student("张三", 23), "北京");
            tm.put(new Student("李四", 13), "上海");
            tm.put(new Student("赵六", 43), "深圳");
            tm.put(new Student("王五", 33), "广州");
            
            System.out.println(tm);
        }
    
    ​   public static void demo1() {
    ​       TreeMap<Student, String> tm = new TreeMap<>();  //用TreeMap的无参构造方法创建的对象
    ​       //Student作为键,必须让Student实现Comparable接口 并且重写CompareTo方法
            tm.put(new Student("张三", 23), "北京");    
    ​       tm.put(new Student("李四", 13), "上海");
    ​       tm.put(new Student("王五", 33), "广州");
    ​       tm.put(new Student("赵六", 43), "深圳");
    ​       
    ​       System.out.println(tm);
    ​   }
        //Student实现了Comparable接口 重写了CompareTo方法
    ​   public class Student implements Comparable<Student> {   
    ​       private String name;
    ​       private int age;
    ​       public Student() {
    ​           super();    
    ​       }
    ​       public Student(String name, int age) {
    ​           super();
    ​           this.name = name;
    ​           this.age = age;
    ​       }
    ​       
    ​       @Override
    ​       public int compareTo(Student o) {       // 重写了CompareTo方法
    ​           int num = this.age - o.age;     //以年龄为主要条件
    ​           return num == 0 ? this.name.compareTo(o.name) : num;
    ​       }
    ​   }
    

    HashMap的嵌套

    学校一年级学生HashMap
        键:一年级对象HashMap   值:班号1
            键:学生对象 值:归属地    
            键:学生对象 值:归属地
        键:一年级对象HashMap   值:班号2
            键:学生对象 值:归属地
            键:学生对象 值:归属地
    

    代码演示:

    public static void main(String[] args) {
        //定义一年级1班
        HashMap<Student, String> hm1 = new HashMap<>();
        hm1.put(new Student("张三", 23), "北京");
        hm1.put(new Student("李四", 24), "北京");
        hm1.put(new Student("王五", 25), "上海");
        hm1.put(new Student("赵六", 26), "广州");
    
        //定义一年级2班
        HashMap<Student, String> hm2 = new HashMap<>();
        hm2.put(new Student("唐僧", 1023), "北京");
        hm2.put(new Student("孙悟空",1024), "北京");
        hm2.put(new Student("猪八戒",1025), "上海");
        hm2.put(new Student("沙和尚",1026), "广州");
    
        //定义双元课堂
        HashMap<HashMap<Student, String>, String> hm = new HashMap<>();
        hm.put(hm1, "一年级1班");
        hm.put(hm2, "一年级2班");
    
        //遍历双列集合
        for(HashMap<Student, String> h : hm.keySet()) {     
            //hm.keySet()代表的是一年级所有的班级集合  h是每个班级
            String value = hm.get(h);           //get(h)根据班级对象获取班级代号value
            //遍历键的双列集合对象
            for(Student key : h.keySet()) { //h.keySet()代表班级中所有的学生对象集合,key是每个学生
                String value2 = h.get(key);     // h.get(key) 根据学生对象找到学生的所在地value2
                System.out.println(key + "=" + value2 + "=" + value);
            }
        }
    
    }
    

    HashMap和Hashtable

    1:HashMap线程不安全,效率高,1.2出现的;Hashtable线程安全的,效率低,1.0出现的。
    2:HashMap可以存null键或null值,Hashtable不可以存储null键或null值。

    代码演示:

    public static void main(String[] args) {
        HashMap<String, Integer> hm = new HashMap<>();
        hm.put(null, 23);       //不报错   
        hm.put("李四", null);     //不报错
        System.out.println(hm);     //{null=23, 李四=null}
    
        /*
        Hashtable<String, Integer> ht = new Hashtable<>();
        ht.put(null, 23);       //编译不报错,运行报错java.lang.NullPointerException
        ht.put("张三", null);     //编译不报错,运行报错java.lang.NullPointerException
        System.out.println(ht);     
        */
    }
    

    Collections

    public static <T extends Comparable<? super T>> void sort(List<T> list);排序:只能给List排序,而且要求被排序的List集合里面存储的元素必须实现Comparable接口(对象只有实现了Comparable接口,才能说明对象具备比较性)

    public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key);在已经排好序的list集合中查找key的索引,List集合中的元素必须实现Comparable接口

    public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll);给求Collection集合中最大的元素,Collection集合的元素必须实现了Comparable接口,因为如果元素不具备比较性 我如何知道谁最大呢?

    public static void reverse(List<?> list);把list集合反转,list集合里面的元素没有任何要求

    public static void shuffle(List<?> list);把list集合里面的元素打乱顺序,list集合里面的元素没有任何要求

    代码演示:

    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("a");
        list.add("c");
        list.add("d");
        list.add("g");
        list.add("f");  //String类实现了Comparable接口重写了CompareTo方法,所以可以求最大值
        //根据list集合存的元素里面的CompareTo方法的比较规则 获取集合中的最大值
        System.out.println(Collections.max(list));//g
        Collections.reverse(list);          //反转集合
        Collections.shuffle(list);          //随机置换,可以用来洗牌
        System.out.println(list);           //[g, f, d, a, c]
    }
    
    public static void demo() {
        ArrayList<String> list = new ArrayList<>();
        list.add("a");
        list.add("c");
        list.add("d");
        list.add("f");
        list.add("g");
        Collections.sort(list); //进行二分法查找之前,必须先把list集合排序    
        System.out.println(Collections.binarySearch(list, "c"));    //1
        System.out.println(Collections.binarySearch(list, "b"));    //-2
    }
    

    好了集合的全部内容就先总结到这了,以后如果遇到什么问题再继续更新。想了解更多学习知识,请关注微信公众号“阿Q说”,获取更多学习资料吧!你也可以后台留言说出你的疑惑,阿Q将会在后期的文章中为你解答。每天学习一点点,每天进步一点点。

    相关文章

      网友评论

        本文标题:Java基础——集合体系Map详解

        本文链接:https://www.haomeiwen.com/subject/whbxuqtx.html