Java集合篇:Map接口、Map接口的实现类、Collections集合工具类

目录

一、Map接口

1.1 Map接口概述

1.2 Map接口常用功能

二、Map接口的实现类

2.1 Map实现类之一:HashMap

2.1.1 HashMap概述

2.1.2 HashMap的存储结构

2.1.3 哈希冲突

2.1.4 哈希冲突的解决方案

2.1.5 HashMap的源码解析

2.2 Map实现类之二:HashTable

2.3 Map实现类之三:ConcurrentHashMap

2.4 Map实现类之四:Properties

三、Collections集合工具类

3.1 排序操作:(均为static方法)

3.2 Collections常用方法


一、Map接口

1.1 Map接口概述

Map接口与Collection接口并列存在。用于保存具有映射关系的数据,键值对的结构:key-value

Map 中的 key 和 value 都可以是任何引用类型的数据

Map 中的 key 用Set来存放,不允许重复,即同一个 Map 对象所对应 的类,须重写hashCode()和equals()方法

Map底层也是一种哈希表结构

常用String类作为Map的“键”。

key是唯一的,不能重复的,当key重复时,不会创建多个键值对,只会进行覆盖,key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到 唯一的、确定的 value

1.2 Map接口常用功能

添加、删除、修改操作:

  • Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中

  • void putAll(Map<? extends K, ? extends V> m):将m中的所有key-value对存放到当前map中

  • Object remove(Object key):移除指定key的key-value对,并返回value

  • void clear():清空当前map中的所有数据

元素查询的操作:

  • Object get(Object key):获取指定key对应的value

  • default V getOrDefault(Object key, V defaultValue):获取时,如果存在,则返回,如果不存在,则给定一个默认值

  • boolean containsKey(Object key):是否包含指定的key

  • boolean containsValue(Object value):是否包含指定的value

  • int size():返回map中key-value对的个数

  • boolean isEmpty():判断当前map是否为空

  • boolean equals(Object obj):判断当前map和参数对象obj是否相等

元视图操作的方法:

  • Set keySet():返回所有key构成的Set集合

  • Collection values():返回所有value构成的Collection集合

  • Set<Map.Entry<K, V>> entrySet():返回所有key-value对构成的Set集合

		Map map = new HashMap();
		// map.put(..,..)省略
		System.out.println("map的所有key:");
		Set keys = map.keySet();// HashSet
		for (Object key : keys) {
			System.out.println(key + "->" + map.get(key));
		}
		System.out.println("map的所有的value:");
		Collection values = map.values();
		Iterator iter = values.iterator();
		while (iter.hasNext()) {
			System.out.println(iter.next());
		}
		System.out.println("map所有的映射关系:");
		// 映射关系的类型是Map.Entry类型,它是Map接口的内部接口
		Set mappings = map.entrySet();
		for (Object mapping : mappings) {
			Map.Entry entry = (Map.Entry) mapping;
			System.out.println("key是:" + entry.getKey() + ",value是:" + entry.getValue());
		}
		// 获取时,如果存在,则返回,如果不存在,则给定一个默认值
		System.out.println(map.getOrDefault("name2", "佚名"));

学习完Map的各种方法,我们来写一个题:

面试题:给定如上字符串,统计每个单词出现的次数

package com.openlab.lenrn;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class WordCount {
	public static void main(String[] args) {
		String s = "In all the parting, I like it best see you tomorrow.Of all the blessings I prefer, as you wish. Sometimes you look at the wrong person, not because you are jealous, but because you are kind. \r\n" + 
				" You never know how strong you really are until being strong is the only choice you have. Never bend your head. Always hold it high. Look the world straight in the face.The twentieth meeting you used to spend al my luck.Reading is a happy thing. For those who love reading, once they read it,\r\n" + 
				"they can't stop, they can't give up reading, they want to read to the\r\n" + 
				"ends of the world. Some people say: \"the deepest and most peaceful\r\n" + 
				"happiness Calm down, open the book, the deposition\r\n" + 
				"of ink a little bit over the space, those dusty happiness a little bit\r\n" + 
				"open. Reading is really fun.I became a searcher,wanting to find out who I was and what made me unique. My view of myself was changing. I wanted a solid base to start from. I started to resist3 pressure to act in ways that I didn’t like any more,and I was delighted by who I really was. I came to feel much more sure that no one can ever take my place.\r\n" + 
				"Each of us holds a unique place in the world. You are special,no matter what others say or what you may think. So forget about being replaced. You can’t be. ";
 		s = s.replaceAll(",|\\.|\"|\r|\n|:|,", ""); // 把,."\r\n:,等符号替换掉
		String[] strs = s.split("\\s+"); // 分隔字符串,\s匹配任何空白字符,包括空格、制表符、换页符等等, “\s+”则表示匹配任意多个上面的字符
 		System.out.println(Arrays.toString(strs));
 		Map<String, Integer> map = new HashMap<>();
 		for (int i = 0; i < strs.length; i++) {
 			if (map.containsKey(strs[i])) {
				map.replace(strs[i], map.get(strs[i]) + 1);
			}else {
				map.put(strs[i], 1);
			}
		}
 		// 遍历
 		map.forEach((k, v) -> System.out.println(k + " ==> " + v));
 		Scanner scanner = new Scanner(System.in);
 		System.out.print("请输入你要查询的单词:");
 		System.out.println(map.getOrDefault(scanner.next(), 0));
 		scanner.close();
	}
}

这道题呢我们就可以通过Map来求解啦!不必再暴力破解啦

Map接口的常用实现类:HashMap(jdk1.2)、Hashtable(jdk1.2)、ConcurrentHashMap(jdk1.5)、TreeMap、LinkedHashMap和 Properties。其中,HashMap是 Map 接口使用频率最高的实现类,Java中HashMap的实现和源码解析是【重点】

二、Map接口的实现类

2.1 Map实现类之一:HashMap

2.1.1 HashMap概述

HashMap是 Map 接口使用频率最高的实现类。

允许使用null键和null值,与HashSet一样,不保证映射的顺序。

所有的key构成的集合是Set: 无序的、不可重复的。所以,key所在的类要重写: equals()和hashCode()

所有的value构成的集合是Collection:无序的、可以重复的。所以,value所在的类要重写:equals()

一个key-value构成一个entry

所有的entry构成的集合是Set:无序的、不可重复的

HashMap 判断两个 key 相等的标准是:两个 key 通过 equals() 方法返回 true, hashCode 值也相等。

HashMap 判断两个 value相等的标准是:两个 value 通过 equals() 方法返回 true。

2.1.2 HashMap的存储结构

JDK 7及以前版本:HashMap是数组+链表结构(即为链地址法)

JDK 8版本发布以后:HashMap是数组+链表+红黑树实现。

2.1.3 哈希冲突

首先,要明白哈希冲突,我们需要明白什么是哈希表。

哈希表(又叫散列表)是根据关键码值(Key-value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表,也就是哈希表。

哈希表其实就是一个存放哈希值的一个数组,哈希值是通过哈希函数计算出来的,那么哈希冲突就是两个不同值的东西,通过哈希函数计算出来的哈希值相同,这样他们存在数组中的时候就会发生冲突,这就是哈希冲突。就像是高铁座位,一般是一人一座的,但是突然系统可能出了问题,两个人可能买到了同一个座位的票,那么这时候就发生了冲突。

2.1.4 哈希冲突的解决方案

1. 再哈希法

同时构造多个不同的哈希函数。

2. 开放地址法

开放定址法就是一旦发生了冲突,就去寻找下一个空的哈希地址,只要哈希表足够大,空的哈希地址总能找到,然后将数据存入该位置.

3. 链地址法(拉链法)

将每个哈希表的节点当做一个链表的头部或者树的根节点又或者其他的数据结构。 例如,将哈希表的节点当做一个链表的头,将20存入之间的那个哈希表。

4. 建立公共溢出区(公共的缓冲区)

将哈希表分为基本表溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表。

2.1.5 HashMap的源码解析

HashMap源码中的重要常量:

  • DEFAULT_INITIAL_CAPACITY = 1 << 4:HashMap的默认容量,16 ,这里使用<<运算符是提示我们扩容始终应该为2的幂次方倍。准确来说,每次扩容是两倍

  • MAXIMUM_CAPACITY = 1 << 30:HashMap的最大支持容量,2^30

  • DEFAULT_LOAD_FACTOR = 0.75f:HashMap的默认加载因子,因为泊松分布

  • TREEIFY_THRESHOLD = 8:Bucket中链表长度大于该默认值,同时数组长度大于64,该链表转化为红黑树

  • UNTREEIFY_THRESHOLD = 6:Bucket中红黑树存储的Node小于该默认值,如果该链表之前是红黑树,则将重新转化为链表结构

  • MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量。(当桶中Node的 数量大到需要变红黑树时,若hash表容量小于MIN_TREEIFY_CAPACITY时,此时应执行 resize扩容操作这个MIN_TREEIFY_CAPACITY的值至少是TREEIFY_THRESHOLD的4 倍。)

  • table:存储元素的数组,总是2的n次幂

  • entrySet:存储具体元素的集

  • size:HashMap中存储的键值对的数量

  • modCount:HashMap扩容和结构改变的次数。

  • threshold:扩容的临界值,=容量*填充因子

  • loadFactor:填充因子

源码解析的结论:

1. HashMap结构

  • JDK7及其以前版本,HashMap:整体结构是一个"数组 + 链表" 结构

  • JDK8之后,HashMap:整体结构是一个"数组 + 链表 + 红黑树" 结构,目的链表的查询

2. 什么时候链表转换为红黑树:

当HashMap中的其中一个链的对象个数如果达到了8个,此时如果capacity没有达到64,那么HashMap会先扩容解决,如果已经达到了64,那么这个链会变成树,结点类型由Node变成TreeNode类型。当然,如果当映射关系被移除后,下次resize方法时判断树的结点个数低于6个,也会把树再转为链表。

3. HashMap默认数组的容量是16,每次扩容必须保证为2的幂次方倍。准确来说,是原有容量的2倍。

4. HashMap默认的负载因子是0.75,是因为泊松分布。

5. 构造HashMap:在构造时,所有参数默认,则容量是16,负载因子是0.75。如果在构造时,自己传递参数,容量必须包证为2的幂次方倍

6. put方法解析(如何添加元素)

  • 首先会将key进行hash运算,如果为null,则hash值为0,否则返回key的高低位二进制都参与到运算中来,使得key的hash值足够散列(不要让key都插入到一个节点中)

  • 添加节点:如果数组没有初始化,同时长度为0。如果第一次添加节点,则容量是16,负载因子是0.75,触发扩容的阈值:12

  • 插入时,求插入key的下标,获取hashCode值,做求模运算,和常规运算不一样,hash % length <==> (length - 1) & hash

JDK1.8相较于之前的变化:

  • 1.HashMap map = new HashMap();//默认情况下,先不创建长度为16的数组

  • 2.当首次调用map.put()时,再创建长度为16的数组

  • 3.数组为Node类型,在jdk7中称为Entry类型

  • 4.形成链表结构时,新添加的key-value对在链表的尾部(七上八下)

  • 5.当数组指定索引位置的链表长度>8时,且map中的数组的长度> 64时,此索引位置 上的所有key-value对使用红黑树进行存储。

2.2 Map实现类之二:HashTable

  • Hashtable是个古老的 Map 实现类,JDK1.0就提供了。不同于HashMap, Hashtable是线程安全的。

  • Hashtable实现原理和HashMap相同,功能相同。底层都使用哈希表结构,查询 速度快,很多情况下可以互用。

  • 与HashMap不同,Hashtable 不允许使用 null 作为 key 和 value

  • 与HashMap一样,Hashtable 也不能保证其中 Key-Value 对的顺序

  • Hashtable判断两个key相等、两个value相等的标准,与HashMap一致。

hashtable扩容是2倍+1

2.3 Map实现类之三:ConcurrentHashMap

jdk5但是了的并发的HashMap,采用的分段加锁的方式,所以效率被Hashtable高至少16倍,如果出现高并发场景,建议使用ConcurrentHashMap

ConcurrentHashMap在jdk8做了一次优化,加锁方式进行优化,引入了偏向锁(无锁)和自旋锁

2.4 Map实现类之四:Properties

Properties 类是 Hashtable 的子类,该对象用于处理属性文件

由于属性文件里的 key、value 都是字符串类型,所以 Properties 里的 key 和 value 都是字符串类型

存取数据时,建议使用setProperty(String key,String value)方法和 getProperty(String key)方法

Java中的配置文件:

  • xml:在spring boot 出现之后慢慢用注解替代xml,但是还在用

  • JSON(JavaScript Object Notation, JS对象简谱)是一种轻量级的数据交换格式

  • yaml

  • properties

三、Collections集合工具类

Collections 是一个操作 Set、List 和 Map 等集合的工具类

Collections工具类和操作数组的工具类:Arrays一样,提供了大量的好用的操作数组和容器的各种静态方法!Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作, 还提供了对集合对象设置不可变、对集合对象实现同步控制等方法

3.1 排序操作:(均为static方法)

  • reverse(List):反转 List 中元素的顺序

  • shuffle(List):对 List 集合元素进行随机排序

  • sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序

  • sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序

  • swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换

注意:使用sort方法,需要排序的对象必须实现Comparable或Comparator两个接口

Comparable和Comparator两个接口:

自然排序:

Comparable:在Java中,所以需要排序比较的对象,必须实现这个接口,来实现比较规则

定制排序:

Comparator:比较器接口,如果在开发过程中,有特殊的需求(临时修改排序规则),可以使用sort的第二个重载方法,该方法的第二个参数,用来指定比较器对象(临时修改排序规则)

如下我们有一个Person的JavaBean,我们要对他的对象集合做排序:

package com.openlab.day22.beans;

public class Person implements Comparable<Person> {

	private int id;
	private String name;
	private String nickname;
	private String gender;
	private int age;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getNickname() {
		return nickname;
	}

	public void setNickname(String nickname) {
		this.nickname = nickname;
	}

	public String getGender() {
		return gender;
	}

	public void setGender(String gender) {
		this.gender = gender;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public Person() {
	}

	public Person(int id, String name, String nickname, String gender, int age) {
		super();
		this.id = id;
		this.name = name;
		this.nickname = nickname;
		this.gender = gender;
		this.age = age;
	}

	@Override
	public String toString() {
		return "Person [id=" + id + ", name=" + name + ", nickname=" + nickname + ", gender=" + gender + ", age=" + age
				+ "]";
	}

	@Override
	public int compareTo(Person o) {
		return this.getId() - o.id; 
	}

}
    @Test
	void test02() {
		List<Person> users = new ArrayList<Person>();
		users.add(new Person(1, "ljh", "刘建宏", "男", 20));
		users.add(new Person(2, "qq", "钱七", "男", 18));
		users.add(new Person(3, "zl", "赵六", "男", 70));
		users.add(new Person(4, "ww", "王五", "女", 19));
		users.add(new Person(5, "lisi", "李四", "男", 30));
		users.add(new Person(6, "zs", "张思", "女", 10));
		
		// 排序自定义对象,必须实现Comparable接口,执行排序规则
		Collections.sort( users);
		System.out.println(users);
	}

    @Test
	void test03() {
		// 找到年龄最大的三个人
		List<Person> users = new ArrayList<Person>();
		users.add(new Person(1, "ljh", "刘能", "男", 20));
		users.add(new Person(2, "qq", "钱七", "男", 18));
		users.add(new Person(3, "zl", "赵六", "男", 70));
		users.add(new Person(4, "ww", "王五", "女", 19));
		users.add(new Person(5, "lisi", "李四", "男", 30));
		users.add(new Person(6, "zs", "张思", "女", 10));
		
		// 如果在开发过程中,有特殊的需求(临时修改排序规则)
		// 可以使用sort的第二个重载方法,该方法的第二个参数
		// 用来指定比较器对象(临时修改排序规则)
		Collections.sort(users, new Comparator<Person>() {
			// 临时指定对象的排序规则是:年龄由大到小
			@Override
			public int compare(Person o1, Person o2) {
				return o2.getAge() - o1.getAge();
			}
		});
		System.out.println(users);
	}

3.2 Collections常用方法

查找、替换

  • Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素

  • Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回 给定集合中的最大元素

  • Object min(Collection)

  • Object min(Collection,Comparator)

  • int frequency(Collection,Object):返回指定集合中指定元素的出现次数

  • void copy(List dest,List src):将src中的内容复制到dest中

  • boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所有旧值

同步控制

Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集 合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Golang_HZ

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

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

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

打赏作者

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

抵扣说明:

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

余额充值