我是怎么跟面试官聊Map集合的

我习惯了无所谓,却不是真的什么都不在乎。                 请关注:源码猎人

目录

简介

Map 框架

Map 接口源码解读

Map 内部类Entry接口源码

AbstractMap 抽象类源码解读

Dictionary 抽象类源码解读

SortedMap 接口源码解读

常见面试题


简介

Map是我们常用集合之一,它用于保存具有映射关系的数据。Map集合中的每一个元素都是由键值对构成。Map中所有的键不能重复,但是值可以重复。

Map 框架

Map 一个高度封装的集合接口,它派生出来的类或接口如下

整个Map框架中用到了Dictionary 抽象类,其他类都是Map直接或间接派生出来。

Map 接口源码解读

public interface Map<K,V>

Map 接口定义了集合的基本行为,及内部元素模型。

Map 未实现方法

// 获取元素个数
int size();
// 是否为空
boolean isEmpty();
// 是否包含key
boolean containsKey(Object key);
// 是否包含value
boolean containsValue(Object value);
// 根据key取值
V get(Object key);
// 添加元素
V put(K key, V value);
// 移除key
V remove(Object key);
// 添加一个Map集合
void putAll(Map<? extends K, ? extends V> m);
// 清除
void clear();
// 获取整个key集合
Set<K> keySet();
// 获取所有值
Collection<V> values();
// 获取所有键值对
Set<Map.Entry<K, V>> entrySet();
// 比较是否一样
boolean equals(Object o);
// 获取集合hashCode
int hashCode();

Map 默认方法

default V getOrDefault(Object key, V defaultValue) {
    V v;
    // key不存在或key对应值为空返回默认值,否有返回实际值
    return (((v = get(key)) != null) || containsKey(key))
            ? v
            : defaultValue;
}

根据key获取值,为空或不存在返回默认值

default V putIfAbsent(K key, V value) {
    V v = get(key);
    // key为空则添加
    if (v == null) {
        v = put(key, value);
    }
    return v;
}

如果key不存在或为空就添加此键值对

default boolean remove(Object key, Object value) {
    Object curValue = get(key);
    // value不一样或key不存在返回false
    if (!Objects.equals(curValue, value) ||
            (curValue == null && !containsKey(key))) {
        return false;
    }
    remove(key);
    return true;
}

如果key对应的值与value一样就删除

default boolean replace(K key, V oldValue, V newValue) {
    Object curValue = get(key);
    // 旧值与key对应值不同时返回false
    if (!Objects.equals(curValue, oldValue) ||
            (curValue == null && !containsKey(key))) {
        return false;
    }
    put(key, newValue);
    return true;
}

使用新值替换旧值,旧值与key对应值不同时返回false

default V replace(K key, V value) {
    V curValue;
    if (((curValue = get(key)) != null) || containsKey(key)) {
        curValue = put(key, value);
    }
    return curValue;
}

当key存在时,用value覆盖原值,不存在时返回空

Map 内部类Entry接口源码

interface Entry<K,V>

方法定义

K getKey();
V getValue();
V setValue(V value);
boolean equals(Object o);
int hashCode();

定义了基本键值对行为

AbstractMap 抽象类源码解读

public abstract class AbstractMap<K,V> implements Map<K,V>

定了所有Map集合的基本骨架

AbstractMap属性

// 键集合
transient Set<K>        keySet;
// 值集合
transient Collection<V> values;

AbstractMap构造函数

protected AbstractMap() {
}

AbstractMap未实现的方法

public abstract Set<Entry<K,V>> entrySet();

AbstractMap实现了Map接口中除entrySet()以外的方法。AbstractMap已实现的方法
在这些已实现的方法中,只需要关注equals和hashcode方法即可,其他方法子类均已覆盖

public boolean equals(Object o) {
    // 地址一样时为true
    if (o == this)
        return true;
    // 不是Map或子类实例时为false
    if (!(o instanceof Map))
        return false;
    // 强转o为Map
    Map<?,?> m = (Map<?,?>) o;
    // m长度为空
    if (m.size() != size())
        return false;

    try {
        // 遍历当前集合所有元素
        Iterator<Entry<K,V>> i = entrySet().iterator();
        while (i.hasNext()) {
            Entry<K,V> e = i.next();
            K key = e.getKey();
            V value = e.getValue();
            // 有任意一个值不像等就返回false
            if (value == null) {
                if (!(m.get(key)==null && m.containsKey(key)))
                    return false;
            } else {
                if (!value.equals(m.get(key)))
                    return false;
            }
        }
    } catch (ClassCastException unused) {
        return false;
    } catch (NullPointerException unused) {
        return false;
    }
    // 循环完返回true
    return true;
}

equals判断方式,先判断类型,在判断长度,之后判断元素,这跟List不同是,List中元素顺序不同会导致equals不同,这儿用的是包含和取值方式比较。第一个元素要么都是空,其他元素可以无顺序限制。

public int hashCode() {
    int h = 0;
    // 遍历所有元素
    Iterator<Entry<K,V>> i = entrySet().iterator();
    while (i.hasNext())
        // 元素hashCode累加
        h += i.next().hashCode();
    return h;
}

hashcode方法是所有元素hashcode求和

Dictionary 抽象类源码解读

Dictionary类,顾名思义,就是字典类,算是早期的Map,不过该类基本上已经废弃了。为什么废弃呢,大致看下Dictionary的源码就知道了。除了常规的get,put请求外,还提供了一些遍历的方法,返回的是Enumeration类型。而Enumeration接口其实算是被Iterator替换了,因为Iterator提供的功能更多,更方便。

public abstract class Dictionary<K,V>

构造函数

public Dictionary() {
}

的方法

abstract public int size();
abstract public boolean isEmpty();
abstract public Enumeration<K> keys();
abstract public Enumeration<V> elements();
abstract public V get(Object key);
abstract public V put(K key, V value);
abstract public V remove(Object key);
public interface NavigableMap<K,V> extends SortedMap<K,V>

方法

// 找到第一个比指定的key小的值
Map.Entry<K,V> lowerEntry(K key);
// 找到第一个比指定的key小的key
K lowerKey(K key);
// 找到第一个小于或等于指定key的值
Map.Entry<K,V> floorEntry(K key);
// 找到第一个小于或等于指定key的key
K floorKey(K key);
//  找到第一个大于或等于指定key的值
Map.Entry<K,V> ceilingEntry(K key);
//找到第一个大于或等于指定key的key
K ceilingKey(K key);
// 找到第一个大于指定key的值
Map.Entry<K,V> higherEntry(K key);
//找到第一个大于指定key的key
K higherKey(K key);
// 获取最小值
Map.Entry<K,V> firstEntry();
// 获取最大值
Map.Entry<K,V> lastEntry();
// 删除最小的元素
Map.Entry<K,V> pollFirstEntry();
// 删除最大的元素
Map.Entry<K,V> pollLastEntry();
//返回一个倒序的Map
NavigableMap<K,V> descendingMap();
// 返回一个Navigable的key的集合,NavigableSet和NavigableMap类似
NavigableSet<K> navigableKeySet();
// 对上述集合倒序
NavigableSet<K> descendingKeySet();

SortedMap 接口源码解读

public interface SortedMap<K,V> extends Map<K,V>

方法

// 排序比较器
Comparator<? super K> comparator();
// 获取其中一段
SortedMap<K,V> subMap(K fromKey, K toKey);
// 获取小于toKey的键值对
SortedMap<K,V> headMap(K toKey);
/// 获取大于fromKey的键值对
SortedMap<K,V> tailMap(K fromKey);
// 返回此映射中当前第一个键
K firstKey();
// 返回映射中当前最后一个键
K lastKey();
// 与父接口Map中的定义一样,但有顺序
Set<K> keySet();
// 与父接口Map中的定义一样,但有顺序(根据key的顺序)
Collection<V> values();
// 与父接口Map中的定义一样,但有顺序(根据key的顺序)
Set<Map.Entry<K, V>> entrySet();    

SortedMap提供了获取最大值与最小值的方法,但对于一个已经排序的数据集,除了最大值与最小值之外,我们还想对任何一个元素,找到比它小的值和比它大的值,还可以按照原有的顺序倒序排序等,这时候就可以用NavigableMap接口

常见面试题

1、你都知道哪些常用的Map集合?
HashMap、HashTable、LinkedHashMap、ConcurrentHashMap。

2、Collection 集合接口和Map接口有什么关系?
没关系,Collection是List、Set父接口不是Map父接口。

3、AbstractMap 作用是什么?

AbstractMap 提供了 Map 的基本实现,使得我们以后要实现一个 Map 不用从头开始,只需要继承 AbstractMap, 然后按需求实现/重写对应方法即可。

4、Map 的键可以重复吗?为什么?

Map 的键不能重复,因为所有Map中,都是通过键来计算元素在集合中的位置,如果键重复,就没办法定位元素,那么所有Map算法都需要修改。

5、Map 的值可以重复吗?为什么?

Map 的值可以重复,应为在计算元素位置时,值不参与运算,所以值重不重复都没关系。

 

  • 6
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

源码猎人

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

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

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

打赏作者

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

抵扣说明:

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

余额充值