1.Map
声明1
1.1 双列集合的特点
单列集合一次只能添加一个元素,双列集合一次可以添加一对元素
例:
小米手机 | 2000 |
---|---|
华为手机 | 5000 |
苹果手机 | 9000 |
这三对元素,左边的我们称之为键,右边的称为值。他们是一一对应的关系
所以双列集合中的元素又叫键值对、键值对对象、Entry
总结:
双列集合的特点
- 双列集合一次需要存一对数据,分别为键和值
- 键不能重复, 值可以重复
- 键和值是一一对应的,每一个键只能找到自己对应的值
- 键+值 这个整体 我们称之为“键值对”或者“键值对对象”,在Java中叫做“
Entry对象
”
1.2Map集合方法
-
方法介绍
方法名 说明 V put(K key,V value) 添加元素2 V remove(Object key) 根据键删除键值对元素3 void clear() 移除所有的键值对元素 boolean containsKey(Object key) 判断集合是否包含指定的键 boolean containsValue(Object value) 判断集合是否包含指定的值 boolean isEmpty() 判断集合是否为空 int size() 集合的长度,也就是集合中键值对的个数 - 当键不存在的时候返回的是null
- 当集合中有键的时候会覆盖这个键对应的值并把这个值进行返回
-
示例代码
public class MapDemo02 { public static void main(String[] args) { //创建集合对象 Map<String,String> map = new HashMap<String,String>(); //V put(K key,V value):添加元素 map.put("张无忌","赵敏"); map.put("郭靖","黄蓉"); map.put("杨过","小龙女"); //V remove(Object key):根据键删除键值对元素 System.out.println(map.remove("郭靖")); System.out.println(map.remove("郭襄")); //void clear():移除所有的键值对元素 map.clear(); //boolean containsKey(Object key):判断集合是否包含指定的键 System.out.println(map.containsKey("郭靖")); System.out.println(map.containsKey("郭襄")); //boolean isEmpty():判断集合是否为空 System.out.println(map.isEmpty()); //int size():集合的长度,也就是集合中键值对的个数 System.out.println(map.size()); //输出集合对象 System.out.println(map); } }
1.3Map集合的获取方法
-
方法介绍
方法名 说明 V get(Object key) 根据键获取值 Set keySet() 获取所有键的集合 Collection values() 获取所有值的集合 Set<Map.Entry<K,V>> entrySet() 获取所有键值对对象的集合 -
示例代码
public class MapDemo03 { public static void main(String[] args) { //创建集合对象 Map<String, String> map = new HashMap<String, String>(); //添加元素 map.put("张无忌", "赵敏"); map.put("郭靖", "黄蓉"); map.put("杨过", "小龙女"); //V get(Object key):根据键获取值 // System.out.println(map.get("张无忌")); // System.out.println(map.get("张三丰")); //Set<K> keySet():获取所有键的集合 // Set<String> keySet = map.keySet(); // for(String key : keySet) { // System.out.println(key); // } //Collection<V> values():获取所有值的集合 Collection<String> values = map.values(); for(String value : values) { System.out.println(value); } } }
1.4Map集合的遍历
遍历1
-
遍历思路
- 我们刚才存储的元素都是成对出现的,所以我们把Map看成是一个夫妻对的集合
- 把所有的丈夫给集中起来
- 遍历丈夫的集合,获取到每一个丈夫
- 根据丈夫去找对应的妻子
- 我们刚才存储的元素都是成对出现的,所以我们把Map看成是一个夫妻对的集合
-
步骤分析
- 获取所有键的集合。用
keySet()
方法实现 - 遍历键的集合,获取到每一个键。用增强for实现
- 根据键去找值。用
get(Object key)
方法实现
- 获取所有键的集合。用
-
代码实现
在进行遍历的时候可以使用以前我们使用的三种方法:
- 迭代器
- 增强for
- Lamda表达式
public class MapDemo01 { public static void main(String[] args) { //创建集合对象 Map<String, String> map = new HashMap<String, String>(); //添加元素 map.put("张无忌", "赵敏"); map.put("郭靖", "黄蓉"); map.put("杨过", "小龙女"); //获取所有键的集合。用keySet()方法实现 Set<String> keySet = map.keySet(); //遍历键的集合,获取到每一个键。用增强for实现 for (String key : keySet) { //根据键去找值。用get(Object key)方法实现 String value = map.get(key); System.out.println(key + "," + value); } } }
遍历2
-
遍历思路
- 我们刚才存储的元素都是成对出现的,所以我们把Map看成是一个夫妻对的集合
- 获取所有结婚证的集合
- 遍历结婚证的集合,得到每一个结婚证
- 根据结婚证获取丈夫和妻子
- 我们刚才存储的元素都是成对出现的,所以我们把Map看成是一个夫妻对的集合
-
步骤分析
-
获取所有键值对对象的集合
Set<Map.Entry<K,V>> entrySet()
:获取所有键值对对象的集合
-
遍历键值对对象的集合,得到每一个键值对对象
- 用增强for实现,得到每一个
Map.Entry
4
- 用增强for实现,得到每一个
-
根据键值对对象获取键和值
- 用
getKey()
得到键 - 用
getValue()
得到值
这里的get方法应该是特有的方法,获得键和值
- 用
-
-
代码实现
public class MapDemo02 { public static void main(String[] args) { //创建集合对象 Map<String, String> map = new HashMap<String, String>(); //添加元素 map.put("张无忌", "赵敏"); map.put("郭靖", "黄蓉"); map.put("杨过", "小龙女"); //获取所有键值对对象的集合 Set<Map.Entry<String, String>> entrySet = map.entrySet(); //遍历键值对对象的集合,得到每一个键值对对象 for (Map.Entry<String, String> me : entrySet) { //根据键值对对象获取键和值 String key = me.getKey(); String value = me.getValue(); System.out.println(key + "," + value); } } }
forEeach遍历3
使用Map.ForEach()
方法传入匿名内部类来遍历
同时使用Lambda
表达式来简化代码
Map<String , Integer> map1 = new HashMap<>();
map1.forEach(new BiConsumer<String, Integer>() {
@Override
public void accept(String s, Integer integer) {
}
});
我们要注意:上述的
Biconsumer
接口的泛型类型和所处的代码环境有关,在主方法中推荐的泛型类型是:<T extends Object, U extends Object>
1.5HashMap集合
HashMap集合概述和特点
- 所有Map集合的特点都是由键决定的:
- 存取无序
- 不重复
- 无索引
-
特点:
HashMap
底层是哈希表结构的- 依赖
hashCode
方法和equals
方法保证键的唯一 - 如果键要存储的是自定义对象,需要重写
hashCode和equals
方法
需要注意只跟键有关,当键的值重复的时候,就会进行覆盖而不是舍弃,这一点和
HashSet
有所区别
HashMap集合应用
-
案例需求
- 创建一个HashMap集合,键是学生对象(Student),值是居住地 (String)。存储多个元素,并遍历。
- 要求保证键的唯一性:如果学生对象的成员变量值相同,我们就认为是同一个对象
-
代码实现
学生类
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; if (age != student.age) return false; return name != null ? name.equals(student.name) : student.name == null; } @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; result = 31 * result + age; return result; } }
测试类
public class HashMapDemo { public static void main(String[] args) { //创建HashMap集合对象 HashMap<Student, String> hm = new HashMap<Student, String>(); //创建学生对象 Student s1 = new Student("林青霞", 30); Student s2 = new Student("张曼玉", 35); Student s3 = new Student("王祖贤", 33); Student s4 = new Student("王祖贤", 33); //把学生添加到集合 hm.put(s1, "西安"); hm.put(s2, "武汉"); hm.put(s3, "郑州"); hm.put(s4, "北京"); //遍历集合 Set<Student> keySet = hm.keySet(); for (Student key : keySet) { String value = hm.get(key); System.out.println(key.getName() + "," + key.getAge() + "," + value); } } }
1.6LinkedHashMap集合
特点:
- 由键决定: 有序、不重复、无索引。
- 这里的有序指的是保证存储和取出的元素顺序一致-
- 原理: 底层数据结构是依然哈希表,只是每个键值对元素又额外的多了一个双链表的机制记录存储的顺序。5
LinkedHashMap
没有什么需要我们特别掌握的点,它的使用方法和HashMap
没有本质的区别
##TreeMap集合
1.7TreeMap集合
TreeMap
底层是红黑树结构- 依赖自然排序或者比较器排序,对键进行排序
- 如果键存储的是自定义对象,需要实现
Comparable接口
或者在创建TreeMap
对象时候给出比较器排序规则
两种比较器使用
-
Comparator
:TreeMap<Integer,String> tr = new TreeMap<>(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { //o1:现在这个键的值 //o2:已经存在在的键的值 return o1 - o2; } }); //使用Lambda表达式 TreeMap<Integer,String> tr = new TreeMap<>(( o1, o2)->{ return o1 - o2 } );
-
自定义类实现
Comparable
接口public class Student implements Comparable<Student>{ //在接口中重写compareTo方法 @Override public int compareTo(Student o) { //按照年龄进行排序 int result = o.getAge() - this.getAge(); //次要条件,按照姓名排序。 result = result == 0 ? o.getName().compareTo(this.getName()) : result; return result; } }
this
:表示当前要添加的元素o
:表示已经在红黑树中存在的元素
返回值:
- 负数:表示当前要添加的元素是小的,存左边
- 正数:表示当前要添加的元素是大的,存右边
- 0:表示当前要添加的元素已经存在,舍弃
需要我们注意的是:上述代码是重写了
Student
类中的comapreTo
方法,在方法中this.getName()方法的返回值是String类型,所以在使用的时候调用的是String中重写的比较方法 -
两种比较器的使用规范:
- 对于Java已经写好的引用类型来说:默认的比较规则是在
Comparable
接口中写好的,当其中的比较规则不能满足我们的需求时,我们使用comparator
比较器来自定义规则 - 对于我们自定义的类来说,我们直接重写
Comparable
接口中的compareTo
方法即可
- 对于Java已经写好的引用类型来说:默认的比较规则是在
TreeMap集合应用案例
-
案例需求
- 创建一个TreeMap集合,键是学生对象(Student),值是籍贯(String),学生属性姓名和年龄,按照年龄进行排序并遍历
- 要求按照学生的年龄进行排序,如果年龄相同则按照姓名进行排序
-
代码实现
学生类
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 = o.getAge() - this.getAge(); //次要条件,按照姓名排序。 result = result == 0 ? o.getName().compareTo(this.getName()) : result; return result; } }
测试类
public class Test1 { public static void main(String[] args) { // 创建TreeMap集合对象 TreeMap<Student,String> tm = new TreeMap<>(); // 创建学生对象 Student s1 = new Student("xiaohei",23); Student s2 = new Student("dapang",22); Student s3 = new Student("xiaomei",22); // 将学生对象添加到TreeMap集合中 tm.put(s1,"江苏"); tm.put(s2,"北京"); tm.put(s3,"天津"); // 遍历TreeMap集合,打印每个学生的信息 tm.forEach( (Student key, String value)->{ System.out.println(key + "---" + value); } ); } }
HashMap和TreeMap源码解析
略
2.可变参数
这是补充内容,可变参数指的是在方法中形参的数量可以变化
格式:
数据类型...名字
举例:int...args
例如:假如构造一个方法计算n个数字的相加
-
我们之前的方法:使用数组
int[] arr = {1,2,3,4,5,6,7,8,9,10}; public static int Sum(int[] arr) { int sum = 0; for (int ar:arr) { sum += ar; } return sum; }
但是这样的方法不够方便
-
现在使用可变参数
//测试类 Sum(1, 2, 3,4,5);//可以传入任意个数 //方法 public static int Sum2(int...args) { int sum = 0; for (int arg : args) { sum += arg; } return sum; }
在底层实际上也是使用了数组的方法,只是Java把这些步骤帮我们省去了
在底层也是创建了一个
args
名字的数组
细节:
- 在方法的形参中至多只能出现一个可变参数
- 当一个方法的形参列表除了可变参数还有其他参数的时候,要把可变参数放在形参列表的最后面6
3.Collections工具类
这是集合的工具类而不是集合,它是用来操作集合的
工具类的特点:
- 没有成员变量
- 私有化构造方法
- 静态成员方法
3.1常用API
为了方便这里直接给上图片
需要我们掌握的是前两个
-
addAll
第一个形参是所有的
Collection
接口的实现类,方法中使用了多态的方式表示 -
shuffle
只适用于
List
集合
4.集合嵌套
在存储复杂信息的时候我们通常会使用集合的嵌套,键值对中的值存储的还是一个集合
ArrayList<String> list = new ArrayList<>();
//在集合中添加元素
Map<String, String> map = new HashMap<>();
map.put("henansheng",list)
注释:
来自黑马程序员 ↩︎
put
方法有细节需要注意
put
方法注意细节 ↩︎根据键进行键值对的删除 ↩︎
同样也是
Set
集合,所以可以使用另外的两种遍历方法 ↩︎这里使用的是双向链表
↩︎上面的两个细节都是因为可变参数可以接收理论上实参中所有的参数,但是可变参数接收完了,其他的参数就没有实参对应了 ↩︎