一、HashMap的底层实现

HashMap底层是基于数组和链表实现的。其中最重要的参数:容量和负载因子。

容量的默认大小事16,负载因子是0.75,当HashMap的size>16*0.75的时候就会发生库容(容量和负载因子都可以自由调整)

Hashmap实现了Map接口,允许放入null元素,出了该类未实现同步外,其余和HashTable大致相同,跟TreeMap不同,该容器不保证冤死顺序,根据需要该容器可能对元素重新哈希,元素的顺序也会被重新打散,因此不同时间迭代同一个HashMap的顺序可能会不同。

二、HashMap的put(key,value)方法

首先会将传入的可以、做hash运算计算出hashCode,然后根据数组长度取模计算出在数组中的index下表

由于在计算机中位运算比取模运算效率高,所以HashMsap规定数组的长度为2n。这样用2n-1做位运算与取模效果一致,并且效率要高出许多

由于数组的长度有限,所以难免出现不同放入key通过运算得到的index相同,这种情况可以利用链表来解决,HashMap会在table[index]出形成链表,采用头插法将数据插入链表中

三、HashMap的get(key)fangfa

get和put类似,也是讲传入的可以计算出index,如果该位置上是一个链表就需要比那里整个链表,通过key.equals(k)来找到对应的元素。

遍历方式:

第一种

Iterator<Map.Entry<String, Integer>> entryIterator=map.entrySet().iterator();
while(entryIterator.hasNext()){
Map.Entry<String,Integer> next=entryIterator.next();
System.err.println("key="+next.getKey()+"value="+next.getValue());
}

第二种

Iterator iterator=map.keySet().iterator();
while(iterator.hasNext()){
String key=iterator.next();
System.err.println("key="+key+"value="+map.get(key));
}

第三种

map.forEach((key,value)->{
System.err.println("key="+key+"value="+value);
});

第一种可以把key value同时取出,第二种还得需要通过key去一次value,效率较低,第三种需要JDK1.8以上,通过外层遍历table,内层遍历链表或红黑树。

四、为什么多线程场景下不推荐使用HashMap

在并发环境下使用HashMap容易出现死循环。并发场景下发生扩容,调用resize()方法里的rehash()时,容易出现环形链表。这样当获取一个不存在的key时,计算出的index正好是环形链表的下标时就会出现死循环

所以,HashMap只能在单线程中使用,并且尽量的预设容量,尽可能的减少扩容发

在JDK1.8中对HashMap进行了优化:当hash碰撞之后写入链表的长度超过阈值(默认为8),链表将会转换成红黑树。假设hash冲突非常严重,一个数组后面接了很长的链表,此时查询的时间复杂度就是O(n)。如果是红黑树,时间复杂度就是O(logn)。大大提高了查询的效率。多线程场景下推荐使用ConcurrentHashMap。

五、HashSet的底层实现

HashSet是对HashMap的简单包装,对HashSet的函数调用都会转换成合适的HashMap方法,因此HashSet的实现非常简单。

成员变量

首先了解下HashSet的成员变量

  private transient HashMap<E,Object> map;

    // Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

发现主要有两个变量:

map:用于存放最终数据

PRESENT:是所有写入map的value值

构造函数

public HashSet() {
map = new HashMap<>();
} public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
} public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
} HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
} public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}

构造函数很简单,利用了HashMap初始化了map

add

 public boolean add(E e) {
return map.put(e, PRESENT)==null;
}

比较关键的就是这个add()方法。可以看出他是将存放的对象当做了HashMap的键,value都是相同的PRESENT.由于HashMap的key是不能重复的,所以每当有重复的值写入到HashSet中只能存放不重复的元素

集合框架-HashMap&HashSet&LinkedHshMap的更多相关文章

  1. Java集合框架之HashSet浅析

    Java集合框架之HashSet浅析 一.HashSet综述: 1.1HashSet简介 位于java.util包下的HashSet是Java集合框架的重要成员,它在jdk1.8中定义如下: publ ...

  2. Java自学-集合框架 HashMap

    Java集合框架 HashMap 示例 1 : HashMap的键值对 HashMap储存数据的方式是-- 键值对 package collection; import java.util.HashM ...

  3. java - day011 - 集合, ArrayList HashMap,HashSet, Iterator 接口, for-each 循环格式

    集合 ArrayList 丑数: 能被3,5,7整除多次, ArrayList     list 接口             | - ArrayList             | - Linked ...

  4. java集合框架之HashSet

    参考http://how2j.cn/k/collection/collection-hashset/364.html#nowhere 元素不能重复 Set中的元素,不能重复重复判断标准是: 首先看ha ...

  5. java集合框架(hashSet自定义元素是否相同,重写hashCode和equals方法)

    /*HashSet 基本操作 * --set:元素是无序的,存入和取出顺序不一致,元素不可以重复 * (通过哈希值来判断是否是同一个对象) * ----HashSet:底层数据结构是哈希表, * 保证 ...

  6. 集合框架—HashMap

    HashMap提供了三个构造函数:       HashMap():构造一个具有默认初始容量 (16) 和默认加载因子 (0.75) 的空 HashMap.       HashMap(int ini ...

  7. [javaSE] 集合框架(HashSet)

    Set:元素是无序,不可重复的 HaseSet:底层数据结构是哈希表 定义一个类Demo 获取Demo对象,system.out.println(demo),打印demo对象,Demo@xxxxxx ...

  8. java集合框架 hashMap 简单使用

    参考文章:http://blog.csdn.net/itm_hadf/article/details/7497462 通常,默认加载因子 (.75) 在时间和空间成本上寻求一种折衷.      加载因 ...

  9. Java自学-集合框架 HashMap和Hashtable的区别

    HashMap和Hashtable之间的区别 步骤 1 : HashMap和Hashtable的区别 HashMap和Hashtable都实现了Map接口,都是键值对保存数据的方式 区别1: Hash ...

随机推荐

  1. [NOI2003]逃学的小孩 (贪心+树的直径+暴力枚举)

    Input 第一行是两个整数N(3 <= N <= 200000)和M,分别表示居住点总数和街道总数.以下M行,每行给出一条街道的信息.第i+1行包含整数Ui.Vi.Ti(1<=Ui ...

  2. JVM内存管理——总结篇

    JVM内存管理--总结篇 自动内存管理--总结篇 内存划分及作用 常见问题 内存划分及作用 程序计数器 线程私有.字节码行号指示器. 执行Java方法,计数器记录的是字节码指令地址:执行本地(Nati ...

  3. java NIO 原理解析之学习笔记

    关键抽象 1.Buffer缓冲区 NIO数据传递模型,是一个连续的内存区域.所有数据传递均通过buffer类处理:NIO提供了字符串.整形.字节.堆等多种缓冲区. 2.Channel(通道) NIO把 ...

  4. git clone下载速度很慢的解决方法

    一直用git clone从github上下载源码学习,但是有时候git clone速度好慢,只有几Kb的速度,按这个速度下载安卓源码的话估计要下一年. 然后我再网上找各种教程,试过通过vps下载git ...

  5. day55 js进阶

    目录 引子 一.BOM操作 1 window对象 2 window子对象 3 history对象 4 location对象(掌握) 5 弹出框 6 计时器相关 二.DOM操作 1 查找标签 1.1 直 ...

  6. java IO流 (二) IO流概述

    1.流的分类* 1.操作数据单位:字节流.字符流* 2.数据的流向:输入流.输出流* 3.流的角色:节点流.处理流 图示: 2.流的体系结构 说明:红框对应的是IO流中的4个抽象基类.蓝框的流需要大家 ...

  7. 数据分析05 /pandas的高级操作

    数据分析05 /pandas的高级操作 目录 数据分析05 /pandas的高级操作 1. 替换操作 2. 映射操作 3. 运算工具 4. 映射索引 / 更改之前索引 5. 排序实现的随机抽样/打乱表 ...

  8. InnoDB表存储结构及keyring加密

    ibdata是InnoDB最重要的系统表空间文件,它记录了InnoDB的核心信息,包括事务系统信息.元数据信息,记录InnoDB change buffer的btree,防止数据损坏的double w ...

  9. 集合-ConcurrentLinkedQueue 源码解析

    问题 (1)ConcurrentLinkedQueue是阻塞队列吗? (2)ConcurrentLinkedQueue如何保证并发安全? (3)ConcurrentLinkedQueue能用于线程池吗 ...

  10. 动手实现 LRU 算法,以及 Caffeine 和 Redis 中的缓存淘汰策略

    我是风筝,公众号「古时的风筝」. 文章会收录在 JavaNewBee 中,更有 Java 后端知识图谱,从小白到大牛要走的路都在里面. 那天我在 LeetCode 上刷到一道 LRU 缓存机制的问题, ...