第五章:集合04

第三章:集合03

一:Map接口

1. 框架结构

在这里插入图片描述

2. Map接口特点及常用方法

  1. 所存储值的特点:Map用于保存具有映射关系的数据,Map里保存着两组数据:key和value,它们都可以使任何引用类型的数据,但key不能重复。所以通过指定的key就可以取出对应的value。
  2. 存储特点:无序,且不可重复。
  3. 必须掌握的底层实现类:HashMap,TreeMap.
  4. 注意事项: Map 没有继承 Collection 接口, Map 提供 key 到 value 的映射,你可以通过“键”查找“值”。一个 Map 中不能包含相同的 key ,每个 key 只能映射一个 value 。 Map 接口提供 3 种集合的视图, Map 的内容可以被当作一组 key 集合,一组 value 集合,或者一组 key-value 映射。
  5. 常用方法
    在这里插入图片描述
  6. Map集合如何遍历??方法如何使用??

/*
    java.util.Map接口中常用方法
        1.Map集合和Collection没有继承关系
        2.Map集合以key和value的方式存储数据:键值对
            kay和value都是引用数据类型。
            kay和value都是存储对象的内存地址。
            kay起主导地位,value是key的一个附属品。
        3.常用方法
             V put(K key, V value) //向Map集合中添加键值对
             V get(Object key)  //通过key获取value
             void clear()        //清空Map集合
             boolean containsKey(Object key) //判断Map集合中是否包含某个key
             boolean containsValue(Object value)//判断Map中是否包含某个value
             boolean isEmpty()//判断Map集合中元素个数是否为0
             V remove(Object key) //通过key删除键值对
             int size() //获取Map中键值对的个数
             Collection<V> values()//获取Map集合中所有的value,返回一个Colection

             Set<K> keySet()  //获取Map集合所有的key
             Set<Map.Entry<K,V>> entrySet() //将Map集合转换为一个Set集合
             【注意:Map集合通过entrySet()方法转换成的这个Set集合,Set集合中元素类型是Map.Entry<K,V>(内部类)】
                Map集合对象
                    key               value
                    -----------------------
                    1                zhangsan
                    2                  lisi
                    3                 wangwu
                    4                zhaoliu
                    Set set = map.entrySet();
                    Set集合对象
                        1=zhangsan
                        2=lisi
                        3=wangwu
                        4=zhaoliu
 */
public class MapTest01 {
    
    
    public static void main(String[] args) {
    
    
        //创建Map对象
        Map<Integer,String> map = new HashMap<>();
        // V put(K key, V value) //向Map集合中添加键值对
        map.put(1,"zhangsan");
        map.put(2,"lisi");
        map.put(3,"wangwu");
        map.put(4,"zhaoliu");

        //    V get(Object key)  //通过key获取value
        System.out.println(map.get(2));

        //int size() //获取Map中键值对的个数
        System.out.println("键值对数量为:" + map.size());

        // V remove(Object key) //通过key删除键值对
        map.remove(2);
        System.out.println("键值对数量为:" + map.size());

        //boolean containsKey(Object key) //判断Map集合中是否包含某个key
        //boolean containsValue(Object value)//判断Map中是否包含某个value
        System.out.println(map.containsKey(1));
        System.out.println(map.containsValue("zhangsan"));

        // Collection<V> values()//获取Map集合中所有的value,返回一个Colection
        Collection<String> values = map.values();
        for (String s:values) {
    
    
            System.out.println(s);
        }


        // void clear()        //清空Map集合
        map.clear();
        System.out.println("键值对数量为:" + map.size());
        System.out.println(map.isEmpty());

    }
}

/*
    Map集合的遍历
        大数据量使用第二种方式(效率高)
 */
public class MapTest02 {
    
    
    public static void main(String[] args) {
    
    
        /*
          第一种方式:由map.keySet()获取所有的key,通过Set集合中的key,由迭代器遍历
         */
        Map<Integer,String> map = new HashMap<>();

        map.put(1,"zhangsan");
        map.put(2,"lisi");
        map.put(3,"wangwu");
        map.put(4,"zhaoliu");
        //获取所有的key
        Set<Integer> keys= map.keySet();// Set<K> keySet()  //获取Map集合所有的key
        //迭代器
        Iterator<Integer> it= keys.iterator();
        while (it.hasNext()){
    
    
            Integer key = it.next();
            System.out.println(key +"=" +map.get(key));//V get(Object key)  //通过key获取value
        }
        //由keymap.keySet()获取所有的,通过Set集合中的key,增强for循环遍历
        System.out.println("----------------------");
        for (Integer key:keys) {
    
    
            System.out.println(key +"=" + map.get(key));
        }
        System.out.println("----------------------");
        /*
          第二种方式:由Set<Map.Entry<K,V>> entrySet() //将Map集合转换为一个Set集合
         */
        Set<Map.Entry<Integer,String>> set = map.entrySet();
        //遍历set集合
        Iterator<Map.Entry<Integer,String>> it2= set.iterator();
        //直接输出这个node
//        while (it2.hasNext()){
    
    
//            System.out.println(it2.next());
//        }
        //或者通过node内部类中的方法 node.getKey();和node.getValue();获取key和value方法
        while (it2.hasNext()){
    
    
            Map.Entry<Integer,String> node = it2.next();
            Integer key = node.getKey();
            String value = node.getValue();
            System.out.println(key +"=" + value);
        }
        System.out.println("----------------------");
        for (Map.Entry<Integer,String> node:set) {
    
    
            System.out.println(node.getKey()+ "---->" + node.getValue());
        }
    }
}

3. Map接口实现类之HashMap集合

  1. 底层数据结构:HashMap集合底层是哈希表/散列表的数据结构

  2. 存储特点:无序,且不可重复(k值一定不能相同)。

  3. 如何保证:无序和不可重复。
    无序:由于底层的数据结构是数组加单向链表,因此我们在存储时底层并不知道挂在哪一个单向链表中,而取值是是先根据数组下标,在第一个数组的单向链表中取数据,所以会无序。
    不可重复:底层的k值一定不可重复。

  4. 存取值的原理是什么。

存:put(k,v)实现原理:
    第一步:首先将k,v封装到Node对象当中(节点)。
    第二步:调用K的hashCode()方法得出hash值。
    第三步:通过哈希表函数/哈希算法,将hash值转换成数组的下标,下标位置上如果没有任何元素,
           就把Node添加到这个位置上。如果下标位置有元素(即存在hash冲突),此时,就会拿着k和
           链表上每个节点的k进行equal(因为底层存储的并不是基本数据类型,而是数据类型的地址值
           (可以理解引用数据类型)如果所有的equals方法返回都是false(即k值不相同),那么这个新的
           节点将被添加到链表的末尾,如其中有一个equals返回了true(即k值相同),那么这个节点的
           value将会被覆盖。。
 取:map.get(k)实现原理:
    第一步:先调用k的hashCode()方法得出哈希值,并通过哈希算法转换成数组的下标。
    第二步:通过上一步哈希算法转换成数组的下标之后,在通过数组下标快速定位到某个位置上。
    第三步:如果该位置上没有链表(红黑树)则返回null。如果这个位置上有单向链表,那么它就会拿着
           参数K和单向链表上的每一个节点的K进行equals,如果所有equals方法都返回false,则get方法返回null。
           如果其中一个节点的K和参数K进行equals返回true,那么此时该节点的value就是我们要找的value了,
           get方法最终返回这个要找的value。
  1. 为什么k值类型的类(如k部分为String)必须同时重写hashCode和equals方法。
    假设第一步:k部分存储的值为Student对象:代码如下所示:

    class Student{
          
          
        private int no;
        private String name;
    }
    

    第二步:创建Student对象s1和s2.

        Student1 s1 = new Student(111,"zhangsan");
        Student  s2 = new Student(111,"zhangsan");
    

    第三步:此时我们往HashMap集合中存储元素。

    Map<Student,Integer> hashMapTest = new HashMap<>();
    map1.put(s1,222);
    map1.put(s2,333);
    

    第四步:前面说到,存储Map中的元素一定是无序且不可重复的(如何做到不可重复:因为Map中的元素都是一键一值的,如果k相同,那么一定是对原值得覆盖)。
    因此,从上面的例子我们可以明显的看到s1和s2是相同的,所以存储进去是s2对s1的覆盖,但是s1和s2是两个对象,由于Student没有重写hashCode方法,所以它们是默认继承Object类的hashCode方法,由于它们是两个对象,所以JVM默认它们是不相等的。
    但是我们从内容中发现,它们其实是相同的,此时如果我们重写hashCode 方法则在hash值方面它们是相等的。
    在put(k,v)方法中当K中的hash值相等的时候,它们一定是在同一个链表结构中,此时我们利用equals方法进行对比,但是我们在Student类中没有对equals方法重写,默认继承Object类,
    但是 Object类对比的是两个对象的内存地址,但是这两个对象的内存地址不相等,如果我们不重写equals方法,发现s2能够存储进去,但没有对si覆盖,所以这是不对的。
    综上所述,当使用 HashMap集合时,我们必须要保证所存储的内容的对象必须重写了hashCode和equals方法。

4. Map接口实现类之TreeMap集合

在这里插入图片描述

  1. 底层数据结构:TreeMap集合底层红黑树(个人认为是平衡二叉树)的数据结构

  2. 存储特点:无序,且不可重复(k值一定不能相同),但确实可排序的。

  3. 如何保证:无序和不可重复。
    无序:由于底层的数据结构是数组加单向链表,因此我们在存储时底层并不知道挂在哪一个单向链表中,而取值是是先根据数组下标,在第一个数组的单向链表中取数据,所以会无序。
    不可重复:底层的k值一定不可重复。

  4. 存取值的原理是什么。

         如HashMap集合相差不大,其实我们一般很少用Map集合,但我们大多数用的是Set集 合,但是
    无论是HashSet集合还是TreeSet集合,其底层都是HashMap和TreeMap集合,即我们在存储时
    是将值存储在Map集合中的k中。
    
  5. TreeMap集合是如何可排序的。

           由于TreeMap集合时一个自平衡二叉数,所以它在内部是可以进行比较(是有大小的)的,如何
    进行比较??
        首先:TreeMap集合是对存储进去元素的k值进行比较,如果我们的k值是String,或者是包装类型,
     当然可以,因为它们是可以进行比较的。
        同理:如HashMap集合一样,如果我们的k值是我们自己制作的引用数据类型呢??如何对应进行
        比较??是按照什么进行比较??比较的规则是什么??如果我们没有自己
     制定规则的话JVM会不知道如何进行比较,自然就构不成书,此时会出现异常:
     java.lang.ClassCastException:class 进阶.集合05.Map集合.HashMap.Person cannot 
     be cast to class java.lang.Comparable
    
    import java.util.TreeSet;
    
    /*
        对于自定义的类型来说,Tree可以排序吗??
            以下程序对于Person类型来说,无法排序。因为没有指定Person对象之间的比较规则。
            谁大谁小并没有说明。
        java.lang.ClassCastException:
         class 进阶.集合05.Map集合.HashMap.Person cannot be cast to class java.lang.Comparable
        出现这个异常的原因是:
            Person类没有实现java.lang.Comparable接口。
     */
    public class TreeSetTest02 {
          
          
        public static void main(String[] args) {
          
          
            Person p1 = new Person(32);
            Person p2 = new Person(20);
            Person p3 = new Person(30);
            Person p4 = new Person(25);
            TreeSet<Person> people = new TreeSet<>();
            people.add(p1);
            people.add(p2);
            people.add(p3);
            people.add(p4);
            for (Person p :people) {
          
          
                System.out.println(p);
            }
        }
    }
    class Person{
          
          
        int age;
    
        public Person(int age) {
          
          
            this.age = age;
        }
    
        @Override
        public String toString() {
          
          
            return "Person{" +
                    "age=" + age +
                    '}';
        }
    }
    

综上所述,我们在自己制定k值得数据类型的时候,并将其存放在TreeMap集合中必须制定相应的比较规则,否在在存储时会报错。那么如何制定比较规则呢??有两种,如下所示。

TreeMap集合比较规则法一

在创建类时,继承java.lang.Comparable接口,并在类中重写了 compareTo()方法,如下所示:

import java.util.TreeSet;
public class TreeSetTest03 {
    
    
    public static void main(String[] args) {
    
    
        CusTomer c1 = new CusTomer(32);
        CusTomer c2 = new CusTomer(20);
        CusTomer c3 = new CusTomer(30);
        CusTomer c4 = new CusTomer(25);
        TreeSet<CusTomer> cusTomers = new TreeSet<>();
        cusTomers.add(c1);
        cusTomers.add(c2);
        cusTomers.add(c3);
        cusTomers.add(c4);
        for (CusTomer c :cusTomers) {
    
    
            System.out.println(c);
        }
    }
}
//放在TreeSet集合中的元素,需要实现java.lang.Comparable接口
//并且实现compareTo方法。equals可以不写
class CusTomer implements Comparable<CusTomer>{
    
    
    int age;
    public CusTomer(int age) {
    
    
        this.age = age;
    }
    @Override
    public String toString() {
    
    
        return "CusTomer{" +
                "age=" + age +
                '}';
    }

    //需要在这个方法中编写比较的逻辑,按照说明进行比较
    //k.compareTo(t.key)
    //拿着参数k和集合中的每一个k进行比较,返回值可能是>0 ,<0 =0
    @Override
    public int compareTo(CusTomer o) {
    
    
        //比较规则最终还是有程序员指定的:例如按照年龄升虚。或者按照年龄降序
//        int age1 = this.age;
//        int age2 = o.age;
//        if (age1 == age2){
    
    
//            return 0;
//        }else if (age1 > age2){
    
    
//            return 1;
//        }else {
    
    
//            return -1;
//        }
        return this.age - o.age;
    }
}
TreeMap集合比较规则法二

自己创建比较器


import java.util.Comparator;
import java.util.TreeSet;
/*
    最终的结论:
        放到TreeSet集合或者TreeMap集合key部分的元素要想做到排序,包括两种方式:
        第一种:放到集合中元素的对象类型,自己实现了java.lang.Comparable接口。
        第二种:放到集合中的自定义对象,在构造TreeSet或者TreeMap集合的时候给他传入一个比较器对象,
                或者在自定义对象实现了java.lang.Comparable接口
    Comparable和Comparator怎么选择
        当比较规则不会发生改变的时候,或者说当比较规则只有1个的时候,建议使用Comparable接口
        如果比较规则有多个,并且需要多个比较规则之间频繁切换,建议使用Comparator接口

        Comparator接口接口设计符合OCP原则
 */

public class TreeSetTest04 {
    
    
    public static void main(String[] args) {
    
    
        //创建TreeSet集合的时候,需要使用比较器
        //TreeSet<WuGui> wuGuis = new TreeSet<>();

        //TreeSet<WuGui> wuGuis = new TreeSet<>(new WuGuiComparator());

        //匿名内部内的方式
        TreeSet<WuGui> wuGuis = new TreeSet<>(new Comparator<WuGui>() {
    
    
            @Override
            public int compare(WuGui o1, WuGui o2) {
    
    
                return o1.age-o2.age;
            }
        });

        wuGuis.add(new WuGui(1000));
        wuGuis.add(new WuGui(800));
        wuGuis.add(new WuGui(810));

        for (WuGui wuGui:wuGuis) {
    
    
            System.out.println(wuGui);
        }
    }
}
class WuGui{
    
    
    int age;
    public WuGui(int age) {
    
    
        this.age = age;
    }
    @Override
    public String toString() {
    
    
        return "WuGui{" +
                "age=" + age +
                '}';
    }
}
class WuGuiComparator implements Comparator<WuGui> {
    
    
    @Override
    public int compare(WuGui o1, WuGui o2) {
    
    
        //指定比较规则
        //按照年龄排序
        return o1.age - o2.age;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_28384023/article/details/109692214
今日推荐