什么是Map集合

现实生活中,常会看到这样的一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射。Java提供了专门的集合类用来存放这种对象关系的对象,即java.util.Map接口。

Map与Collection集合区别

Collection集合
单列集合,一次只能添加一个元素
有的是有索引,有的没有索引
有的集合可以存储重复的元素,有的则不可以
有的元素是无序的,有的是有序的
Map集合
Map集合是双列集合,由Key和Value组成
Key是不允许重复的,Value是允许重复
Key允许存null值的,但是只能存储唯一的一个

Map集合中常用的子类

  • HashMap

存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。

  • LinkedHashMap

HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。

Map接口中常用的方法

方法名

说明

public V put(K key, V value)

把指定的键与指定的值添加到Map集合中。

public V remove(Object key)

把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。

public V get(Object key)

根据指定的键,在Map集合中获取对应的值。

boolean containsKey(Object key)

判断集合中是否包含指定的键。

public Set<K> keySet()

获取Map集合中所有的键,存储到Set集合中。

public Set<Map.Entry<K,V>> entrySet()

获取到Map集合中所有的键值对对象的集合(Set集合)。

==代码演示==

public class MapDemo {
    public static void main(String[] args) {
        //创建 map对象
        HashMap<String, String>  map = new HashMap<String, String>();

        //添加元素到集合
        map.put("唐三", "小舞");
        map.put("戴沐白", "朱竹清");
        map.put("奥奇卡", "宁荣荣");
        System.out.println(map);

        //String remove(String key)
        System.out.println(map.remove("奥斯卡"));
        System.out.println(map);

        // 想要查看 谁是谁的对象
        System.out.println(map.get("唐三"));
        System.out.println(map.get("戴沐白")); 
        System.out.println(map.get("马红俊"));//找不到返回null
    }
}

==注意事项==

使用put方法时,若指定的键(key)在集合中没有,则没有这个键对应的值,返回null,并把指定的键值添加到集合中;

若指定的键(key)在集合中存在,则返回值为集合中键对应的值(该值为替换前的值),并把指定键所对应的值,替换成指定的新值。

Map集合的遍历

keySet

即通过元素中的键,获取键所对应的值

分析步骤
1. 获取Map中所有的键,由于键是唯一的,所以返回一个Set集合存储所有的键。方法提示:keyset()
2. 遍历键的Set集合,得到每一个键。
3. 根据键,获取键所对应的值。方法提示:get(K key)
代码演示
public class MapDemo01 {
    public static void main(String[] args) {
        //创建Map集合对象 
        HashMap<String,String> map = new HashMap<String,String>();
        //添加元素到集合 
        map.put("唐三", "小舞");
        map.put("戴沐白", "朱竹清");
        map.put("奥奇卡", "宁荣荣");
        //获取所有的键  获取键集
        Set<String> keys = map.keySet();
        // 遍历键集 得到 每一个键
        for (String key : keys) {
            //key  就是键
            //获取对应值
            String value = map.get(key);
            System.out.println(key+"的CP是:"+value);
        }  
    }
}

EntrySet

什么是Entry

Map中存放的是两种对象,一种称为key(键),一种称为value(值),它们在Map中是一一对应关系,这一对对象又称做Map中的一个Entry(项)Entry将键值对的对应关系封装成了对象。即键值对对象,这样我们在遍历Map集合时,就可以从每一个键值对(Entry)对象中获取对应的键与对应的值。

获取Entry

Map集合中通过entrySet() 方法获取Entry对象

public Set<Map.Entry<K,V>> entrySet(): 获取到Map集合中所有的键值对对象的集合(Set集合)。
Entry对象中的常用方法

既然Entry表示了一对键和值,那么也同样提供了获取对应键和对应值得方法

方法名

说明

public K getKey()

获取Entry对象中的键

public V getValue()

获取Entry对象中的值

System.out.println("---------通过iterator-------------");
  Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator();
  while (it.hasNext()){
      //返回的 key value    要先用变量,接受一下迭代器的对象;
      Map.Entry<Integer, String> next = it.next();
      Integer key = next.getKey();
      String value = next.getValue();
      System.out.println(key+": "+value);
  }
  
  System.out.println("----------jdk1.8新特性------------");
  map.forEach((key, value) -> {
      System.out.println(key+": "+value);
  });

Map存储自定义类型元素

需求
每位学生(姓名,年龄)都有自己的家庭住址。那么,既然有对应关系,则将学生对象和家庭住址存储到map集合中。学生作为键, 家庭住址作为值。
需要: 学生姓名相同并且年龄相同视为同一名学生。
编写学生类
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 boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
编写测试类
public class HashMapTest {
    public static void main(String[] args) {
        //1,创建Hashmap集合对象。
        Map<Student,String>map = new HashMap<Student,String>();
        //Map<String,Student>map2=new HashMap<String,Student>();
        //2,添加元素。
        map.put(newStudent("lisi",28), "上海");
        map.put(newStudent("wangwu",22), "北京");
        map.put(newStudent("zhaoliu",24), "成都");
        map.put(newStudent("zhouqi",25), "广州");
        map.put(newStudent("wangwu",22), "南京");        
        //3,取出元素。键找值方式
        Set<Student>keySet = map.keySet();
        for(Student key: keySet){
            String value = map.get(key);
            System.out.println(key.toString()+"....."+value);
        }
    }
}
总结
  • 当给HashMap中存放自定义对象时,如果自定义对象作为key存在,这时要保证对象唯一,必须复写对象的hashCode和equals方法(如果忘记,请回顾HashSet存放自定义对象)。
  • 如果要保证map中存放的key和取出的顺序一致,可以使用java.util.LinkedHashMap集合来存放。

LinkedHashMap

什么是LinkedHashMap

我们知道HashMap保证成对元素唯一,并且查询速度很快,可是成对元素存放进去是没有顺序的,那么我们要保证有序,还要速度快怎么办呢?我们就可以使用LinkedHashMap

LinkedHashMap的特点

有序的,而且key不允许重复
数据结构: 哈希表 + 链表

总结:有序,key唯一

代码演示

public class LinkedHashMapDemo {
    public static void main(String[] args) {
        LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
       map.put("唐三", "小舞");
        map.put("戴沐白", "朱竹清");
        map.put("奥奇卡", "宁荣荣");
        Set<Entry<String, String>> entrySet = map.entrySet();
        for (Entry<String, String> entry : entrySet) {
            System.out.println(entry.getKey() + "  " + entry.getValue());
        }
    }
}

TreeMap

TreeMap类概述

键是红黑树结构,可以保证键的排序和保证唯一

public static void main(String[] args) {
        // 创建集合对象 会对key进行排序,并且唯一
        TreeMap<String, String> tm = new TreeMap<String, String>();
        // 创建元素并添加元素
        tm.put("a", "你好");
        tm.put("c", "世界");
        tm.put("e", "爪哇");
        tm.put("b", "世界2");
        tm.put("e", "爪哇EE");
        // 遍历集合
        Set<String> set = tm.keySet();
        for (String key : set) {
            String value = tm.get(key);
            System.out.println(key + "---" + value);
        }
    }

HashMap和Hashtable的区别?---面试题

/*
HashMap和Hashtable是Java中两种常用的哈希表实现,它们在功能和使用上有一些区别。
1. 线程安全性:
   - HashMap是非线程安全的,不保证在多线程环境下的正确性。
   - Hashtable是线程安全的,通过使用同步方法来确保线程安全。

2. 空值(null)的处理:
   - HashMap允许使用null作为键(key)和值(value),即可以存储null键和null值。
   - Hashtable不允许使用null作为键和值,如果尝试存储null,则会抛出NullPointerException。

3. 继承和实现关系:
   - HashMap是Hashtable的轻量级实现,实现了Map接口,不是线程安全的。
   - Hashtable是Dictionary类的子类,实现了Map接口,并且是线程安全的。

4. 性能:
   - HashMap因为不需要进行线程安全的同步处理,通常比Hashtable的性能更好。
   - Hashtable在多线程环境下具有额外的开销,可能对性能产生负面影响。

总体来说,如果你在单线程环境下使用哈希表,并且可能需要处理空值,推荐使用HashMap。而如果在多线程环境下使用哈希表,或者需要线程安全性,可以选择Hashtable。
 */
public class HashtableDemo {
    public static void main(String[] args) {
        // HashMap<String, String> hm = new HashMap<String, String>();
        Hashtable<String, String> hm = new Hashtable<String, String>();

        hm.put("it001", "hello");
        // hm.put(null, "world"); //NullPointerException
        // hm.put("java", null); // NullPointerException

        System.out.println(hm);
    }
}