(2)第二种——获取到所有的键值对对象,每一个对象打印键、值
Map集合
之前学习的单列集合
而双列集合如下图所示:
在Map中,键值对是一一对应的关系。
把一对键值对视为键值对对象,在Java中,叫做Entry
概述
interface Map<K,V> K:键的类型;V:值的类型
特点
-
双列集合,一个键对应一个值
-
键不可以重复,值可以重复
基本使用
public class MapDemo01 {
public static void main(String[] args) {
//创建集合对象
Map<String,String> map = new HashMap<String,String>();
//V put(K key, V value) 将指定的值与该映射中的指定键相关联
// 学号 -- 姓名
map.put("201810066","林青霞");
map.put("201810044","张曼玉");
map.put("201810022","王祖贤");
map.put("201810056","柳岩");
//输出集合对象
System.out.println(map);
//{201810056=柳岩, 201810066=林青霞, 201810044=张曼玉, 201810022=王祖贤}
}
}
基本功能(定义在Map接口中的共性方法)
方法名 | 说明 |
---|---|
V put(K key,V value) | 添加元素 |
V remove(Object key) | 根据键删除键值对元素 |
void clear() | 移除所有的键值对元素 |
boolean containsKey(Object key) | 判断集合是否包含指定的键 |
boolean containsValue(Object value) | 判断集合是否包含指定的值 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中键值对的个数 |
代码实现
public class Demo{
public static void main(String[] args) {
Map<String,String> mp = new HashMap<>();
//(1) V put(K key,V value)添加元素
//如果键不存在,就直接添加到集合中
//如果键存在,那么会覆盖原先的值,并把原先的值返回
mp.put("1001","小明");
mp.put("1002","小强");
mp.put("1003","小张");
mp.put("1004","小李");
System.out.println(mp);
//{1004=小李, 1003=小张, 1002=小强, 1001=小明}
String s1 = mp.put("1004","aaa");
System.out.println(s1);
//小李
System.out.println(mp);
//{1004=aaa, 1003=小张, 1002=小强, 1001=小明}
//(2) V remove(Object key)根据键删除键值对元素
String s2 = mp.remove("1004");
System.out.println(s2);
//aaa
System.out.println(mp);
//{1003=小张, 1002=小强, 1001=小明}
//(3) boolean containsKey(Object key)判断集合是否包含指定的键
System.out.println(mp.containsKey("1001"));
//true
System.out.println(mp.containsKey("2002"));
//false
//(4) boolean containsValue(Object value)判断集合是否包含指定的值
System.out.println(mp.containsValue("小张"));
//true
//(5) int size()集合的长度,也就是集合中键值对的个数
System.out.println(mp.size());
//3
//(6) boolean isEmpty()判断集合是否为空
System.out.println(mp.isEmpty());
//false
//(7) void clear()移除所有的键值对元素
mp.clear();
System.out.println(mp);
//{} 空!
}
}
获取功能
方法名 | 说明 |
---|---|
V get(Object key) | 根据键获取值 |
Set<K> keySet() | 获取所有键的集合 |
Collection<V> values() | 获取所有值的集合 |
Set<Map.Entry<K,V>> entrySet() | 获取所有键值对对象的集合 |
Map集合的遍历(2种方式)
(1)第一种——获取所有的键,get到所有的值
详细步骤如下
代码实现
public class Demo {
public static void main(String[] args) {
//创建集合并添加元素
Map<String,String> map = new HashMap<>();
map.put("1号丈夫","1号妻子");
map.put("2号丈夫","2号妻子");
map.put("3号丈夫","3号妻子");
map.put("4号丈夫","4号妻子");
map.put("5号丈夫","5号妻子");
//获取所有的键
Set<String> keys = map.keySet();
for (String key : keys) {
//通过每一个key,来获取到对应的值
String value = map.get(key);
System.out.println(key + "-" + value);
//1号丈夫-1号妻子
//2号丈夫-2号妻子
//5号丈夫-5号妻子
//4号丈夫-4号妻子
//3号丈夫-3号妻子
}
}
}
(2)第二种——获取到所有的键值对对象,每一个对象打印键、值
详细步骤如下
代码实现
public class Demo {
public static void main(String[] args) {
//创建集合并添加元素
Map<String,String> map = new HashMap<>();
map.put("1号丈夫","1号妻子");
map.put("2号丈夫","2号妻子");
map.put("3号丈夫","3号妻子");
map.put("4号丈夫","4号妻子");
map.put("5号丈夫","5号妻子");
//首先要获取到所有的键值对对象
//Set集合里面装的是键值对对象(Entry对象)
//而Entry里面装的是键和值
Set<Map.Entry<String,String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
//得到每一个键值对对象
System.out.println(entry.getKey() + "-" + entry.getValue());
//1号丈夫-1号妻子
//2号丈夫-2号妻子
//5号丈夫-5号妻子
//4号丈夫-4号妻子
//3号丈夫-3号妻子
}
}
}
HashMap集合
概述和特点
-
HashMap底层是哈希表结构的
-
依赖hashCode方法和equals方法保证键的唯一
-
如果键要存储的是自定义对象,需要重写hashCode和equals方法
HashMap集合应用案例
案例需求
创建一个HashMap集合,键是学生对象(Student),值是居住地 (String)。存储多个元素,并遍历。
要求保证键的唯一性:如果学生对象的成员变量值相同,我们就认为是同一个对象
代码实现
由于此处键是自定义对象,所以我们必须要重写hashCode()与equals方法。
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
使用3种方式遍历HashMap
public class Demo {
public static void main(String[] args) {
HashMap<Student, String> hm = new HashMap<>();
Student s1 = new Student("小明", 12);
Student s2 = new Student("小张", 22);
Student s3 = new Student("小李", 32);
Student s4 = new Student("小红", 15);
hm.put(s1, "南京");
hm.put(s2, "北京");
hm.put(s3, "上海");
hm.put(s4, "广州");
//(1) 第一种遍历:先找到所有的键,再通过每一个键找到对应的值
Set<Student> keys = hm.keySet();
for (Student key1 : keys) {
String value1 = hm.get(key1);
System.out.println(key1 + "--" + value1);
}
System.out.println("======================");
//(2) 第二种遍历:先获取到所有的键值对对象,再获取里面的每一个键和每一个值
Set<Map.Entry<Student, String>> entries = hm.entrySet();
for (Map.Entry<Student, String> entry : entries) {
Student key2 = entry.getKey();
String value2 = entry.getValue();
System.out.println(key2 + "--" + value2);
}
System.out.println("======================");
//(3) 第三种遍历:使用Map种的forEach方法
hm.forEach(
(Student key3, String value3) -> {
System.out.println(key3 + "--" + value3);
}
);
}
}
运行结果
Student{name='小张', age=22}--北京
Student{name='小李', age=32}--上海
Student{name='小红', age=15}--广州
Student{name='小明', age=12}--南京
======================
Student{name='小张', age=22}--北京
Student{name='小李', age=32}--上海
Student{name='小红', age=15}--广州
Student{name='小明', age=12}--南京
======================
Student{name='小张', age=22}--北京
Student{name='小李', age=32}--上海
Student{name='小红', age=15}--广州
Student{name='小明', age=12}--南京Process finished with exit code 0
forEach()源码解析
我们可以选中HashMap然后Ctrl+B
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
然后再进入Map接口里面,再Ctrl+F12,搜索出forEach()方法
default void forEach(BiConsumer<? super K, ? super V> action) {
Objects.requireNonNull(action);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
action.accept(k, v);
}
}
可以发现action是BiConsumer类型的,而BiConsumer只是一个接口,里面只有一个抽象方法accept
public interface BiConsumer<T, U> {
void accept(T t, U u);
......
}
具体过程如下
总的来说就是:
forEach()方法底层进行了一次遍历,依次得到每一个键和值,然后通过action.accept(k,v),将键和值又传递给,我们自己写的lambda表达式中,来执行对应的逻辑。
TreeMap集合
概述与特点
-
TreeMap底层是红黑树结构
-
依赖自然排序或者比较器排序,对键进行排序
-
如果键存储的是自定义对象,需要实现Comparable接口或者在创建TreeMap对象时候给出比较器排序规则
TreeMap集合应用案例
案例需求
-
创建一个TreeMap集合,键是学生对象(Student),值是籍贯(String),学生属性姓名和年龄,按照年龄进行排序并遍历
-
要求按照学生的年龄进行排序,如果年龄相同则按照姓名进行排序
代码实现
实现方式(一)实现Comparable接口
public class Demo {
public static void main(String[] args) {
TreeMap<Student,String> tm = new TreeMap<>();
Student s1 = new Student("小黑",23);
Student s2 = new Student("小红",32);
Student s3 = new Student("小白",13);
Student s4 = new Student("小李",34);
tm.put(s1,"南京");
tm.put(s2,"上海");
tm.put(s3,"广州");
tm.put(s4,"江苏");
tm.forEach(
(Student key,String value) -> {
System.out.println(key + "---" + value);
}
);
//Student{name='小白', age=13}---广州
//Student{name='小黑', age=23}---南京
//Student{name='小红', age=32}---上海
//Student{name='小李', age=34}---江苏
}
}
public class Student implements Comparable<Student>{
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
//按照年龄
int result = this.getAge() - o.getAge();
//按照姓名
result = result == 0 ? this.getName().compareTo(o.getName()) : result;
return result;
}
}
实现方式(二)比较器排序规则
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Demo {
public static void main(String[] args) {
TreeMap<Student,String> tm = new TreeMap<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
int result = o1.getAge() - o2.getAge();
result = result == 0 ? o1.getName().compareTo(o2.getName()) : result;
return result;
}
});
Student s1 = new Student("小黑",23);
Student s2 = new Student("小红",32);
Student s3 = new Student("小白",13);
Student s4 = new Student("小李",34);
tm.put(s1,"南京");
tm.put(s2,"上海");
tm.put(s3,"广州");
tm.put(s4,"江苏");
tm.forEach(
(Student key,String value) -> {
System.out.println(key + "---" + value);
}
);
//Student{name='小白', age=13}---广州
//Student{name='小黑', age=23}---南京
//Student{name='小红', age=32}---上海
//Student{name='小李', age=34}---江苏
}
}