Map存储容量及内存占用测试

 

Integer a = 1;
long start = 0;
long end = 0;
// 先垃圾回收
System.gc();
start = Runtime.getRuntime().freeMemory();
HashMap map = new HashMap();
for (int i = 0; i < 1000000; i++) {
map.put(i, a);
}
// 快要计算的时,再清理一次
System.gc();
end = Runtime.getRuntime().freeMemory();
System.out.println("一个HashMap对象占内存:" + (end - start));

 

==================================================================================================================================================================================================================================================================================================================================================================

HashMap 和 Hashtable的区别

 

 

1、HashMap是非线程安全的,HashTable是线程安全的。

 

2、HashMap的键和值都允许有null值存在,而HashTable则不行。

 

3、因为线程安全的问题,HashMap效率比HashTable的要高。

 

 

 

HashMap 内部存储结构:

 

Java中数据存储方式最底层的两种结构,一种是数组,另一种就是链表,

 

数组的特点:连续空间,寻址迅速,但是在删除或者添加元素的时候需要有较大幅度的移动,所以查询速度快,增删较慢。

 

而链表正好相反,由于空间不连续,寻址困难,增删元素只需修改指针,所以查询慢、增删快。

 

哈希表中和了链表和数组的有事。哈希表具有较快(常量级)的查询速度,及相对较快的增删速度,所以很适合在海量数据的环境中使用。一般实现哈希表的方法采用“拉链法”,我们可以理解为“链表的数组”,如下图:

 

我们可以发现哈希表是由数组+链表组成的,一个长度为16的数组中,每个元素存储的是一个链表的头结点。那么这些元素是按照什么样的规则存储到数组中呢。一般情况是通过hash(key)%len获得,也就是元素的key的哈希值对数组长度取模得到。比如上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12。所以12、28、108以及140都存储在数组下标为12的位置。它的内部其实是用一个Entity数组来实现的,属性有key、value、next。

 

 

二、HashTable的内部存储结构

 

HashTable和HashMap采用相同的存储机制,二者的实现基本一致,不同的是:

 

1、HashMap是非线程安全的,HashTable是线程安全的,内部的方法基本都是synchronized。

 

2、HashTable不允许有null值的存在。

 

在HashTable中调用put方法时,如果key为null,直接抛出NullPointerException。其它细微的差别还有,比如初始化Entry数组的大小等等,但基本思想和HashMap一样。

 

三、HashTable和ConcurrentHashMap的比较

 

如我开篇所说一样,ConcurrentHashMap是线程安全的HashMap的实现。同样是线程安全的类,它与HashTable在同步方面有什么不同呢?

 

之前我们说,synchronized关键字加锁的原理,其实是对对象加锁,不论你是在方法前加synchronized还是语句块前加,锁住的都是对象整体,但是ConcurrentHashMap的同步机制和这个不同,它不是加synchronized关键字,而是基于lock操作的,这样的目的是保证同步的时候,锁住的不是整个对象。事实上,ConcurrentHashMap可以满足concurrentLevel个线程并发无阻塞的操作集合对象

====================================================================================================================================================================================================================================================================================================================================================================

 

总结一下hashMap和hashtable方面的知识点吧:

 1.  关于HashMap的一些说法:

 a)  HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。HashMap的底层结构是一个数组,数组中的每一项是一条链表。

 b)  HashMap的实例有俩个参数影响其性能: “初始容量” 和 装填因子。

 c)  HashMap实现不同步,线程不安全。  HashTable线程安全 d)  HashMap中的key-value都是存储在Entry中的。

e)  HashMap可以存null键和null值,不保证元素的顺序恒久不变,它的底层使用的是数组和链表,通过hashCode()方法和equals方法保证键的唯一性

f)  解决冲突主要有三种方法:定址法,拉链法,再散列法。HashMap是采用拉链法解决哈希冲突的。 注: 链表法是将相同hash值的对象组成一个链表放在hash值对应的槽位;    用开放定址法解决冲突的做法是:当冲突发生时,使用某种探查(亦称探测)技术在散列表中形成一个探查(测)序列。 沿此序列逐个单元地查找,直到找到给定 的关键字,或者碰到一个开放的地址(即该地址单元为空)为止(若要插入,在探查到开放的地址,则可将待插入的新结点存人该地址单元)。   拉链法解决冲突的做法是: 将所有关键字为同义词的结点链接在同一个单链表中 。若选定的散列表长度为m,则可将散列表定义为一个由m个头指针组成的指针数 组T[0..m-1]。凡是散列地址为i的结点,均插入到以T[i]为头指针的单链表中。T中各分量的初值均应为空指针。在拉链法中,装填因子α可以大于1,但一般均取α≤1。拉链法适合未规定元素的大小。   

  2.  Hashtable和HashMap的区别:

a)   继承不同。  public class Hashtable extends Dictionary implements Map public class HashMap extends  AbstractMap implements Map

b)  Hashtable中的方法是同步的,而HashMap中的方法在缺省情况下是非同步的。在多线程并发的环境下,可以直接使用Hashtable,但是要使用HashMap的话就要自己增加同步处理了。

c)  Hashtable 中, key 和 value 都不允许出现 null 值。 在 HashMap 中, null 可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为 null 。当 get() 方法返回 null 值时,即可以表示 HashMap 中没有该键,也可以表示该键所对应的值为 null 。因此,在 HashMap 中不能由 get() 方法来判断 HashMap 中是否存在某个键, 而应该用 containsKey() 方法来判断。

d)  两个遍历方式的内部实现上不同。Hashtable、HashMap都使用了Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。

e)  哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值。

 f)  Hashtable和HashMap它们两个内部实现方式的数组的初始大小和扩容的方式。HashTable中hash数组默认大小是11,增加的方式是old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。   注:  HashSet子类依靠hashCode()和equal()方法来区分重复元素。      HashSet内部使用Map保存数据,即将HashSet的数据作为Map的key值保存,这也是HashSet中元素不能重复的原因。而Map中保存key值的,会去判断当前Map中是否含有该Key对象,内部是先通过key的hashCode,确定有相同的hashCode之后,再通过equals方法判断是否相同。
 

Collectors.toMap 是Java 8 中提供的一个便捷的方法,用于将流(Stream)中的元素收集到一个Map中。它接受两个函数作为参数,分别用于提取Map中的键和值。 关于内存占用的问题,`Collectors.toMap` 方法本身并不会直接影响内存占用。它只是将流中的元素收集到一个Map中,而Map本身会占用一定的内存空间。 在使用 `Collectors.toMap` 进行元素收集时,需要注意以下几点以减少内存占用: 1. 确保键值对的唯一性:如果流中存在重复的键,可以使用 `toMap` 方法的第三个参数来指定如何处理重复键,例如通过合并值或抛出异常。 2. 指定Map的实现类:可以通过使用 `toMap` 方法的第四个参数来显式指定Map的实现类。选择适当的Map实现类可以根据具体需求来平衡内存占用和性能。 3. 控制并发性:如果在并发环境中使用 `toMap` 方法,可以使用 `toConcurrentMap` 方法来获取一个线程安全的并发Map,以避免可能的竞态条件和线程安全问题。 除了以上几点,还应该注意流(Stream)本身的元素数量和大小。如果流中包含大量元素或者元素较大,可能会占用更多的内存。在这种情况下,可以考虑使用并行流(parallelStream)来并行处理元素,以提高性能和减少内存占用。 总体而言,`Collectors.toMap` 方法本身并不会直接影响内存占用,而是取决于最终生成的Map实例和流中元素的特性。合理地使用该方法,并结合其他相关技术手段,可以有效地控制内存占用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值