Java集合三-Map


前言

Map集合是以 键值对<key , value> 保存数据的,key值具有唯一性


一、HashMap(数组+链表+红黑树)

HashMap以hashCode的值存储数据,大多数可以直接定位,所以有很快的访问速度,查询取值很快,但是遍历顺序是不一定的HashMap最多一条记录键为null,允许多条记录为null,非线程安全,但可以用Collections的synchronizedMap方法,使HashMap具有线程安全的能力,或者使用ConsurrentHashMap,画图展示下HashMap的结构。
我们首先来看下Java7中HashMap的结构

在这里插入图片描述

static class Entry<K,V> implement Map Entry<K,V>{
	final K key;
	V value;
	Entry<K,V> next;
	int hash;
}

整个来看HashMap是一个数组,然后数组中每一个元素是一个单向链表,上图黑色的部分是实体嵌套Entry的实例,Entry包含四个属性:Key,value,hash值和用于单向链表的next。

  1. capacity:当前数组容量,始终保持 2^n,可以扩容,扩容后数组大小为当前的 2 倍。
  2. loadFactor:负载因子,默认为 0.75。
    13/04/2018 Page 51 of 283
  3. threshold:扩容的阈值,等于 capacity * loadFactor

在Java8中,对HashMap进行了一些修改,最大不同就是用了红黑树,所以其由数组+链表+红黑树组成。
 就是在HashMap中我们依据hash值可以很快找到数组具体下标,但是之后的话,需要顺着链表一个个比较下去才能找到我们想要的,时间复杂度取决于链表长度,为O(n)。为了降低开销,在Java8中,当链表中元素超过了8个以后,会把链表转换成为红黑树,在这些位置进行查找的时候可以降低时间复杂度,为O(logN).
Java8的HashMap结构。
在这里插入图片描述
  然后这边讲一个比较生僻的,Java7和Java8在这里有一个特殊的变化,就是HashMap在Java7中是头插法,而Java8是尾插法。好处:Java7在使用的时候有一个Entry结点,它的内部类数据插入时用头插法,导致数据扩容时链表产生一个循环,下一次get时候产生死循环,导致多线程并发不安全,就是我push进去一个值出来没变。
 而Java8中使用尾插法,用Node结点,put做了优化,扩容时候尾插不会产生死循环,但线程不能保证安全。

这是基本的HashMap的认识,由于HashMap太重要了所以我在此处记录一下我的理解。如果有大神看了觉得有问题欢迎指正,评论区欢迎。

我们首先来回忆一下数据结构----Java最基本的数据结构有数组和链表。数组的特点是空间连续(大小固定)、查找(寻址)迅速,但是插入和删除时需要移动元素,所以查询快,增加删除慢。链表恰好相反,可动态增加或减少空间以适应新增和删除元素,但查找时只能顺着一个个节点查找,所以增加删除快,查找慢。那么有没有一个综合数组和链表优点的数据结构呢?那就是哈希表。但是有个很尴尬的局面,虽然哈希表是综合了优点,但是它实际上查找没有数组快,插入删除没有链表快,只能算是一种折中的方式。
实际简单的使用:

public class HashMapDemo {
    public static void main(String[] args) {
        //声明一个HashMap
        HashMap hashMap=new HashMap();
        //插入值
        hashMap.put("Item1","value1");
        hashMap.put("Item2","value2");
        hashMap.put("Item3","value3");
        hashMap.put("Item4","value4");
        //输出值
        System.out.println(hashMap.get("Item1"));
        
        Set set =hashMap.entrySet();
        //迭代器
        Iterator  iterator =set.iterator();

        while(iterator.hasNext()){

            Map.Entry  mapentry = (Map.Entry) iterator.next();
            System.out.println(mapentry.getKey()+"/"+mapentry.getValue());

        }
    }
}

效果:
在这里插入图片描述

二、HashTable(线程安全)

是遗留类,很多映射的常用功能和与HashMap类似,不同的是它承自Dictionary类,并且是线程安全的,任一时间只有一个线程能写HashTable,并发性不如ConcurrentHashMap因为ConcurrentHashMap引入了分段锁,HashTable不建议在新代码中使用,不需要线程安全的场合可以用HashMap替换,需要线程安全的场所可以用ConcurrentHashMap替换。

三、TreeMap(可以排序)

TreeMap实现SortedMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当Iterator遍历TreeMap时,得到的记录是排过序的,如果使用排序的映射建议使用TreeMap。
 在使用TreeMap时候,key必须实现Comparable接口,或在构造TreeMap传入自定义的Comparator,否则会在运行时抛出java.lang.Class.Exception类型的异常。


总结

  HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。Entry就是数组中的元素,每个 Entry 其实就是一个key-value对,它持有一个指向下一个元素的引用,这就构成了链表。
HashMap 在底层将 key-value 当成一个整体进行处理,这个整体就是一个 Entry 对象。HashMap 底层采用一个 Entry[] 数组来保存所有的 key-value 对,当需要存储一个 Entry 对象时,会根据hash算法来决定其在数组中的存储位置,再根据equals方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Entry时,也会根据hash算法找到其在数组中的存储位置,再根据equals方法从该位置上的链表中取出该Entry。
默认是构建一个初始容量为 16,负载因子为 0.75 的 HashMap。也就是说,默认情况下,数组大小为16,那么当HashMap中元素个数超过160.75=12的时候,就把数组的大小扩展为 216=32,即扩大一倍,然后重新计算每个元素在数组中的位置,
而这是一个非常消耗性能的操作,所以如果我们已经预知HashMap中元素的个数,
那么预设元素的个数能够有效的提高HashMap的性能。

HashMap和HashTable的区别

1.Map是一个以键值对存储的接口。Map下有两个具体的实现,分别是HashMap和HashTable.
2.HashMap是线程非安全的,HashTable是线程安全的,所以HashMap的效率高于HashTable.
3.HashMap允许键或值为空,而HashTable不允许键或值为空.

  好了,暂时集合的更新要告一段落了,后面可能更新其它的,例如项目或者其它基础知识,重点在项目上面把,后面我会把我的毕业设计放上来,有需要的自取。
链接: 毕业设计-大数据音乐推荐系统。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Biver__

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

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

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

打赏作者

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

抵扣说明:

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

余额充值