3 数据结构_Java集合_01(List Map)

1  简介

2 HashMap

2.1 默认情况下:初始容量是16,加载因子是0.75

2.2 制定容量大小和不指定容量大小的区别

	public static void main(String[] args) {
		   int aHundredMillion = 10000000;

		   Map<Integer, Integer> map = new HashMap<>();
		   long s1 = System.currentTimeMillis();
		   for (int i = 0; i < aHundredMillion; i++) {
		       map.put(i, i);
		   }
		   long s2 = System.currentTimeMillis();
		   System.out.println("未初始化容量,耗时 : " + (s2 - s1));

		   Map<Integer, Integer> map1 = new HashMap<>(aHundredMillion / 2);
		   long s5 = System.currentTimeMillis();
		   for (int i = 0; i < aHundredMillion; i++) {
		       map1.put(i, i);
		   }
		   long s6 = System.currentTimeMillis();
		   System.out.println("初始化容量5000000,耗时 : " + (s6 - s5));

		   Map<Integer, Integer> map2 = new HashMap<>(aHundredMillion);
		   long s3 = System.currentTimeMillis();
		   for (int i = 0; i < aHundredMillion; i++) {
		       map2.put(i, i);
		   }
		   long s4 = System.currentTimeMillis();
		   System.out.println("初始化容量为10000000,耗时 : " + (s4 - s3));
		}

2.3 map扩容机制

HashMap的扩容条件就是当HashMap中的元素个数(size)超过临界值(threshold)时就会自动扩容。

在HashMap中,threshold = loadFactor * capacity

我们可以通过代码:得到loadFacotr 得到capacity 算术临界值!

public static void main(String[] args) throws Exception{
		
		Map<String, String> map = new HashMap<String, String>(3);
		map.put("hahaha", "hahaha");
		System.out.println("因为制定了临界值,容量是:第一次2^n大于capacity的值!即2^1 2^2 ...,我们发现是2^2=4");
		
		Class<?> mapType = map.getClass();
		Method capacity = mapType.getDeclaredMethod("capacity");
		capacity.setAccessible(true);
		System.out.println("capacity : " + capacity.invoke(map));
		
		Method loadFactor = mapType.getDeclaredMethod("loadFactor");
		loadFactor.setAccessible(true);
		System.out.println("loadFactor : " + loadFactor.invoke(map));
		System.out.println("临界值是:"+(int)((int)capacity.invoke(map)*(float)loadFactor.invoke(map)));
		   
	}

2.4 hashmap 默认容量 加载因子 扩容两倍的哲学

    说到底是用内存换性能的做法

    减少扩容,较少rehash.这些耗时,导致性能下降!

    容量变大,一次存储,增加了内存,性能得到增加!

    就比如为什么加载因子是0.75 不是0.5 1 不是其他的! 底层肯定是数学!

    但是从中庸的角度来看,0.5 表示一半的时候 就扩容 浪费 1 表示满的时候才扩容!肯定会增加put的时间!

    

    HashMap负载因子为0.75是空间和时间成本的一种折中

2.5 hashmap本质是什么样的数据结构

        hashmap其实就是数组+链表 

        首先是hash桶,根据x-fx 映射进桶中的某一个位置,如果此位置没有值,就直接加进去!如果有值,就在这个地址后面接链表!

         所以:hashMap 是先比较hashcode,hashcode相同的在比较equals方法

3 HashMap与HashTable区别

HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。

主要的区别有:线程安全性,同步(synchronization),以及速度

HashMap几乎可以等价于Hashtable

HashMap:非synchronized 接受null(key和value都可以为null)

Hashtable是synchronized的,可被多线程共享

HashMap不能保证随着时间的推移Map中的元素次序是不变的

使用场景:单线程使用HashMap 多线程使用HashTable

4 为什么使用ConcurrentHashMap

同步的集合类(Hashtable和Vector),同步的封装类(使用Collections.synchronizedMap()方法和Collections.synchronizedList()方法返回的对象)可以创建出线程安全的Map和List。但是有些因素使得它们不适合高并发的系统。它们仅有单个锁,对整个集合加锁,以及为了防止ConcurrentModificationException异常经常要在迭代的时候要将集合锁定一段时间,这些特性对可扩展性来说都是障碍。

ConcurrentHashMap和CopyOnWriteArrayList保留了线程安全的同时,也提供了更高的并发性。ConcurrentHashMap和CopyOnWriteArrayList并不是处处都需要用,大部分时候你只需要用到HashMap和ArrayList,它们用于应对一些普通的情况。

ConcurrentHashMap和Hashtable的区别

Hashtable和ConcurrentHashMap有什么分别呢?它们都可以用于多线程的环境,但是当Hashtable的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的时间。因为ConcurrentHashMap引入了分割(segmentation),不论它变得多么大,仅仅需要锁定map的某个部分,而其它的线程不需要等到迭代完成才能访问map。简而言之,在迭代的过程中,ConcurrentHashMap仅仅锁定map的某个部分,而Hashtable则会锁定整个map。

5 equals()和hashCode()的应用,以及它们在HashMap中的重要性

equals方法没被重写的时候   比较的只是对象的地址  重写之后 比较的才是对象里的内容
重写equals的时候 务必需要重写hashcode 不然在用到容器的时候 会出现问题
         因为容器会去判断新加入的对象的hashcode 在集合中是否存在 再去判断对象的内容
如使用HaspMap  就是先判断hashcode 再判断对象的值!

6 何如使用线程安全的HashMap
实现线程安全的方式有三种,

分别是使用HashTable、Collections.SynchronizeMap、ConcurrentHashMap

7 集合分类

  线程安全类/并发集合类

Vector、HashTable、Properties和Stack是同步类,所以它们是线程安全的,能够在多线程环境下使用。

Java1.5并发API包括一些集合类。同意迭代时改动,由于它们都工作在集合的克隆上。所以它们在多线程环境中是安全的。

Java1.5并发包(java.util.concurrent)包括线程安全集合类,同意在迭代时改动集合。

迭代器被设计为fail-fast的,会抛出ConcurrentModificationException。一部分类为:CopyOnWriteArrayList、 ConcurrentHashMap、CopyOnWriteArraySet。

 

猜你喜欢

转载自blog.csdn.net/hellolovelife/article/details/81060952