Collection集合的格式:
[元素1, 元素2, 元素3, ...]
Map集合的格式:
{key1=value1, key2=value2, key3=value3, ...}
通俗点讲就是,key是老公,value是老婆,都是成双成对的,并且不能出轨。
分析:
购物车购买提供的四个商品和购买数量在后台需要容器存储。
每个商品对象都一一对应一个购买数量。
把商品对象看成是Map集合的键,购买数量看成Map集合的值。
{商品1=2, 商品2=3, 商品3=2, 商品4=3}
1、Map集合是什么?适用场景是什么样的?
都是由键决定的
。键是无序、不重复、无索引,值不做要求
。后面重复的键对应的值
会覆盖前面重复键的值
。键值对都可以为null
HashMap:元素按照键是无序、不重复、无索引,值不做要求。(与Map(父亲)体系一致)
LinkedHashMap:元素按照键是有序
、不重复、无索引,值不做要求。
排序
、不重复、无索引,值不做要求。1、Map集合的特点是什么样的?
有序
、不重复、无索引,值不做要求。排序
、不重复、无索引,值不做要求。Map的API如下:
方法 | 说明 |
---|---|
V put(K key, V value) | 添加元素 |
V remove(Object key) | 根据键删除键值对元素 |
V get(Object key) | 根据键获取对应的值 |
Set< K > keyset() | 获取全部键的集合 |
Collection< V > values() | 获取全部值的集合 |
void clear() | 移除所有键值对元素 |
boolean containsKey(Object key) | 判断集合是否包含指定的键 |
boolean containsValue(Object value) | 判断集合是否包含指定的值 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中键值对的个数 |
void putAll(Map extends K, ? extends V> m) | 把一个集合中的所有元素拷贝到另一个集合中去 |
package com.app.d8_map_api;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
目标:掌握Map集合体系的常用API
*/
public class MapAPIDemo1 {
public static void main(String[] args) {
// 1、添加元素:无序、不重复、无索引。
Map<String, Integer> maps = new HashMap<>();
maps.put("李宁音速6", 10);
maps.put("娃娃", 20);
maps.put("李宁音速6", 100); // Map集合后面的键对应的元素,会覆盖前面重复的整个键值对元素。
maps.put("华为nova7", 100);
maps.put("毛巾", 10);
maps.put("手表", 10);
System.out.println(maps); // {手表=10, 李宁音速6=100, 华为nova7=100, 娃娃=20, 毛巾=10}
// 2、清空集合
// maps.clear();
// System.out.println(maps);
System.out.println("-----------------------------------");
// 3、判断集合是否为空,为空返回true,反之!
System.out.println(maps.isEmpty());
System.out.println("-----------------------------------");
// 4、根据键获取对应值
Integer key = maps.get("李宁音速6");
System.out.println(key); // 100
System.out.println(maps.get("华为nova7")); // 100
System.out.println(maps.get("华为nova9")); // null
System.out.println("-----------------------------------");
// 5、根据键删除整个元素。(删除键会返回键的值)
System.out.println(maps.remove("娃娃"));
System.out.println(maps);
System.out.println("-----------------------------------");
// 6、判断是否包含某个键,包含返回true,反之!
System.out.println(maps.containsKey("华为nova7")); // true
System.out.println(maps.containsKey("娃娃")); // false
System.out.println(maps.containsKey("娃娃2")); // false
System.out.println("-----------------------------------");
// 7、判断是否包含某个值,包含返回true,反之!
System.out.println(maps.containsValue(100)); // true
System.out.println(maps.containsValue(10)); // false
System.out.println(maps.containsValue(33)); // false
System.out.println("-----------------------------------");
// {手表=10, 李宁音速6=100, 华为nova7=100, 娃娃=20, 毛巾=10}
// 8、获取全部键的集合:Set keySet()
// 为什么用Set集合来接?因为Set集合:无序、不重复、无索引,符合Map集合的键的特点
Set<String> keys = maps.keySet();
System.out.println(keys); // [李宁音速6, 华为nova7, 娃娃, 毛巾]
System.out.println("-----------------------------------");
// 9、获取全部值的集合:Collection values()
// 为什么用Collection集合来接?因为Collection集合:可重复,可以保留重复的值
Collection<Integer> values = maps.values();
System.out.println(values); // [10, 100, 100, 10]
System.out.println("-----------------------------------");
// 10、集合的大小
System.out.println(maps.size()); // 4,因为键值对是一个整体
System.out.println("----------------拓展---------------");
// 11、合并其他Map集合。(拓展)
Map<String, Integer> map1 = new HashMap<>();
map1.put("java1", 1);
map1.put("java2", 100);
Map<String, Integer> map2 = new HashMap<>();
map2.put("java2", 1);
map2.put("java3", 100);
map1.putAll(map2); // 把集合map2的元素拷贝一份到map1中去
System.out.println(map1);
System.out.println(map2);
}
}
{手表=10, 李宁音速6=100, 华为nova7=100, 娃娃=20, 毛巾=10}
-----------------------------------
false
-----------------------------------
100
100
null
-----------------------------------
20
{手表=10, 李宁音速6=100, 华为nova7=100, 毛巾=10}
-----------------------------------
true
false
false
-----------------------------------
true
true
false
-----------------------------------
[手表, 李宁音速6, 华为nova7, 毛巾]
-----------------------------------
[10, 100, 100, 10]
-----------------------------------
4
----------------拓展---------------
{java3=100, java2=1, java1=1}
{java3=100, java2=1}
Process finished with exit code 0
取出全部键的集合后,遍历键的Set集合,然后根据键找对应的值。
方法 | 说明 |
---|---|
Set< K > keySet() | 获取所有键的集合 |
V get(Object key) | 根据键获取值 |
package com.app.d9_map_traverse;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
目标:掌握Map集合的遍历方式一:键找值
*/
public class MapTraverseDemo1 {
public static void main(String[] args) {
// 创建一个Map集合,添加一些数据
Map<String, Integer> maps = new HashMap<>();
maps.put("宇智波鼬手办", 20);
maps.put("Nike跑鞋", 100);
maps.put("手表", 20);
maps.put("U盘", 2000);
System.out.println("遍历前:" + maps); // {手表=20, 宇智波鼬手办=20, U盘=2000, Nike跑鞋=100}
System.out.println();
// 键找值遍历方式
// 1、先获取全部键的集合
Set<String> keys = maps.keySet();
// 2、遍历全部的键
System.out.println("遍历后:");
for (String key : keys) {
// 根据键得到对应的值
int value = maps.get(key);
System.out.println(key + "==>" + value);
}
}
}
遍历前:{手表=20, 宇智波鼬手办=20, U盘=2000, Nike跑鞋=100}
遍历后:
手表==>20
宇智波鼬手办==>20
U盘==>2000
Nike跑鞋==>100
Process finished with exit code 0
方法 | 说明 |
---|---|
Set< Map.Entry< K, V > > entrySet() | 获取所有键值对对象的集合 |
K getKey() | 获取键 |
V getValue() | 获取值 |
package com.app.d9_map_traverse;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
目标:掌握Map集合的遍历方式二:键值对
*/
public class MapTraverseDemo2 {
public static void main(String[] args) {
// 创建一个Map集合,添加一些数据
Map<String, Integer> maps = new HashMap<>();
maps.put("宇智波鼬手办", 20);
maps.put("Nike跑鞋", 100);
maps.put("手表", 20);
maps.put("U盘", 2000);
System.out.println("遍历前:" + maps); // {手表=20, 宇智波鼬手办=20, U盘=2000, Nike跑鞋=100}
System.out.println();
/*
{手表=20, 宇智波鼬手办=20, U盘=2000, Nike跑鞋=100}
☟
使用foreach遍历Map集合,发现Map集合的键值对元素是没有直接类型的,因此不能直接foreach遍历集合。
☟
通过调用Map集合的API,entrySet()方法把Map集合转换成Set集合形式:maps.entrySet()。
方法内部其实也是先将全部键和全部值遍历出来,然后将键和值封装成一个对象,这样就可以作为一个对象遍历了
[(手表=20), (宇智波鼬手办=20), (U盘=2000), (Nike跑鞋=100)]
entry
☟
Set> entries = maps.entrySet();
☟
此时可以使用foreach遍历这个键值对对象了
*/
// 1、把Map集合转换成Set集合形式:将键值对封装成一个对象
Set<Map.Entry<String, Integer>> entries = maps.entrySet();
// 2、使用foreach遍历这个键值对对象
System.out.println("遍历后:");
for (Map.Entry<String, Integer> entry : entries){
String key = entry.getKey(); // 获取键
int value = entry.getValue(); // 获取值
System.out.println(key + "==>" + value);
}
}
}
遍历前:{手表=20, 宇智波鼬手办=20, U盘=2000, Nike跑鞋=100}
遍历后:
手表==>20
宇智波鼬手办==>20
U盘==>2000
Nike跑鞋==>100
Process finished with exit code 0
方法 | 说明 |
---|---|
default void forEach(BiConsumer super K, ? super V> action) | 结合lambda遍历Map集合 |
package com.app.d9_map_traverse;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
/**
目标:掌握Map集合的遍历方式三:结合lambda表达式遍历Map集合
*/
public class MapTraverseDemo3 {
public static void main(String[] args) {
// 创建一个Map集合,添加一些数据
Map<String, Integer> maps = new HashMap<>();
maps.put("宇智波鼬手办", 20);
maps.put("Nike跑鞋", 100);
maps.put("手表", 20);
maps.put("U盘", 2000);
System.out.println("遍历前:" + maps); // {手表=20, 宇智波鼬手办=20, U盘=2000, Nike跑鞋=100}
System.out.println();
// 1、使用JDK8后的新技术,结合lambda遍历map
// {手表=20, 宇智波鼬手办=20, U盘=2000, Nike跑鞋=100}
/*
分析:
其实forEach()内部是用方式二帮我们遍历的:
先将Map集合转换成Set集合形式:将键值对封装成一个对象;
取出键和值,回调accept()方法,分别将键和值传送给这里
*/
/*maps.forEach(new BiConsumer() {
@Override
public void accept(String key, Integer value) {
System.out.println(key + "==>" + value);
}
});*/
System.out.println("遍历后:");
maps.forEach(( k, v) -> System.out.println(k + "==>" + v) );
}
}
遍历前:{手表=20, 宇智波鼬手办=20, U盘=2000, Nike跑鞋=100}
遍历后:
手表==>20
宇智波鼬手办==>20
U盘==>2000
Nike跑鞋==>100
Process finished with exit code 0
package com.app.d10_map_test;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
/**
需求:
某个班级80名学生,现在需要组成秋游活动,班长提供了四个景点依次是(A、B、C、D),
每个学生只能选择一个景点,请统计出最终哪个景点想去的人数最多。
*/
public class MapTest {
public static void main(String[] args) {
// 1、将80个学生选择的景点拿到程序中。
// a、提供四个景点给学生选择:A B C D(个数确定、类型确定。数组存)
String[] selects = {"A", "B", "C", "D"};
// b、创建随机数对象,用于随机一个索引,模拟学生选择景点
Random rd = new Random();
// c、创建StringBuilder对象,用于拼接80名学生的选择结果
StringBuilder sb = new StringBuilder();
// d、模拟80名学生选择景点
for (int i = 0; i < 80; i++) {
// e、拼接80名学生选择的结果
sb.append(selects[rd.nextInt(selects.length)]);
}
System.out.println("随机选择结果:" + sb); // 80名学生的最终选择结果
// 2、定义Map集合用于存储最终统计的结果。例如:选择A景点=30名学生,B=20,C=20,D=10。键是景点,值是选择数量
Map<Character, Integer> infos = new HashMap<>();
// 3、遍历80个学生选择的景点,看Map集合中是否存在,不存在存入 “某景点=1”,存在则其对应值+1。
// a、定义循环遍历80名学生选择的景点
for (int i = 0; i < sb.length(); i++) {
// b、提取当前选择的景点字符
char attraction = sb.charAt(i);
// c、判断当前选择的景点是否存在于集合中
if (infos.containsKey(attraction)){
// 存在,说明此景点不是第一次被选择,让其景点的选择票数+1
infos.put(attraction, infos.get(attraction) + 1);
}else {
// 不存在,说明此景点是第一次被选择,让其景点的选择票数为1
infos.put(attraction, 1);
}
}
// System.out.println("最终统计票数:" + infos);
// 4、统计完选择票数后,优化输出
infos.forEach( (k, v) -> System.out.println("景点" + k + "的选择票数:" + v) );
}
}
随机选择结果:CBCCADAACCAABCDCCBBACBDBDDDDBBDABCDAACBDDABCDDDDABDDDAABCCCDABCCBBCABDDBDAABADBC
景点A的选择票数:18
景点B的选择票数:20
景点C的选择票数:19
景点D的选择票数:23
Process finished with exit code 0
只需要自己控制程序一步一步往下走,就可以看完整个执行流程了!!
如果已经明白了整个流程,不想看了,可以结束Debug的执行:
HashMap跟HashSet底层原理是一模一样的,都是哈希表结构
,只是HashMap的每个元素包含两个值(键值对)而已。
实际上:Set系列集合的底层就是Map实现的,只是Set集合中的元素只要键数据,不要值数据而已。
HashMap的添加原理:
将键、值作为一个Entry对象,然后获取Entry对象的哈希值,对底层位置求余,存入求余结果对应的位置。
如果下一个要添加的元素也算到了同一个位置,就会形成链表,当链表长度超过8,就会自动转换为红黑树。
1、HashMap的特点和底层原理是什么样的?
由键决定:无序、不重复、无索引。
HashMap底层是哈希表结构的。
依赖hashCode和equals方法保证键
的唯一。
如果键
要存储的是自定义对象,需要重写hashCode和equals方法。
有序
、不重复、无索引。底层数据结构依然是哈希表,只是每个键值对元素额外多了一个双链表的机制记录存储的顺序。
将要添加的键值对封装成一个对象,然后对底层数组长度求余,存入求余结果对应的位置;
然后上一个元素记住下一个元素的地址,下一个元素又记住上一个元素的地址,形成一个双链表。
可排序
、不重复、无索引。TreeMap跟TreeSet的底层原理是一样的。
实际上:TreeSet底层是基于TreeMap设计的。
package com.app.d11_map_impl;
/**
自定义类:猫类。
指定规则排序方式一:
让自定义的类实现Comparable接口重写里面的compareTo方法,来自定义比较规则。
*/
public class Cat implements Comparable<Cat>{
/**
猫的属性:昵称、毛色、价格、重量
*/
private String name;
private String color;
private double price;
private int weight;
/**
* 重写Comparable接口的compareTo方法,来自定义比较规则。
* @param o 接收一个猫对象
* @return
*/
@Override
public int compareTo(Cat o) {
// 自定义比较规则:按照猫的重量升序排序
return this.weight - o.weight; // 去除重量重复的数据
// return this.weight - o.weight >= 0 ? 1 : -1; // 保留重量重复的数据
}
public Cat(){
}
public Cat(String name, String color, double price, int weight) {
this.name = name;
this.color = color;
this.price = price;
this.weight = weight;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
", color='" + color + '\'' +
", price=" + price +
", weight=" + weight +
'}';
}
}
package com.app.d11_map_impl;
import java.util.Collections;
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
/**
目标:掌握Map集合的实现类:TreeMap
特点:可排序、不重复、无索引
*/
public class TreeMapDemo3 {
public static void main(String[] args) {
// 多态写法
// 1、默认排序
Map<Integer, String> cats = new TreeMap<>();
cats.put(13, "小花猫");
cats.put(2, "小黑猫");
cats.put(23, "小白猫");
cats.put(1, "小黄猫");
cats.forEach( (k, v) -> System.out.println(k + " : " + v) );
System.out.println("------------------------------------");
// Map students = new TreeMap<>(); // 一行经典代码
// 2、自定义排序规则:
// 方式二:集合自定义Comparator比较器对象,重写比较规则。
// 按照猫的价格来降序排序
Map<Cat, String> students = new TreeMap<>((o1, o2) ->
Double.compare(o2.getPrice(), o1.getPrice())
);
students.put(new Cat("胖球", "花白色", 899.9, 40), "广西");
students.put(new Cat("咪咪", "白色", 666.5, 40), "湖北");
students.put(new Cat("小黑", "黑色", 899.8, 25), "广州");
students.put(new Cat("虾皮", "黄色", 566.9, 33), "安徽");
students.forEach( (k, v) -> System.out.println(k + " = " + v) );
}
}
1 : 小黄猫
2 : 小黑猫
13 : 小花猫
23 : 小白猫
------------------------------------
Cat{name='胖球', color='花白色', price=899.9, weight=40} = 广西
Cat{name='小黑', color='黑色', price=899.8, weight=25} = 广州
Cat{name='咪咪', color='白色', price=666.5, weight=40} = 湖北
Cat{name='虾皮', color='黄色', price=566.9, weight=33} = 安徽
Process finished with exit code 0
package com.app.d11_map_impl;
import java.util.*;
/**
目标:通过案例导学,理解集合嵌套
需求:某个班级多名学生,现在需要组成秋游活动,班长提供了四个景点依次是(A、B、C、D),
每个学生可以选择多个景点,请统计出最终哪个景点想去的人数最多。
*/
public class MapTest4 {
public static void main(String[] args) {
// 1、模拟多名学生选择景点,记住每个学生选择的情况
// a、创建一个集合存储学生选择的景点数据
Map<String, List<String>> data = new HashMap<>();
// b、将学生选择的景点数据存入集合中
List<String> stuSelects1 = new ArrayList<>(); // 创建一个集合用于存储学生选择的景点数据
Collections.addAll(stuSelects1, "A", "C"); // 存储学生选择的景点数据
data.put("张飞", stuSelects1); // 将学生选择的景点数据存入集合
List<String> stuSelects2 = new ArrayList<>();
Collections.addAll(stuSelects2, "B", "C", "D");
data.put("关羽", stuSelects2);
List<String> stuSelects3 = new ArrayList<>();
Collections.addAll(stuSelects3, "A", "B", "C", "D");
data.put("赵云", stuSelects3);
System.out.println("所有学生的选择情况:" + data);
// 所有学生的选择情况:{关羽=[B, C, D], 张飞=[A, C], 赵云=[A, B, C, D]}
// values
// 2、统计多名学生的最终投票人数
// a、创建集合用于统计每个景点的投票人数
Map<String, Integer> infos = new HashMap<>();
// b、得到所有人选择的景点数据(值)
Collection<List<String>> values = data.values();
//System.out.println("所有人选择的景点数据:" + values);
// 所有人选择的景点数据:[[B, C, D], [A, C], [A, B, C, D]]
// value
// c、循环遍历所有人选择的景点数据
for (List<String> value : values) {
// [B, C, D]
// a
// d、循环遍历单个人选择的景点数据
for (String a : value) {
// e、判断此景点是否存在于集合中
if (infos.containsKey(a)) {
// 存在,说明此景点不是第一次被选择,让其选择数量+1
infos.put(a, infos.get(a) + 1);
}else {
// 不存在,说明此景点是第一次被选择,让其选择数量为1
infos.put(a, 1);
}
}
}
// 3、统计完每个景点的投票人数,优化输出
infos.forEach((k, v) -> System.out.println("景点" + k + "的投票人数:" + v));
}
}
所有学生的选择情况:{关羽=[B, C, D], 张飞=[A, C], 赵云=[A, B, C, D]}
景点A的投票人数:2
景点B的投票人数:2
景点C的投票人数:3
景点D的投票人数:2
Process finished with exit code 0