Map集合

Map集合

在数据结构之中,除了进行单个对象的保存,也可以进行二元偶对象的保存(key=value),通过key获取value

Collection集合保存数据的目的是为了输出,Map集合保存数据是为了key的查找

1、Map接口

Map接口是二元偶对象保存的最大父接口,定义如下:

public interface Map<K,V>

该接口是一个独立的父接口,在进行接口对象实例化的时候需要设置Key与Value

Map接口中定义有许多方法,其中核心操作方法有:

No方法名称类型描述
1V put(K key,V value)普通向集合中保存数据
2V get(Object key)普通根据key查询数据
3Set<Map.Entry<K,V>> entrySet()普通将Map集合转为Set集合
4boolean containsKey(Object key)普通查询指定的key是否存在
5Set keySet()普通将Map集合的key转为Set集合
6V remove(Object key)普通根据key删除指定数据

Map集合集合中也提供了static方法

观察Map集合特点

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      Map<String, Integer> map = Map.of("One"  , 1 , "Two" , 2);
      System.out.println(map);
   }
}

在Map集合之中数据保存就是按照“key=value”的形式存储的

使用of()方法时,里面的数据是不允许重复的,如果重复则会出现“IllegalArgumentException“异常,如果设置为null,会出现“NullPointerException”异常

of()方法并不是Map集合的标准用法,开发之中需要通过Map集合的子类进行接口对象实例化

常用子类有:HashMap , Hashtable , TreeMap , LinkedHashMap

2、HashMap子类

HashMap定义:

public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable

HashMap主要特点:无序存储

HashMap继承结构:

在这里插入图片描述

观察Map集合的使用

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      Map<String, Integer> map = new HashMap<>();
      map.put("one" , 1);
      map.put("two" , 2);
      map.put("one" , 3); //key重复
      map.put(null , 0);  //key 为 null
      map.put("zero" , null);  //value 为 null
      System.out.println(map);
   }
}
//{null=0, zero=null, one=3, two=2}

以上的操作形式为Map集合使用的最标准的处理形式

通过HashMap实例化的Map接口可以针对key或value保存null的数据,即便保存数据的key重复,也不会出现异常,而是出现内容替换


Map接口中的put()方法本身是有返回值的,这个返回值是在重复key的情况下返回旧的value

观察put方法

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      Map<String, Integer> map = new HashMap<>();
      System.out.println(map.put("one" , 1)); //key不重复,返回null
      System.out.println(map.put("one" , 3)); //key重复,返回旧数据
   }
}
//null
//1

当使用无参构造时会有一个loadFactor属性,该属性默认值为0.75

static final float DEFAULT_LOAD_FACTOR = 0.75f;
public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}

在使用put()方法进行数据保存时会调用一个putVal()方法,同时会将key进行hash处理(生成hash码)

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

putVal()方法里面依然提供有一个Node节点类进行数据的保存

在使用putVal()方法时会调用resize()方法,此方法是进行容量的扩充


面试题:在进行HashMap的put()操作的时候,如何实现容量扩充的?

  • 在HashMap类中提供有一个“DEFAULT_INITIAL_CAPACITY”常量,作为初始化容量配置,该常量默认大小为16个元素
  • 当保存内容的容量超过了阈值(DEFAULT_LOAD_FACTOR = 0.75f),相当于“容量 * 阈值 = 12”个时就会进行容量扩充
  • 在进行扩充的时候HashMap采用的是成倍的扩充模式,即:每次扩充2倍容量

面试题:请解释HashMap的工作原理?(JDK1.8之后开始的)

  • 在HashMap中进行数据存储依然利用了Node类完成,这种情况就证明可以使用的数据结构只有两种:链表(时间复杂度:“O(n)”)、二叉树(时间复杂度:“O(logn)”)
  • 从JDK 1.8开始,HashMap的实现出现了改变,因为其要适应于大数据时代,所以对于存储发生了变化,在HashMap类内部提供有一个重要的常量:“static final int TREEIFY_THRESHOLD = 8;”。在使用HashMap保存时,如果保存数据个数没有超过阈值(8),则会按照链表的形式存储;如果超过了,则将链表转为红黑树,利用左旋与右旋保证数据的查询性能

3、LinkedHashMap子类的子类

LinkedHashMap是基于链表实现的,保存数据的顺序为增加顺序

LinkedHashMap定义:

public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>

因为是链表保存,所以使用LinkedHashMap时数据量不要特别大,否则时间复杂度攀升

LinkedHashMap继承关系:

在这里插入图片描述

使用LinkedHashMap

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      Map<String, Integer> map = new LinkedHashMap<>();
      map.put("one" , 1);
      map.put("two" , 2);
      map.put("one" , 3); //key重复
      map.put(null , 0);  //key 为 null
      map.put("zero" , null);  //value 为 null
      System.out.println(map);
   }
}
//{one=3, two=2, null=0, zero=null}

LinkedHashMap进行存储的保存数据是添加顺序

4、Hashtable子类

Hashtable定义:

public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, Serializable

Hashtable继承结构:
在这里插入图片描述

观察Hashtable的使用

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      Map<String, Integer> map = new Hashtable<>();
      map.put("one" , 1);
      map.put("two" , 2);
      map.put("one" , 3); //key重复
      System.out.println(map);
   }
}
//{two=2, one=3}

在使用Hashtable时,key和value都不允许为空,否则会报“NullPointerException”异常


面试题:请解释HashMap与Hashtable的区别?

  • HashMap中的方法都属于异步操作(非线程安全),HashMap允许保存null的数据
  • Hashtable中的方法都属于同步操作(线程安全),Hashtable不允许保存null的数据,否则出现“NullPointerException”异常

5、Map.Entry内部接口

List(LinkedList子类)依靠的是链表实现的数据存储,在进行存储数据时将数据保存在Node节点中,在HashMap中也可以见到Node类定义,其本身实现了Map.Entry接口

static class Node<K,V> implements Map.Entry<K,V> {}

因此,所有的key和value的数据都被封装在了Map.Entry接口之中

Map.Entry定义:

public static interface Map.Entry<K,V>

此内部接口中提供有两个重要方法:

  • 获取key:K getKey()
  • 获取value:V getValue()

JDK1.9之后,Map接口中追加了一个新的方法:

  • 创建Map.Entry对象:public static <K,V>Map.Entry<K,V> entry(K k,V v)

创建Map.Entry对象

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      Map.Entry<String, Integer> entry = Map.entry("one", 1);
      System.out.println("获取key:" + entry.getKey());
      System.out.println("获取value:" + entry.getValue());
      System.out.println(entry.getClass().getName()); //使用子类
   }
}
//获取key:one
//获取value:1
//java.util.KeyValueHolder

在整个Map集合里面,Map.Entry的主要作用就是作为Key和Value的包装类型使用,大部分情况下在进行存储时都会将key和value包装为一个Map.Entry对象进行使用

6、利用Iterator输出Map集合

Map集合中没有方法可以直接返回Iterator接口对象

在Map集合里面保存的实际上是一组Map.Entry_接口对象(里面包装的是Key与Value),因此Map依然实现的是单值的保存

在Map中有一个方法:Set<Map.Entry<K,V>> entrySet() , 将全部的Map集合转为Set集合

如果要使用Iterator实现Map集合的输出,必须按照以下步骤处理

  1. 利用Map接口中提供的entrySet()方法将Map集合转为Set集合
  2. 利用Set接口中的Iterator()方法将Set集合转为Iterator接口实例
  3. 利用Iterator进行迭代输出获取每一组的Map.Entry对象,随后通过getKey()与getValue()获取数据

利用Iterator输出Map集合

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      Map<String, Integer> map = new HashMap<>();
      map.put("one" , 1);
      map.put("two" , 2);
      Set<Map.Entry<String, Integer>> set = map.entrySet();   //将Map集合变为Set集合
      Iterator<Map.Entry<String, Integer>> iterator = set.iterator();
      while (iterator.hasNext()) {
         Map.Entry<String, Integer> next = iterator.next();
         System.out.println(next.getKey() + " = " + next.getValue());
      }
   }
}
//one = 1
//two = 2

虽然Map集合支持迭代输出,但是Map的主要用法是实现数据的key的查找操作


如果不使用Iterator,而使用foreach输出,也需要将Map集合转为Set集合

使用foreach输出Map集合

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      Map<String, Integer> map = new HashMap<>();
      map.put("one" , 1);
      map.put("two" , 2);
      Set<Map.Entry<String, Integer>> set = map.entrySet();   //将Map集合变为Set集合
      for (Map.Entry<String , Integer> entry : set) {
         System.out.println(entry.getKey() + " = " + entry.getValue());
      }
   }
}
//one = 1
//two = 2

7、自定义Map的key类型

对于自定义Key类型所在的类中一定要重写hashCode()与equals()方法

使用自定义类作为Key类型

public class JavaDemo {
   public static void main(String[] args) throws Exception {
      Map<Person, String> map = new HashMap<>();
      map.put(new Person("张三" , 18) , "linlin");
      System.out.println(map.get(new Person("张三" , 18)));
      
   }
}
class Person{
   private String name;
   private int age;
   
   public Person(String name, int age) {
      this.name = name;
      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 age == person.age && Objects.equals(name, person.name);
   }
   
   @Override
   public int hashCode() {
      return Objects.hash(name, age);
   }
}
//linlin

虽然允许使用自定义类作为Key的类型,但在开发之中,Map集合的Key常用类型就是:String , Long , Integer , 尽量使用系统类


面试题:如果在进行HashMap数据操作时出现了Hash冲突(Hash码相同),HashMap是如何解决的?

  • 当出现了Hash冲突后为了保证程序的正常执行,会在冲突的位置上将所有Hash冲突的内容转为链表保存

上一篇:集合输出
下一篇:集合工具类
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MyRedScarf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值