Java基础-Map接口及其实现

需求:有如下学生,学号和学生一一对应。
* 学号1 学生1
* 学号2 学生2
* 学号3 学生3
* 就我们目前所学的知识如何表达呢?
* 我们可以通过二维数组,或者一个Set和一个个List组合来表达。
* 但是,这样的话,我们就创建了多个容器。不好。
* 怎么办呢?java就提供了一种新的集合:Map
* Map:最大的优点就是体现对应关系。
* Map是一个键值对形式的集合。它的数据不再是单个的了,必须同时有键和值组成。

Map和Collection的区别:
Map:是(键值对)双列形式的集合;键必须是唯一的,不能重复,但其值(Value)可以重复
Collection:是单列值形式的集合;Collection体系中的List集合,元素允许重复;Set集合,元素不允许重复

还记得学习集合的原则——学顶层,用底层

那么现在来学习一下Map接口中的共性功能。

A:增加功能
     V put(K key,V value):当key在集合中不存在时,添加元素;当key在集合存在时候,替换元素。
代码:
import java.util.HashMap;
import java.util.Map;

public class Test12 {
    public static void main(String[] args) {
        //创建Map集合,由于Map是一个接口,只能通过new其子类HashMap来创建对象
        Map<String,String> map = new HashMap<String,String>();
        //创建并添加元素  put(key, value)
        map.put("01", "xiaocangyouzi");
        map.put("02", "xiaozemaliya");
        map.put("03", "mashengyoumei");

        System.out.println("map : " +map);
    }
}

结果:
这里写图片描述
分析:
这里的添加的方法与Collection体系中不一样,因为Map是以键值对存在的,所以要put两个内容,一个是key ,一个是value

B:删除功能
     void clear():清除所有键值对数据。这个太暴力,一般不用。
     V remove(Object key):根据指定的键删除键值对。
    代码:
public class Test12 {
    public static void main(String[] args) {
        //创建Map集合,由于Map是一个接口,只能通过new其子类HashMap来创建对象
        Map<String,String> map = new HashMap<String,String>();
        //创建并添加元素  put(key, value)
        map.put("01", "xiaocangyouzi");
        map.put("02", "xiaozemaliya");
        map.put("03", "mashengyoumei");

        //根据指定的key删除元素 remove(object key)
        map.remove("01");
        System.out.println("remove key为01的元素后:" + map);

        //暴力清空
        map.clear();
        System.out.println("clear后的map: " + map);

    }
}

结果:
这里写图片描述
分析:
remove是根据指定的key来删除元素,所以remove 01上的元素,集合里还有其他元素;而clear()是指清空整个集合

C:判断功能
     boolean containsKey(Object key):判断指定的键是否在集合中存在
     boolean containsValue(Object vlaue):判断指定的值是否在集合中存在
     boolean isEmpty():判断集合是否为空
     代码:
import java.util.HashMap;
import java.util.Map;

public class Test12 {
    public static void main(String[] args) {
        //创建Map集合,由于Map是一个接口,只能通过new其子类HashMap来创建对象
        Map<String,String> map = new HashMap<String,String>();
        //创建并添加元素  put(key, value)
        map.put("01", "xiaocangyouzi");
        map.put("02", "xiaozemaliya");
        map.put("03", "mashengyoumei");

        //boolean containsKey(Object key):判断指定的键是否在集合中存在
        System.out.println("map中包含01的key吗? " + map.containsKey("01"));

        //boolean containsValue(Object vlaue):判断指定的值是否在集合中存在
        System.out.println("may中包含\"xiaocangyouzi\"的value吗? " + map.containsValue("xiaocangyouzi"));

        //boolean isEmpty():判断集合是否为空
        System.out.println("这个集合为空吗? " + map.isEmpty());


    }
}

结果
这里写图片描述

获取长度功能:int size()
代码
import java.util.HashMap;
import java.util.Map;

public class Test12 {
    public static void main(String[] args) {
        //创建Map集合,由于Map是一个接口,只能通过new其子类HashMap来创建对象
        Map<String,String> map = new HashMap<String,String>();
        //创建并添加元素  put(key, value)
        map.put("01", "xiaocangyouzi");
        map.put("02", "xiaozemaliya");
        map.put("03", "mashengyoumei");

        //E:长度功能  int size()
        System.out.println("map集合的长度:" + map.size());


    }
}

结果:
这里写图片描述

在学习Collection体系的时候,我们对集合最常见的操作是遍历集合,获取集合中的值。那么我们如何遍历Map集合呢?
下面重点学习一下Map中的获取功能:
D:获取功能
Set

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Test12 {
    public static void main(String[] args) {
        //创建Map集合,由于Map是一个接口,只能通过new其子类HashMap来创建对象
        Map<String,String> map = new HashMap<String,String>();
        //创建并添加元素  put(key, value)
        map.put("01", "xiaocangyouzi");
        map.put("02", "xiaozemaliya");
        map.put("03", "mashengyoumei");

        /*获取功能
         *      Object get(Object key):根据键获取值
         *      Set<K> keySet():所有键的集合
         *      Collection<V> values():所有值的集合
         */
        //获取所有键的集合
        Set<String> keys = map.keySet();
        System.out.println("map集合中所有key:"+keys);

        //获取所有value的集合
        Collection<String> valuse = map.values();
        System.out.println("map集合中所有value:" + valuse);

        //根据指定key获取value
        String value = map.get("01");
        System.out.println("获取key为01的value:" + value); 


    }
}

结果:
这里写图片描述
分析:
为什么获取keySet的时候用的是Set集合,而获取values的时候,却使用Collection呢?
这是和map中的key和value的特性有关的~
在map中,key是不允许重复的,和set中不能存储重复元素的特性相关,所以选择set集合
而map中的value是允许重复的,假如还使用set集合来存储,那么就会冲问题,所以要使用
Collection体系中的List子体系存储。

通过学习上面的获取方法,我们是否可以通过上面获取到的key的集合keySet()还有get(Object key)来遍历Map集合呢?
我们知道,在Map体系中,key是唯一的,只要我们可以获取到key,那么就可以使用get(key)方法来获取所有元素。
那么下面来使用方式一来遍历Map中的元素
代码:

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Test12 {
    public static void main(String[] args) {
        //创建Map集合,由于Map是一个接口,只能通过new其子类HashMap来创建对象
        Map<String,String> map = new HashMap<String,String>();
        //创建并添加元素  put(key, value)
        map.put("01", "xiaocangyouzi");
        map.put("02", "xiaozemaliya");
        map.put("03", "mashengyoumei");

        //遍历方式一
        //首先获取key的集合
        Set<String> keys = map.keySet();
        //通过遍历keys集合,每获取到一个key,就将该key对应的value输出
        for(String key : keys) {
            String value = map.get(key);
            System.out.println(key +"--" + value);
        }

    }
}

结果:
这里写图片描述
分析:
方式一的方法有三个步骤:
A:首先要先获取到map中的所有key,并且存储到一个set集合里面
B:要先获取每一个key对应的value,那么就需要将set集合里面的key一一获取到
C:每获取到一个key,就可以使用map的get(key)方法来获取对应的value
D:将key和对应的value打印出来

遍历Map集合还有第二种方法,刚才在map的获取功能里,我们还有一个功能没有学习,现在就可以用来遍历Map集合。
Set

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Test12 {
    public static void main(String[] args) {
        // 创建Map集合,由于Map是一个接口,只能通过new其子类HashMap来创建对象
        Map<String, String> map = new HashMap<String, String>();
        // 创建并添加元素 put(key, value)
        map.put("01", "xiaocangyouzi");
        map.put("02", "xiaozemaliya");
        map.put("03", "mashengyoumei");

        // 遍历方式二
        // 先获取到Map.Entry<String,String>对象集合
        Set<Map.Entry<String, String>> meSet = map.entrySet();
        // 遍历meSet集合,获取到里面每一个Map.Entry<String,String>对象
        for (Map.Entry<String, String> me : meSet) {
            //使用Map.Entry对象的getKey和getValue方法,分别回去值
            String key = me.getKey();
            String value = me.getValue();
            //输出获取到的key和value
            System.out.println(key + "--" + value);
        }

    }
}

结果:
这里写图片描述
分析:
使用Map.Entry键值对对象遍历Map集合步骤:
A:获取集合中所有的键值对,并封装成Map.Entry对象,存储在Set集合里面
B:遍历set集合,获取每一个Map.Entry对象
C:调用该对象的getKey获取key,调用该对象的getValue获取value
D:输出key和对应的value

Map接口中的自实现类HashMap

HashMap:基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null
键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映
射的顺序,特别是它不保证该顺序恒久不变。

HashMap中的方法和Map中方法基本上保持一致,因此,我们学会了Map中的方法就可以了,这里就直接使用上面学习的一些方法,对HashMap集合进行操作:

需求:
    *HashMap存储键和值。并遍历。
      键:String
      值:String

代码:
package cn.itcast_02;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/*
 * HashMap存储键和值。并遍历。
 * 键:String
 * 值:String
 */
public class HashMapDemo {
    public static void main(String[] args) {
        // 创建集合对象
        HashMap<String, String> hm = new HashMap<String, String>();

        // 创建并添加元素
        hm.put("刘备", "孙尚香");
        hm.put("曹操", "卞氏");
        hm.put("孙权", "步练氏");

        // 遍历
        Set<String> set = hm.keySet();
        for (String key : set) {
            String value = hm.get(key);
            System.out.println(key + "***" + value);
        }

        System.out.println("---------------------");
        // 遍历2
        Set<Map.Entry<String, String>> hmSet = hm.entrySet();
        for (Map.Entry<String, String> me : hmSet) {
            String key = me.getKey();
            String value = me.getValue();
            System.out.println(key + "***" + value);
        }
    }
}

结果:
这里写图片描述

那么如果HashMap中的value为一个自定义对象,该如何操作呢?
HashMap存储键和值。并遍历。
键:String 学号
值:Student (name,age)

Student代码:

package cn.itcast_02;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public int 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) {
        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;
    }

}

HashMapDemo代码:

package cn.itcast_02;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/*
 * HashMap存储键和值。并遍历。
 * 键:String 学号
 * 值:Student (name,age)
 */
public class HashMapDemo2 {
    public static void main(String[] args) {
        // 创建集合对象
        HashMap<String, Student> hm = new HashMap<String, Student>();

        // 创建元素对象
        Student s1 = new Student("李世民", 30);
        Student s2 = new Student("朱元璋", 40);
        Student s3 = new Student("武则天", 50);

        // 添加元素
        hm.put("it001", s1);
        hm.put("it002", s2);
        hm.put("it003", s3);

        // 遍历
        Set<String> set = hm.keySet();
        for (String key : set) {
            Student value = hm.get(key);
            System.out.println(key + "***" + value.getName() + "***"
                    + value.getAge());
        }

        // 遍历2
        Set<Map.Entry<String, Student>> hmSet = hm.entrySet();
        for (Map.Entry<String, Student> me : hmSet) {
            String key = me.getKey();
            Student value = me.getValue();
            System.out.println(key + "***" + value.getName() + "***"
                    + value.getAge());
        }
    }
}

结果:
这里写图片描述

上面的案例的数据都是正常的数据,如果现在,我们往集合中加入一些相同key,但是不同内容的元素时,是否可以保证key的唯一呢?
测试代码:

package cn.itcast_02;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/*
 * HashMap存储键和值。并遍历。
 * 键:String 学号
 * 值:Student (name,age)
 */
public class HashMapDemo2 {
    public static void main(String[] args) {
        // 创建集合对象
        HashMap<String, Student> hm = new HashMap<String, Student>();

        // 创建元素对象
        Student s1 = new Student("李世民", 30);
        Student s2 = new Student("朱元璋", 40);
        Student s3 = new Student("武则天", 50);
        Student s4 = new Student("大傻逼", 50);

        // 添加元素
        hm.put("it001", s1);
        hm.put("it002", s2);
        hm.put("it003", s3);
        hm.put("it003", s4);

        // 遍历
        Set<String> set = hm.keySet();
        for (String key : set) {
            Student value = hm.get(key);
            System.out.println(key + "***" + value.getName() + "***" + value.getAge());
        }
        System.out.println("------华丽的分割线------");

        // 遍历2
        Set<Map.Entry<String, Student>> hmSet = hm.entrySet();
        for (Map.Entry<String, Student> me : hmSet) {
            String key = me.getKey();
            Student value = me.getValue();
            System.out.println(key + "***" + value.getName() + "***" + value.getAge());
        }
    }
}

结果:
这里写图片描述
分析:
这里被替换掉了。。。说明在HashMap中,key是唯一的,当集合中存在一个已有的key,再
往里添加的相同key的元素时,新的元素会替换掉旧的元素。
为什么String类型的数据可以做到保证唯一呢?
我们可以看一下String类的源码。在之前的学习中,我们知道,在hash数据结构中,要想保证唯一性,就
必须重写hashCode方法和equals方法
String类重写hashCode方法
这里写图片描述
String类重写equals方法
这里写图片描述

那么,如果我们需要使用自定义对象作为HashMap中的key的时候,也应该在该对象所属的类中,重写hashCode方法和equals方法:
改进代码如下:
Student.java

package cn.itcast_02;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public int 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) {
        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;
    }

}

HashMapDemo.java

package cn.itcast_02;

import java.util.HashMap;
import java.util.Set;

/*
 * HashMap存储键和值。并遍历。
 * 键:Student (name,age)
 * 值:String 学号
 * 
 * 特有需求:两个对象的成员变量值如果都相同。我们则认为是同一个对象。
 */

public class HashMapDemo3 {
    public static void main(String[] args) {
        // 创建集合对象
        HashMap<Student, String> hm = new HashMap<Student, String>();

        // 创建元素对象
        Student s1 = new Student("李世民", 30);
        Student s2 = new Student("朱元璋", 40);
        Student s3 = new Student("武则天", 50);
        Student s4 = new Student("武则天", 50);
        Student s5 = new Student("武则天", 30);

        // 添加元素
        hm.put(s1, "it001");
        hm.put(s2, "it002");
        hm.put(s3, "it003");
        hm.put(s4, "it004");
        hm.put(s5, "it005");

        // 遍历
        Set<Student> set = hm.keySet();
        for (Student key : set) {
            String value = hm.get(key);
            System.out.println(key.getName() + "***" + key.getAge() + "***"
                    + value);
        }
    }
}

结果:
这里写图片描述
分析:
在代码中,添加了两个我们认为是相同的对象,但是由于在Student类中重写了hashCode和equals方法,所以在HashMap中添加的时候,就没有加入成功。这样就保证了key的唯一性

TreeMap:
TreeMap和HashMap的底层数据结构不同,TreeMap底层的数据结构是红黑树。它保证了元素存储时候按
一定的规则(自然顺序或者自定顺序)存储,并且保证了元素的唯一。
那么TreeMap是通过什么方法保证元素的唯一和顺序的呢?
在学习TreeSet中我们知道,这里有两种方式可以实现,那么TreeMap也是通过这两种方式实现的:
A:对对象所属的类实现Comparable接口,并且重写compareTo方法。
B:调用TreeMap的带参构造方法,并且在参数位置,传入Comparator接口的子类对象,重写
compare方法
在开发中,我们一般会选择第二种方式。遵循一个原则:对修改关闭,对扩展开放。

那么,与上面的HashMap一样,在TreeMap存放String类型的key时,是可以保证唯一和顺序的。因为String类都重写了这些方法和实现了这些接口。

那么我们主要来测试在TreeMap中存放自定义类型数据作为key的情况;
代码:
Student.java

package cn.itcast_03;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

TreeMapDemo.java

package cn.itcast_03;

import java.util.Comparator;
import java.util.Set;
import java.util.TreeMap;

import cn.itcast_02.Student;

/*
 * TreeMap存储自定义对象并遍历。
 * 键:Student
 * 值:String
 * 
 * 如果一个自定义对象做键,用TreeMap集合。
 * 就必须要实现排序。
 * 两种方式:
 * A:让自定义对象所属的类去实现Comparable接口
 * B:使用带参构造方法,创建TreeMap,接收Comparator接口参数。
 */
public class TreeMapDemo2 {
    public static void main(String[] args) {
        // 创建集合对象
        TreeMap<Student, String> tm = new TreeMap<Student, String>(
                new Comparator<Student>() {
                    @Override
                    public int compare(Student s1, Student s2) {
                        // int num = s1.getAge() - s2.getAge();
                        int num = s2.getAge() - s1.getAge();
                        int num2 = (num == 0) ? s1.getName().compareTo(
                                s2.getName()) : num;
                        return num2;
                    }
                });

        // 创建元素对象
        Student s1 = new Student("李世民", 30);
        Student s2 = new Student("朱元璋", 40);
        Student s3 = new Student("武则天", 50);
        Student s4 = new Student("武则天", 50);
        Student s5 = new Student("武则天", 30);

        // 添加元素
        tm.put(s1, "it001");
        tm.put(s2, "it002");
        tm.put(s3, "it003");
        tm.put(s4, "it004");
        tm.put(s5, "it005");

        // 遍历
        Set<Student> set = tm.keySet();
        for (Student key : set) {
            String value = tm.get(key);
            System.out.println(key.getName() + "***" + key.getAge() + "***"
                    + value);
        }
    }
}

结果:
这里写图片描述
分析:
从结果可以看出,有两个相同的对象,后面添加的将前面的覆盖了,也是保证了key的唯一。
另外,我们可以看到是按照年龄的从大到小排序的。

Hashtable和HashMap的区别:
特别注意:
由于历史遗留问题,Hashtable的命名不符合我们现在的规范,但是Hashtable在JDK1.0的时候就出现,
目前还有很多程序时使用该类,所以就没有将名称修改,我们一定要注意。

Hashtable :线程安全,效率低;不允许null 的键和值
HashMap :线程不安全,效率高;允许null的键和值

代码:

package cn.itcast_04;

import java.util.HashMap;

/*
 * Hashtable和HashMap的区别?
 * A:Hashtable线程安全,效率低;不允许null键和值。
 * B:HashMap线程不安全,效率高;允许null键和值。
 */
public class HashtableDemo {
    public static void main(String[] args) {
         HashMap<String, String> hm = new HashMap<String, String>();
         hm.put("hello", null);
         hm.put(null, "world");
         hm.put(null, null);
         System.out.println(hm);

    }
}

结果:
这里写图片描述
分析:
这里没有报错,说明在HashMap中是允许null的键和值的。
为什么只有两个元素呢?因为,有两个key为null,所以后者将前者替换掉了

Hashtable

package cn.itcast_04;

import java.util.Hashtable;

/*
 * Hashtable和HashMap的区别?
 * A:Hashtable线程安全,效率低;不允许null键和值。
 * B:HashMap线程不安全,效率高;允许null键和值。
 */
public class HashtableDemo {
    public static void main(String[] args) {

        Hashtable<String, String> ht = new Hashtable<String, String>();
         ht.put("hello", null);
         ht.put(null, "world");
         ht.put(null, null);
        System.out.println(ht);
    }
}

结果:
这里写图片描述
分析:
NullPointerException 空指针异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值