Java 集合框架(Map接口)

本文详细解析了Java Map接口的底层实现,特别是HashMap的内部结构,包括如何通过EntrySet进行遍历。Map底层基于哈希表存储,EntrySet用于方便遍历,同时提供了KeySet和Values集合。此外,文章还介绍了Map的主要方法及遍历策略。
摘要由CSDN通过智能技术生成

Set底层是Map,区别在于Set将每个Key对应的Value,都统一为

private static final Object PRESENT = new Object();

 

JDK8 的Map特点:

1.Map和Collection属于同一级别,前者为双列集合,后者为单列集合,Map是用于保存具有映射关系的数据:Key-Value。

2. Map中的key和 value 可以是任何引用类型的数据,会封装到 HashMap$Node 对象中

3. Map 中的 key 不允许重复,原因和 HashSet 一样,当加入相同的key相当于进行替换

4. Map 中的 value 可以重复

5. Map 的 key 可以为 null, value 也可以为 null ,注意 key 为 null,只能有一个,value 为 null ,可以多个

6. 经常用 String 类作为 Map 的 key
 
7. key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到对应的 value
 
8.与hashset一样不保证映射顺序,因为底层是哈希表存储
 
9.hashmap没有实现同步,线程不安全,方法没有做同步互斥,没有synchronized关键字
 
Map存放数据的key-value:一对key-value就是放在一个HashMap$Node里面,又因为Node实现了Entry接口,有些书上也说一对k-v就是一对Entry
//添加元素时,调用putVal函数
public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
//将元素构建成Node类型结点其放入tab[i] = newNode(hash, key, value, null);

//也就是说最终存放的K-V实际上是:HashMap$Node类型的结点: 
Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
        return new Node<>(hash, key, value, next);
    }

//为了遍历方便,还会创建一个EntrySet集合,该集合存放的元素类型为Entry,而一个Entry对象就包含了一个K-V,即创建了一个EntrySet<Entry<K, V>> 

transient Set<Map.Entry<K,V>> entrySet;
//输出entrySet的运行时类型
Set set = map.entrySet();

System.out.println(set.getClass()); // 输出为HashMap$EntrySet
//在entrySet中,定义的元素类型为Map.Entry,但是存放的数据类型为HashMap$Node
//运行以下代码,印证
for (Object obj: set){
    System.out.println(obj.getClass);//输出为HashMap$Node
}//能这样做的原因就是HashMap$Node 和 Map.Entry有如下关系:
//static class Node<K,V> implements Map.Entry<K,V> 接口多态

// 当把Node放入到EntrySet中时就方便遍历了
//因为底层的EntrySet提供了两个重要的方法:
K getKey();
V getValue();

//因此当我们在遍历Map的时候就可以:
for (Object obj: set){
    //先向下转型
    Map.Entry entry = (Map.Entry)obj;
    System.out.println(entry.getKey() + "-" + entry.getValue());
}

 

//当我们传入的k,v是一个对象的时候,debug更能直观的看到entryset和node的关系
public static void main(String[] args) {
        HashMap map = new HashMap();

        map.put(new A("kk"), new B(15));
        map.put(new A("jj"), new B(51));
    }

debug信息如下:table表地址和entryset中的table指向同一个地址,table表中的Node结点地址和entryset 的table表中结点指向同一地址

总结 :

Map存储元素的底层仍然是存储的哈希表table+链表(红黑树),但是为了方便遍历,在底层又实现了一个EntrySet,将存储的Node结点k-v组成一个个Entry,将这些entry存放到entryset中,同时对于该entryset,提供了两个方法K getKey();V getValue();,来获取对应的k - v,并且为了能够更严苛的遍历条件,比如只想得到key的集合,那么entryset提供了一个Set类keySet(Set的实现子类)用来遍历所有的key,还提供了一个Collection类型的集合Values(Collection的实现子类)来遍历value

final class KeySet extends AbstractSet<K>
final class Values extends AbstractCollection<V>
//通过如下代码印证:
Set set1 = map.keyset();
System.out.println(set1.getClass());//Java.util.HashMap$KeySet

Collection values = map.values();
System.out.println(values.getClass());//java.util.HashMap$Values

Map接口常用方法:

public static void main(String[] args) {
        HashMap map = new HashMap();
        //put
        map.put("邓超", new A("KK"));//OK
        map.put("邓超", "孙俪");
        map.put("aa", "bb");
        map.put("mm", "bb");
        //remove, 根据键删除映射关系
        map.remove("邓超");
        System.out.println("map = " + map);
        //get 根据键获取值
        Object val = map.get("aa");
        System.out.println("val = " + val);
        //size 获取元素个数
        System.out.println("k-v = " + map.size());
        //isEmpty:判断个数是否为0
        System.out.println(map.isEmpty());//F
        //clear,清空键值对
        //map.clear();
        //containsKey:查找键是否存在,containsValue:查找值是否存在
        System.out.println("结果 = " + map.containsKey("aa"));
        System.out.println("结果 = " + map.containsValue("bb"));
    }

 

遍历Map:

 //遍历方式
        //1.获取键集合,遍历
        Set set = map.keySet();
        //增强for
        for (Object obj : set){
            System.out.println(obj + "-" + map.get(obj));
        }
        //迭代器
        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            Object key = iterator.next();
            System.out.println(key + "+" + map.get(key));
        }
        //错误的遍历方式
//        while(iterator.hasNext()){
//            System.out.println(iterator.next() + "+" + map.get(iterator.next()));
//        }
        //2.通过entryset
        Set set1 = map.entrySet();
        for(Object o : set1){
            //向下转型
            Map.Entry entry = (Map.Entry)o;
            System.out.println(entry.getKey() + "-" + entry.getValue());
        }
        //3.通过entryset的迭代器
        Iterator iterator1 = map.entrySet().iterator();
        while(iterator1.hasNext()){
            Map.Entry entry = (Map.Entry)iterator1.next();
            System.out.println(entry.getKey() + "-" + entry.getValue());
        }

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Meikesibondwell

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

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

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

打赏作者

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

抵扣说明:

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

余额充值