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

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将会在后期的文章中为你解答。每天学习一点点,每天进步一点点。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,681评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,710评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,623评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,202评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,232评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,368评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,795评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,461评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,647评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,476评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,525评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,226评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,785评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,857评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,090评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,647评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,215评论 2 341