2.5.4 Map

Lambda表达式:

list.foeEach(System.out::println);//遍历集合并打印
list.foeEach((x)->System.out.println(x+"特殊字"));//拼装信息打印
list.stream().filter(x->x满足的条件).forEach((x)->System.out.println(x+"特殊字"));//打印集合中符合条件的元素
2.5.4.1 Map

键值对类型的集合 每次往集合里面添加一对:key[主键 -》 唯一]=value[值]

Map集合通用的方法:
如何创建对象:
HashMap<泛型,值> map = new HashMap<>();

如何添加元素:map.put(主键,值);

得到集合大小:map.size()

判断集合里面是否存在某个主键:map.containsKey(主键)

判断集合里面是否存在某个值对象:map.containsValue(值)

通过主键得到值对象:map.get(主键)

通过主键删除一整条记录:map.remove(主键)

Map集合遍历方式

  1. 通过map集合得到主键
    Set<主键泛型> set=map.keySet();
    遍历set集合得到所有的主键:x
    通过主键得到值对象:map.get(x)
  2. 通过map集合得到所有值
    Collection<值泛型> cs=map.values();
    遍历集合得到所有的值对象:x
  3. 通过map集合得到一整条记录【主键+值】
    Set<Map.Entry<主键泛型,值泛型>> set=map.entrySet();
    遍历集合得到一条记录:x[主键+值]
    通过记录得到主键对象:x.getKey()
    通过记录得到值对象:x.getValue()
    通过记录对值对象修改:x.setValue(值)
    例1:Map遍历综合应用:
import java.util.*;
public class TestHashMap{
	public static void main(String[] args){
		Map<String,Integer> map=new HashMap<>();
		map.put("[魏] 郭奉孝",38);
		map.put("[魏] 夏侯妙才",80);
		map.put("[蜀] 诸葛孔明",40);
		map.put("[蜀] 赵子龙",98);
		map.put("[吴] 大乔",6);
		map.put("[群] 吕奉先",100);
		map.put("[吴] 鲁子敬",39);
		System.out.println("英雄信息共有:\t"+map.size());
		System.out.println("武力值100有否?:"+map.containsValue(100));
		System.out.println("吴国大乔来否?:"+map.containsKey("[吴] 大乔"));
		System.out.println("蜀国大将赵子龙武力值:\t"+map.get("[蜀] 赵子龙"));
		Set<String> set1=map.keySet();
		int count1=0,count2=0;
		for(String str:set1){
			String[] data=str.split(" ");
			if(data[1].length()>=4){
				count1++;
				}
			if(data[0].contains("吴")){
				count2++;
				}
			}
		System.out.println("名字像鬼子的英雄有:"+count1);
		System.out.println("吴国的武将有:"+count2);
   Collection<Integer> cs=map.values();
   int sum=0;
   for(Integer x:cs){
	   sum+=x;
	   }
   System.out.println("所有英雄平均武力值为:"+sum/cs.size());
Set<Map.Entry<String,Integer>> set2=map.entrySet();
int sum2=0,count3=0;
   for(Map.Entry<String,Integer>  info:set2){
	   if(info.getKey().contains("魏")){
		   sum2+=info.getValue();
		   count3++;
		   }
	}
	System.out.println("魏国的武将平均武力值为:"+sum2/count3);

map.entrySet().stream().filter(x->x.getKey().contains("蜀")).forEach(x->x.setValue(x.getValue()+5));
map.forEach((name,power)->System.out.println(name+":"+power));
	}
}
2.5.4.2 HashMap

底层基于哈希表实现 默认分16个小组 每个对象应该去到哪一个小组根据哈希码值决定 哈希码值通过hashCode()得到
当来到某个小组之后 发现这个小组里面有一个对象的哈希码值和新来对象的哈希码值一样 需要再做==和equals()比较

HashMap底层的
put(主键,值)
get(主键)
remove(主键)
containsKey(主键)
都尊重hashCode() == equals()比较机制

*:Map集合当主键遇到重复元素的时候 主键不变 值替换

2.5.4.3 TreeMap

底层基于二叉树实现的 每一个元素应该去到左子树/右子树
根据compareTo()/compare()最终结果
>0 新来元素放在右边
=0 新来元素舍弃
<0 新来元素放在左边

TreeMap底层的
put(主键,值)
get(主键)
remove(主键)
containsKey(主键)
底层尊重compareTo()/compare()比较机制

一些区别

1)HashMap和Hashtable之间的区别?
a:同步特性不同
HashMap同一时间允许多个线程进行访问 效率较高
但是可能会出现并发错误

Hashtable同一时间允许一个线程进行访问 效率较低
但是不会出现并发错误

从jdk5.0开始 集合的工具类里面提供一个方法
可以将线程不安全的HashMap变成线程安全的Map集合
于是Hashtable渐渐被淘汰了
Map<> x = Collections.synchronizedMap(HashMap对象)

b:底层分组不同
HashMap底层默认分16个小组 程序员可以随意的制定
但是最终一定会变成2的n次方数

Hashtable底层默认分11个小组 程序员可以随意的指定

c:对null处理不同
HashMap无论是主键还是值对象 都可以传null
但是由于主键是唯一的 所以主键只能传一个null

Hashtable无论是主键还是值对象都不能传null
否则触发空指针异常

d:出现的版本不同
Hashtable:since jdk1.0
HashMap:since jdk1.2

2)ConcurrentHahMap HashMap Hashtable之间的区别?
Hashtable效率低 但是安全系数高
为了保证安全性 在每一个方法上面都加上synchronized
如果一个线程在访问哈希表里面的某个小组的时候,会把整个哈希表全部加锁 所以同一时间只能一个线程访问

HashMap效率高 但是安全性低
为了保障效率性 每一个方法都没有加synchronized,所以可能两个线程同时访问一个小组 会产生脏数据

ConCurrentHashMap安全性介于他们两个之间效率型介于他们两个之间
ConCurrentHashMap采用锁分离 -》 将锁的力度降低
如果一个线程在访问某个小组的时候 只是将这个小组加锁,如果有其他的线程也想要访问这个小组 需要排队
如果有其他的线程想要访问另一个小组 直接访问

3)HashMap在7.0之间和7.0之后变化

HashMap底层基于哈希表实现的
jdk7.0之前哈希表由数组和链表组成
数组:装表头信息
优点便于每一个元素查找应该去到哪一个小组

链表:装元素
优点便于每一个元素添加、删除[直接修改指向]

采用头插法

jdk7.0开始哈希表由数组 + 链表 + 二叉树组成
数组:装表头信息
优点便于每一个元素查找应该去到哪一个小组

链表:装元素
优点便于每一个元素添加、删除[直接修改指向]

二叉树:装元素
当小组内元素个数>8个的时候 自动的由链表装成二叉树
提高效率
采用尾插法