Map集合

Map集合

HashMap

Hashtable

TreeMap

Map集合:Map接口是Java中保存二元偶对象(键值对)的最顶层接口。Map集合会一次性保存两个对象,且这两个对象的关系为:key=value结构。这种结构最大的特点是可以通过key找到对应的value值。

public interface Map<K,V> {}//其中key值唯一,value值可重复
//key值唯一,通过一个key值一定能找到唯一的一个value值。但一个value值可能对应多个key值

先来学习一下Map接口的重要方法:

1.public V put(K key, V value);//添加元素
2.public V get(Object key);//根据key值取得对应的value值,若key值不存在返回null
3.public Set<K> keySet();//取得集合中所有key值(key不能重复)
4.public Collection<V> values();//取得所有在value值(value值可重复)
5.public Set<Map.Entry<K, V>> entrySet();//将Map集合转换为Set集合(重要)

Map本身是一个接口,要对其实例化还需依赖其子类,Map接口常用的几个子类如下:

HashMap、TreeMap、Hashtable、ConcurrentHashMap。

1.HashMap(重要面试必问)–类比HashSet/无序存储
自定义类作为key判断是否重复时需要覆写hashCode()与equals()方法。–因HashMap底层实现为哈希表+红黑树(JDK1.8),JDK1.8之前采用哈希表。

HashMap的基本实现:

import java.util.*;

public class Test{
    public static void main(String[] args){
        Map<Integer,String> map=new HashMap<>();
        map.put(1,"小新");
        map.put(2,"小葵");
        map.put(3,"娜娜子");
        map.put(2,"野原");
        map.put(4,"小新");
        map.put(null,"小新");
        map.put(5,null);
        map.put(null,null);
        System.out.println(map);
        System.out.println("取得kay=2的value值:"+map.get(2));
        System.out.println("取得所有的key值:"+map.keySet());
        System.out.println("取得所有的value值:"+map.values());
    }
}
//{null=null, 1=小新, 2=野原, 3=娜娜子, 4=小新, 5=null}
//取得kay=2的value值:野原
//取得所有的key值:[null, 1, 2, 3, 4, 5]
//取得所有的value值:[null, 小新, 野原, 娜娜子, 小新, null]

通过运行结果我们可以发现:

(1)key可为null,且最多只能存在一个,存储于集合第一个位置;若设置时key值重复程序也不会报错,只不过当key值重复时调用put()方法变成了value值的更新操作;

(2)value可为null,且可有多个,存储依key值决定;value值可重复。

关于删除Map结合中元素,共有两种方法:

1.V remove(Object key);//通过key值删除value值
//通过key值删除value值时,若value值相等则删除,不等则不删除(返回false)
//此种删除方法的应用场景为人与电话号码的删除,若相等才删除
2.default boolean remove(Object key, Object value) {
        Object curValue = get(key);
        if (!Objects.equals(curValue, value) ||
            (curValue == null && !containsKey(key))) {
            return false;
        }
        remove(key);
        return true;
    }

对两种删除元素方法简单使用:

import java.util.*;

public class Test{
    public static void main(String[] args){
        Map<Integer,String> map=new HashMap<>();
        map.put(1,"小新");
        map.put(2,"小葵");
        map.put(3,"娜娜子");
        map.put(2,"野原");
        map.put(4,"小新");
        map.put(null,"小新");
        map.put(5,null);
        map.put(6,null);
        System.out.println(map);
        System.out.println("删除key=4的元素:"+map.remove(4));
        System.out.println("删除key=2,value=小葵的元素:"+map.remove(2,"小葵"));
    }
}
//{null=小新, 1=小新, 2=野原, 3=娜娜子, 4=小新, 5=null, 6=null}
//删除key=4的元素:小新
//删除key=2,value=小葵的元素:false

通过以上对两种删除方法的简单使用我们可以发现,若删除成功则会返回value值内容,若删除失败则会返回false。

Map集合使用Iterator输出(重点)

Map接口与Collection接口不同,因Collection接口中存在iterator()方法,可以直接通过集合类取得迭代器输出;Map接口本身并没有迭代器相关的方法,故若想使用Iterator输出就必须先将Map集合转换为Set集合再获取迭代器对象输出。

首先我们先来看一下Collection集合与Map集合内部是如何保存数据的:

在这里插入图片描述

由以上结构图可以看出Collection集合中每次保存单个元素;Map集合每次保存一个键值对,但站在宏观角度来看,将键值对抽象为一个Map.Entry对象,那么Map集合实际上也是每次保存一个对象,本质上与Collection集合一致。

Map集合使用迭代器的简单使用:

import java.util.*;

public class Test{
    public static void main(String[] args){
        Map<Integer,String> map=new HashMap<>();
        map.put(1,"小新");
        map.put(2,"小葵");
        map.put(3,"娜娜子");
        map.put(2,"野原");
        map.put(4,"小新");
        map.put(null,"小新");
        map.put(5,null);
        map.put(6,null);
        //1.首先将Map集合转换为Set集合
        Set<Map.Entry<Integer,String>> set=map.entrySet();
        //2.获得Iterator对象
        Iterator<Map.Entry<Integer,String>> iterator=set.iterator();
        //3.输出
        while (iterator.hasNext()){
            //4取得每个Map.Entry对象
            Map.Entry<Integer,String> entry=iterator.next();
            //5.取得key值和value值
            System.out.println(entry.getKey()+"="+entry.getValue());
        }
    }
}
//null=小新
//1=小新
//2=野原
//3=娜娜子
//4=小新
//5=null
//6=null

Map调用Iterator接口输出,走的是一个间接使用的模式,如下图:

在这里插入图片描述

自定义类作为key判断是否重复时需要覆写hashCode()与equals()方法

以上代码使用的是系统类作为key(Integer,String等),实际上我们也可以自定义类作为key,那么此时要判断key值是否重复时就需要覆写Object类的hashCode()方法与equals()方法。

自定义类作为key值的简单实现:

import java.util.*;
class Person{
    private String name;
    private Integer age;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return Objects.equals(name, person.name) &&
                Objects.equals(age, person.age);
    }

    @Override
    public int hashCode() {

        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Test{
    public static void main(String[] args){
       Map<Person,Integer> map=new HashMap<>();
       map.put(new Person("小新",5),1);
       map.put(new Person("娜娜子",0),2);
       map.put(new Person("小新",5),3);
       map.put(new Person(null,null),2);
       Set<Map.Entry<Person,Integer>> set=map.entrySet();
       Iterator<Map.Entry<Person,Integer>> iterator=set.iterator();
       while (iterator.hasNext()){
           Map.Entry<Person,Integer> entry=iterator.next();
           System.out.println(entry.getValue()+"="+entry.getKey());
       }
    }
}
//3=Person{name='小新', age=5}
//2=Person{name='null', age=null}
//2=Person{name='娜娜子', age=0}

自定义类作为key值和value值,作为value值的类不用覆写hashCode()与equals()方法,因value值可重复;但作为key值的自定义类必须同时覆写hashCode()与equals()方法。

import java.util.*;
class Person{
    private String name;
    private Integer age;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return Objects.equals(name, person.name) &&
                Objects.equals(age, person.age);
    }

    @Override
    public int hashCode() {

        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
class Student{
    private String schoolName;

    public Student(String schoolName) {
        this.schoolName = schoolName;
    }

    @Override
    public String toString() {
        return "Student{" +
                "schoolName='" + schoolName + '\'' +
                '}';
    }
}
public class Test{
    public static void main(String[] args){
       Map<Person,Student> map=new HashMap<>();
       map.put(new Person("小新",5),new Student("双叶幼稚园"));
       map.put(new Person("娜娜子",0),new Student("东京女子大学"));
       map.put(new Person("小新",5),new Student(null));
       map.put(new Person(null,null),new Student(null));
       Set<Map.Entry<Person,Student>> set=map.entrySet();
       Iterator<Map.Entry<Person,Student>> iterator=set.iterator();
       while (iterator.hasNext()){
           Map.Entry<Person,Student> entry=iterator.next();
           System.out.println(entry.getKey()+"="+entry.getValue());
       }
    }
}
Person{name='小新', age=5}=Student{schoolName='null'}
Person{name='null', age=null}=Student{schoolName='null'}
Person{name='娜娜子', age=0}=Student{schoolName='东京女子大学'}
2.TreeMap–类比TreeSet/有序存储

TreeMap类比于TreeSet,判断是否重复时是根据key值,故自定义类作为key值时必须实现Comparable接口或传入比较器。作为key值的自定义类属性不可为null。

系统类作为key值value值的简单使用:

import java.util.*;
public class Test{
    public static void main(String[] args){
       Map<Integer,String> map=new TreeMap<>();
       map.put(1,"kala");
       map.put(2,"pp");
       map.put(5,"pp");
       map.put(3,"oo");
       //默认按照compareTo()方法排序(升序),系统类均实现了Comparable接口
       System.out.println(map);
    }
}
//{1=kala, 2=pp, 3=oo, 5=pp}

自定义类作为key值value值的简单使用:

import java.util.*;
class Person{
    private String name;
    private Integer age;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
class Student{
    private String schoolName;

    public Student(String schoolName) {
        this.schoolName = schoolName;
    }

    @Override
    public String toString() {
        return "Student{" +
                "schoolName='" + schoolName + '\'' +
                '}';
    }
}
//比较器
class AscCompare implements Comparator<Person>{

    @Override
    public int compare(Person o1, Person o2) {
        if(o1.getAge()>o2.getAge()){
            return 1;
        }else if(o1.getAge()<o2.getAge()){
            return -1;
        }else{
            return o1.getName().compareTo(o2.getName());
        }
    }
}
public class Test{
    public static void main(String[] args){
       Map<Person,Student> map=new TreeMap<>(new AscCompare());
       map.put(new Person("小新",5),new Student("双叶幼稚园"));
       map.put(new Person("娜娜子",0),new Student("东京女子大学"));
       map.put(new Person("小新",5),new Student(null));
       Set<Map.Entry<Person,Student>> set=map.entrySet();
       Iterator<Map.Entry<Person,Student>> iterator=set.iterator();
       while (iterator.hasNext()){
           Map.Entry<Person,Student> entry=iterator.next();
           System.out.println(entry.getKey()+"="+entry.getValue());
       }
    }
}
//Person{name='娜娜子', age=0}=Student{schoolName='东京女子大学'}
//Person{name='小新', age=5}=Student{schoolName='null'}

通过上文对HashMap与TreeMap的学习,有没有发现似曾相识呢?没错,HashMap、TreeMap与HashSet、HashSet很像,那他们到底有什么关系呢?

Set接口与Map接口有什么关系?(面试)

我们可通过源码一探究竟:

//HashSet
public boolean add(E e) {
    //HashSet的add方法返回的是map.put(),实际上就是将Set集合中的元素全部放置Map集合中的key值中,value值全部为PRESENT,因此Set集合中才不允许存在重复元素,因Set集合中元素实际上存储于Map集合中的key值
        return map.put(e, PRESENT)==null;
    }
private static final Object PRESENT = new Object();//PRESENT为空的Object对象
//TreeSet
public boolean add(E e) {
        return m.put(e, PRESENT)==null;
    }
通过查看源码我们可以得知,Set本质上就是Map,只不过Set的value值均是相同的空的object,先有Map才有的Set,所以Set中元素才不可重复。将Set所有值均放在了Map集合的key值上。所以HashSet完全就是HashMap,TreeSet完全就是TreeMap。
3.Hashtable子类–key值与value值均不允许为null

JDK1.0提供了三大类:Vector、Enumeration、Hashtable。Hashtable是最早实现这种二元偶对象数据结构,后期的设计也让其与Vector一样多实现了Map接口而已。

Hashtable的简单使用:

import java.util.*;
public class Test{
    public static void main(String[] args){
       Map<Integer,String> map=new Hashtable<>();
       map.put(1,"小新");
       map.put(2,"小葵");
       map.put(1,"野原");
       //NullPointerException
//       map.put(null,"娜娜子");
//       map.put(9,null);
       System.out.println(map);
    }
}
//{2=小葵, 1=野原}

通过输出结果可以看出Hashtablekey值与value值均不能为null,否则会报空指针异常。

请解释HashMap与Hashtable的区别(面试)

HashMap:

1.允许key和value为null,且key值有且只有一个为null,value值可有多个;

2.JDK1.2产生

3.异步处理、效率高、线程不安全

4.底层哈希表+红黑树(JDK1.8),JDK1.8之前只有哈希表

Hashtable:

1.key与value均不为null;

2.JDK1.0产生

3.使用方法上加锁,效率低,线程安全

4.底层哈希表

ConcurrentHashMap还在学习中,及HashMap的实现原理,学完了立马补上啦,嘻嘻

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值