14 Java集合(Map集合+HashMap+泛型使用+集合面试题)

news2024/11/16 4:13:18

集合

      • 14.11 Map集合
        • 14.11.1 Map集合特点
        • 14.11.2 Map集合体系结构
      • 14.12 HashMap
        • 14.12.1 HashMap基本使用
        • 14.12.2 HashMap实际应用
        • 14.12.3 HashMap练习
        • 14.12.4 HashMap底层实现原理
        • 14.12.5 put的过程原码
        • 14.12.6 resize过程原码
        • 14.12.7 get的过程原码
      • 14.13 HashTable
      • 14.14 泛型高级使用
        • 14.14.1 泛型类
        • 14.14.2 泛型接口
        • 14.14.3 泛型方法
        • 14.14.4 泛型上下边界
      • 14.15 常见的集合面试面试题

``

14.11 Map集合

14.11.1 Map集合特点

  • Map集合是双列集合,由key和value组成。称之为键值对
  • 键的特点:无序,无下标,不重复。
  • 值的特点:无序,无下标,可重复

14.11.2 Map集合体系结构

Map集合体系结构
请添加图片描述

14.12 HashMap

14.12.1 HashMap基本使用

常用方法

  • put(K key, V value)
  • get(Object key)
  • Set keySet()
  • Collection values()
  • Set<Map.Entry<K,V>> entrySet()
  • boolean containsKey(Object key)
  • boolean containsValue(Object value)
  • V remove(Object key)
  • int size()
package com.qf.demo01;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;
public class HashMapDemo {
	public static void main(String[] args) {
		//创建HashMap
		HashMap<String, String> map = new HashMap<String, String>(12);
		//向map集合中添加元素
		map.put("usa", "漂亮国");
		map.put("jp", "日本");
		map.put("en", "英国");
		map.put("ca", "加拿大");
		map.put("cn", "中华人民共和国");
		map.put("cn", "中国");
		map.put("china", "中国");
		System.out.println(map);
		//从集合中获取元素的值。    根据key获取对应value
		System.out.println(map.get("cn"));
		System.out.println(map.get("usa"));
		
		//清空map集合中的元素
		//map.clear();
		//System.out.println(map);
		//判断是否包含指定的key
		System.out.println(map.containsKey("xjp"));
		//判断是否包含指定的value
		System.out.println(map.containsValue("中国"));
		//判断集合中的元素长度是否为0
		System.out.println(map.isEmpty());
		//根据key移除map中的元素
		map.remove("jp");
		System.out.println(map);
		//返回map集合中元素的个数
		System.out.println(map.size());		
		System.out.println("=================================");
		//返回map集合中所有的key
		Set<String> keySet = map.keySet();
		for (String key : keySet) {
			System.out.println(key);
		}
		System.out.println("=================================");
		//返回map集合中所有的value
		Collection<String> values = map.values();
		for (String value : values) {
			System.out.println(value);
		}
		System.out.println("=================================");
		//返回map集合中所有的key和value (Entry)
		Set<Entry<String, String>> entrySet = map.entrySet();
		
		for (Entry<String, String> entry : entrySet) {
			System.out.println(entry.getKey());
			System.out.println(entry.getValue());
		}
		System.out.println("=================================");	
	}
}

14.12.2 HashMap实际应用

  • 可以使用Map<String , Obeject> 表示一个实体类
  • 可以使用List<Map<String,Object>> 表示一个实体类集合
public class HashMapDemo02 {
	/**
	 * 表示一件商品:商品编号、名称、价格、产地、上架时间....
	 * 
	 * 实体类表示:
	 *  一件商品:Product对象
	 *      public class Product{
	 *          private int id;
	 *          private String name;
	 *          private double price;
	 *          .....
	 *      }
	 *      Product product = new Product(1,"手机",3000...);
	 *      
	 *  多件商品:List<Product>
	 * 
	 * Map表示:
	 * 	一件商品:Map对象
	 * 		Map<String,Object> map = new HashMap<>();
	 *      map.put("id",1);
	 *      map.put("name","电脑");
	 *      map.put("price",3000.5);
	 * 
	 *  多件商品:List<Map<String,Object>>
	 */
	public static void main(String[] args) {
		//使用Map表示一件商品
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("id", 1);
		map.put("name", "电脑");
		map.put("price",3000.5);
		map.put("createDate", new Date());
		
		Map<String, Object> map1 = new HashMap<String, Object>();
		map1.put("id", 2);
		map1.put("name", "电脑2");
		map1.put("price",3002.5);
		map1.put("createDate", new Date());
		
		//使用List<Map>表示多件商品
		List<Map<String, Object>> list = new ArrayList<Map<String,Object>>();
		list.add(map);
		list.add(map1);
		
		for (Map<String, Object> map2 : list) {
			System.out.println(map2);
		}
		//使用集合实现存储省市信息
	}
}

14.12.3 HashMap练习

案例:使用集合保存省市数据

Map<String, List<String>> map = new HashMap<String, List<String>>();
List<String> city1 = new ArrayList<String>();
city1.add("武汉");
city1.add("监利");
city1.add("黄冈");
city1.add("荆州");
map.put("湖北省", city1);
List<String> city2 = new ArrayList<String>();
city2.add("长沙");
city2.add("岳阳");
city2.add("常德");
city2.add("湘潭");
map.put("湖南省", city2);
System.out.println(map.get("湖北省"));

14.12.4 HashMap底层实现原理

HashMap底层实现原理
请添加图片描述

14.12.5 put的过程原码

put流程
请添加图片描述
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
			   boolean evict) {
	//tab表示存放Node节点的数据   p表示当前节点   n表示长度  i表示节点在数组中的下标			   
	Node<K,V>[] tab; Node<K,V> p; int n, i;
	//判断数组如果为空或者数组长度为0,那么就对数组进行扩容,数组默认初始大小为16
	if ((tab = table) == null || (n = tab.length) == 0)
		n = (tab = resize()).length;
	//将数组的长度-1与hash值进行与运算(计算的结果一定是0~数组长度-1)得到元素应该存放的下标
	//如果当前下标位置为空,那么直接将Node节点存放在当前位置
	if ((p = tab[i = (n - 1) & hash]) == null)
		tab[i] = newNode(hash, key, value, null);
	//如果当前位置不为空(分为三种情况)
	else {
		Node<K,V> e; K k;
		//情况1:要添加的元素与当前位置上的元素相同(hash(hashCode)、key(equals)一致),则直接替换
		if (p.hash == hash &&
			((k = p.key) == key || (key != null && key.equals(k))))
			e = p;
		//情况2:如果要添加的元素是红黑树节点,那么将其添加到红黑树上
		else if (p instanceof TreeNode)
			e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
		//情况3:如果要添加的元素是链表,则需要遍历
		else {
			for (int binCount = 0; ; ++binCount) {
				//将当前元素的下一个节点赋给e
				//如果e为空,则创建新的元素节点放在当前位置的下一个元素上,并退出循环
				if ((e = p.next) == null) {
					p.next = newNode(hash, key, value, null);
					//如果链表的元素个数大于8个(且当数组中的元素个数大于64),则将其转换成红黑树
					if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
						treeifyBin(tab, hash);
					break;
				}
				//要添加的元素与当前位置上的元素相同(hash(hashCode)、key(equals)一致),则直接退出循环
				if (e.hash == hash &&
					((k = e.key) == key || (key != null && key.equals(k))))
					break;
				p = e;
			}
		}
		//如果返回的e不为null
		if (e != null) { // existing mapping for key
			//将e的值赋给oldValue
			V oldValue = e.value;
			if (!onlyIfAbsent || oldValue == null)
				e.value = value;
			afterNodeAccess(e);
			//返回以前的值(当添加的元素已经存在返回的是以前的值)
			return oldValue;
		}
	}
	++modCount;
	//如果数组的元素个数大于阈值则进行扩容
	if (++size > threshold)
		resize();
	afterNodeInsertion(evict);
	return null;
}

14.12.6 resize过程原码

扩容流程
请添加图片描述
final Node<K,V>[] resize() {
	//oldTab 表示原来数组(如果是第二次扩容:长度为16的那个)
	Node<K,V>[] oldTab = table;
	//oldCap 表示原数组的容量(长度)
	int oldCap = (oldTab == null) ? 0 : oldTab.length;
	//oldThr 表示数组原来的阈值 12
	int oldThr = threshold;
	//newCap 新数组的容量 newThr 新数组的阈值
	int newCap, newThr = 0;
	
	if (oldCap > 0) {
		if (oldCap >= MAXIMUM_CAPACITY) {
			threshold = Integer.MAX_VALUE;
			return oldTab;
		}
		//新数组的容量扩大一半  newCap 32
		else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
				 oldCap >= DEFAULT_INITIAL_CAPACITY)
			//新阈值扩大老阈值的一半  newThr 24
			newThr = oldThr << 1; // double threshold
	}
	else if (oldThr > 0) // initial capacity was placed in threshold
		newCap = oldThr;
	else {               // zero initial threshold signifies using defaults
		newCap = DEFAULT_INITIAL_CAPACITY;
		newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
	}
	if (newThr == 0) {
		float ft = (float)newCap * loadFactor;
		newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
				  (int)ft : Integer.MAX_VALUE);
	}
	//threshold 24
	threshold = newThr;
	@SuppressWarnings({"rawtypes","unchecked"})
	//创建一个长度为32的数组
		Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
	//table指向新数组
	table = newTab;
	if (oldTab != null) {
		//将原数组中的元素拷贝到新数组中
		for (int j = 0; j < oldCap; ++j) {
			Node<K,V> e;
			//如果当前位置元素不为空
			if ((e = oldTab[j]) != null) {
				oldTab[j] = null;
				//情况1:当前位置上的下一个元素为空,则直接将这个元素拷贝到新数组中
				if (e.next == null)
					newTab[e.hash & (newCap - 1)] = e;	
				//情况2:当前位置上的元素红黑树类型,则需要进行切割
				else if (e instanceof TreeNode)
					((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
				//情况3:当前位置上的元素链表类型,则需要进行分散拷贝
				else { // preserve order
					Node<K,V> loHead = null, loTail = null;
					Node<K,V> hiHead = null, hiTail = null;
					Node<K,V> next;
					do {
						next = e.next;
						if ((e.hash & oldCap) == 0) {
							if (loTail == null)
								loHead = e;
							else
								loTail.next = e;
							loTail = e;
						}
						else {
							if (hiTail == null)
								hiHead = e;
							else
								hiTail.next = e;
							hiTail = e;
						}
					} while ((e = next) != null);
					if (loTail != null) {
						loTail.next = null;
						newTab[j] = loHead;
					}
					if (hiTail != null) {
						hiTail.next = null;
						newTab[j + oldCap] = hiHead;
					}
				}
			}
		}
	}
	return newTab;
}

14.12.7 get的过程原码

final Node<K,V> getNode(int hash, Object key) {
	Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
	if ((tab = table) != null && (n = tab.length) > 0 &&
		(first = tab[(n - 1) & hash]) != null) {
		//当前first与要找到的hash和key都相等直接返回当前这个first元素
		if (first.hash == hash && // always check first node
			((k = first.key) == key || (key != null && key.equals(k))))
			return first;
		//如果当前first不为空(有两种情况)
		if ((e = first.next) != null) {
			//当前位置是一个红黑树
			if (first instanceof TreeNode)
				//根据hash、key从红黑树上找到对应的元素
				return ((TreeNode<K,V>)first).getTreeNode(hash, key);
			//当前位置是一个链表
			do {
				//循环进行比较直到找到向的hash和key的元素,并返回
				if (e.hash == hash &&
					((k = e.key) == key || (key != null && key.equals(k))))
					return e;
			} while ((e = e.next) != null);
		}
	}
	//如果数组的为空、数组的长度为0、当前下标位置上的值为null,这三种情况都返回null
	return null;
}

14.13 HashTable

Hashtable常用方法与HashMap一致

HashMap与Hashtable区别:

  • 1、Hashtable是线程安全的,HashMap是线程不安全的
  • 2、Hashtable中不允许存储null作为key和value,而HashMap可以

在实际开发中一般都是用HashMap。考虑线程安全使用ConCurrentHashMap

14.14 泛型高级使用

14.14.1 泛型类

泛型类 类名

public class Box<T> {  //T:表示任意的java类型       E、K、V
	private T data;

	public T getData() {
		return data;
	}

	public void setData(T data) {
		this.data = data;
	}
}

14.14.2 泛型接口

泛型接口 接口名

public interface MyInterface<T> {
	public void show(T t);
}

14.14.3 泛型方法

泛型方法 public T 方法名(T t,…){}

//泛型可以作为参数,(必须得先定义  <T> )
public <T> void m1(T t) {
    
}

14.14.4 泛型上下边界

泛型上下边界

  • 语法:
    • 上边界 ? extends 类型
    • 下边界 ? super 类型
public class Demo01 {
	//? 表示不确定类型     此时的?表示Object
	public static void test01(List<?> list) {
		
	}
	/**
	 * 定义泛型上边界
	 * 
	 * <? extends 类型>
	 */
	
	public static void test02(List<? extends Number> list) {
		
	}
	/**
	 * 定义泛型下边界
	 * 
	 * <? super 类型>
	 */
	
	public static void test03(List<? super Number> list) {
		
	}
	
	public static <T> void test04(List<? extends Comparable<T>> list) {
		
	}
	public static void main(String[] args) {
		List<String> list1 = new ArrayList<String>();
		List<Integer> list2 = new ArrayList<Integer>();
		List<Number> list3 = new ArrayList<Number>();
		List<Object> list4 = new ArrayList<Object>();
		test01(list1);
		test01(list2);
		test01(list3);
		test01(list4);
		
		//test02(list1);  //错误,方法定义泛型的上边界,泛型只能是Number及其Number子类
		test02(list2);
		test02(list3);
		//test02(list4);  //错误,方法定义泛型的上边界,泛型只能是Number及其Number子类 
		
		//test03(list1);  //错误,方法定义泛型的下边界,泛型只能是Number及其Number父类
		//test03(list2);
		test03(list3);
		test03(list4);
        
		test04(list1);
		test04(list2);
		//test04(list3);    //错误,方法定义泛型的上边界,泛型必须实现Comparable接口
		//test04(list4);      //错误,方法定义泛型的上边界,泛型必须实现Comparable接口
	}	
}

14.15 常见的集合面试面试题

  • 1、简述:ArrayList、LinkedList、Vector的区别
  • 2、简述:HashSet、TreeSet的去重原理
  • 3、ArrayList、LinkedList底层实现
  • 4、HashMap、HashTable的区别
  • 5、HashMap底层实现原理
  • 6、HashMap扩容机制
  • 7、HashMap的数组容量为什么是2的次方数
  • 8、HashMap的负载因子为什么是0.75
  • 9、HashMap添加元素的过程
  • 10、谈谈了解的数据结构

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/175267.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

5-1中央处理器-CPU的功能和基本结构

文章目录一.CPU的功能二.CPU的基本结构&#xff08;一&#xff09;运算器1.运算器的基本组成2.专用数据通路方式3.CPU内部单总线方式&#xff08;二&#xff09;控制器1.基本组成2.实现过程&#xff08;三&#xff09;寄存器一.CPU的功能 中央处理器&#xff08;CPU&#xff0…

并查集的入门与应用

目录 一、前言 二、并查集概念 1、并查集的初始化 2、并查集的合并 3、并查集的查找 4、初始化、查找、合并代码 5、复杂度 二、路径压缩 三、例题 1、蓝桥幼儿园&#xff08;lanqiaoOJ题号1135&#xff09; 2、合根植物&#xff08;2017年决赛&#xff0c;lanqiaoO…

SQL注入篇 - 布尔盲注及延时注入

数据来源 盲注 什么是盲注&#xff1a; 布尔盲注原理 布尔盲注流程 手工盲注思路&#xff08;以下的文章参考&#xff1a;DVWA-sql注入&#xff08;盲注&#xff09; - N0r4h - 博客园&#xff09; 手工盲注的过程&#xff0c;就像你与一个机器人聊天&#xff0c;这个机器人知…

DGSEA | GSEA做完了不要停,再继续比较一下有意义的通路吧!~

1写在前面 GSEA大家都会用了&#xff0c;但GSEA也有它自己的缺点&#xff0c;就是不能比较两个基因集或通路的富集情况。&#x1f912; 今天介绍一个Differential Gene Set Enrichment Analysis (DGSEA)&#xff0c;可以量化两个基因集的相对富集程度。&#x1f609; 2用到的包…

Java中的位运算及其常见的应用

文章目录1、位运算1.1 原码、反码、补码1.2 位运算符2、位运算的应用2.1 取模运算2.2 奇偶性判断2.3 交换变量的值2.4 加法运算1、位运算 1.1 原码、反码、补码 计算机中所有数据的存储和运算都是以二进制补码的形式进行的。a —> 97&#xff0c;A —> 65&#xff0c;‘…

深入学习Vue.js(十二)编译器

模板DSL的编译器 1.编译器概述 编译器实际上是一段程序&#xff0c;他用来将一种语言A翻译为另一种语言B。其中&#xff0c;A被称为源代码&#xff0c;B被称为目标代码。编译器将源代码翻译为目标代码的过程被称为编译。完整的编译过程通常包含词法分析、语法分析、语义分析、…

软件测试——测试用例

作者&#xff1a;~小明学编程 文章专栏&#xff1a;测试开发 格言&#xff1a;热爱编程的&#xff0c;终将被编程所厚爱。 目录 测试用例的设计方法 等价类 边界值 错误猜测法 判定表法&#xff08;使用于关系组合&#xff09; 设计步骤 具体例子 正交法 场景设计法…

Redis相关简介

1. Redis 简介 在这个部分&#xff0c;我们将学习以下3个部分的内容&#xff0c;分别是&#xff1a; ◆ Redis 简介&#xff08;NoSQL概念、Redis概念&#xff09; ◆ Redis 的下载与安装 ◆ Redis 的基本操作 1.1 NoSQL概念 1.1.1 问题现象 在讲解NoSQL的概念之前呢&am…

8. R语言画:散点图、直方图、条形图、箱线图、小提琴图、韦恩图

b站课程视频链接&#xff1a; https://www.bilibili.com/video/BV19x411X7C6?p1 腾讯课堂(最新&#xff0c;但是要花钱&#xff0c;我花99&#x1f622;&#x1f622;元买了&#xff0c;感觉讲的没问题&#xff0c;就是知识点结构有点乱&#xff0c;有点废话&#xff09;&…

九大数据分析方法-综合型分析方法以及如何使用这九大分析方法

文章目录3 综合型分析方法3.1 相关性分析法3.1.1 直接相关3.1.2 间接相关3.2标签分析法3.3 MECE法4 如何使用九大方法本文来源&#xff0c;为接地气的陈老师的知识星球&#xff0c;以及付同学的观看笔记。3 综合型分析方法 3.1 相关性分析法 相关性分析法&#xff1a;寻找指标…

ROS2机器人编程简述humble-第二章-Executors .3.5

ROS2机器人编程简述humble-第二章-Parameters .3.4由于ROS2中的节点是C对象&#xff0c;因此一个进程可以有多个节点。事实上&#xff0c;在许多情况下&#xff0c;这样做是非常有益的&#xff0c;因为当通信处于同一进程中时&#xff0c;可以通过使用共享内存策略来加速通信。…

freeglut 在mfc 下的编译

freeglut 是OpenGL Utility Toolkit (GLUT) library 的替代版本&#xff0c;glut 应用广阔&#xff0c;但比较陈旧&#xff0c;很久没有更新。 我原来的opengl 用的是glut&#xff0c; 想更新到64位版本&#xff0c;怎么也找不到合适的下载。最后找到完全替代版本freeglut。fre…

【Linux】线程概念 | 互斥

千呼万唤始出来&#xff0c;终于到多线程方面的学习了&#xff01; 所用系统Centos7.6 本文的源码&#x1f449;【传送门】 最近主要是在我的hexo个人博客上更新&#xff0c;csdn的更新会滞后 文章目录1.线程的概念1.1 执行流1.2 线程创建时做了什么&#xff1f;1.3 内核源码中…

每刻和金蝶云星空接口打通对接实战

接通系统&#xff1a;每刻3000中大型企业在用&#xff0c;新一代业财税一体化解决方案提供商。旗下拥有每刻报销、每刻档案、每刻云票、每刻财务共享云平台等&#xff0c;助力企业实现财务数字化转型。对接系统&#xff1a;金蝶云星空金蝶K/3Cloud结合当今先进管理理论和数十万…

算法刷题打卡第72天:最少侧跳次数

最少侧跳次数 难度&#xff1a;中等 给你一个长度为 n 的 3 跑道道路 &#xff0c;它总共包含 n 1 个 点 &#xff0c;编号为 0 到 n 。一只青蛙从 0 号点第二条跑道 出发 &#xff0c;它想要跳到点 n 处。然而道路上可能有一些障碍。 给你一个长度为 n 1 的数组 obstacle…

Cheat Engine7.4 训练教程(非常详细)

目录 一.CE是干什么的&#xff1f; 二.怎么获得&#xff1f; 三.训练教程 步骤 1: 加载进程 步骤 2: 精确值扫描 步骤 3: 未知的初始值 步骤 4: 浮点数 步骤 5: 代码查找 步骤 6: 指针 步骤 7: 代码注入 步骤 8: 多级指针 提示&#xff1a;这篇文章不是一天写完的&a…

设计模式-建造者模式

1.概述 将一个复杂对象的构建与表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。 分离了部件的构造(由Builder来负责)和装配(由Director负责)。 从而可以构造出复杂的对象。这个模式适用于&#xff1a;某个对象的构建过程复杂的情况。由于实现了构建和装配的解耦…

基于ffmpeg的视频处理与MPEG的压缩试验(下载安装使用全流程)

基于ffmpeg的视频处理与MPEG的压缩试验ffmpeg介绍与基础知识对提取到的图像进行处理RGB并转化为YUV对YUV进行DCT变换对每个8*8的图像块进行进行量化操作ffmpeg介绍与基础知识 ffmpeg是视频和图像处理的工具包&#xff0c;它的下载网址是https://ffmpeg.org/download.html。页面…

MySQL高级

存储引擎 MySQL体系结构&#xff1a; 存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式。存储引擎是基于表而不是基于库的&#xff0c;所以存储引擎也可以被称为表引擎。 默认存储引擎是InnoDB。 相关操作&#xff1a; -- 查询建表语句 show create table ac…

Python量化交易07——散户反买策略

参考来源&#xff1a;b站up 邢不行 我们都知道在A股&#xff0c;散户一直是最大的韭菜贡献组群。散户买入多的个股&#xff0c;大概率可能跌的很惨&#xff0c;散户卖出多的股票&#xff0c;大概率会涨。 跟着北向资金买能赚钱&#xff0c;那么跟着散户反买&#xff0c;是不是…