目录
一、Map接口
概述:
将键映射到值的对象,Map不能包含重复的键,每个键最多包含一个值。
举例:
<K,V> 键值对:1001 小王;1002 小李 ; 1001 小周(这样是不允许的,小周会覆盖小王)。
Map集合中,K的值不可以重复,但V的值可以重复
1、Map接口与Collection接口的区别:
1、Map接口与Collection接口是平级关系,不存在继承关系,都是属于util包下面的。
2、Map集合中的元素都是成对出现的,Map中的键是唯一的,值是可以重复的,又称之为:夫妻对。Map的数据结构值针对键有效,跟值无关
3、Collection集合中的元素都是单独出现的,Collection接口下有个子接口Set,他的元素也是唯一的;List中的元素也是单独出现的,可以重复,像这样单独出现的元素,称之为:光棍。Collection集合的数据结构是针对元素有效
2、Map接口的子类和方法
(1)各种功能:
1、添加功能
V put(K key,V value) 将指定的值与该映射中的指定键相关联(可选操作)
举例:map.put(1001,"小王");
2、删除功能
void clear() 从该Map中删除所有的映射
V remove(Object key) 如果存在(可选的操作),从该Map中删除一个键的映射。
3、判断功能
boolean containsKey(Object key)如果此映射包含指定键的映射,则返回true。
boolean containsValue(Object value)如果此Map将一个或者多个键映射到指定的值,则返回true。
boolean isEmpty() 如果此Map不包含键值映射,则返回true。
4、获取功能
V get(Object key) 如果此映射包含该键的映射,返回到指定键所映射的值。或者null
Set(K) keySet() 返回此Map中包含的键的Set视图
Collection<V> values() 返回此Map中包含的值的Collection视图
Set<Map.Entry<K,V>>entrySet() 获取Map中所有的元素,元素的组成是一个键和一个值组成。
5、长度功能
int size() 返回此Map中键值映射的数量
添加、删除、判断与获取中的get()方法代码举例:
import java.util.HashMap;
import java.util.Map;
public class Demo1 {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
// 添加功能:
map.put("小王","职员");
map.put("小李","经理");
map.put("小孙","销售");
map.put("小周","财务");
map.put("小王","总监");
System.out.println("未做操作前的Map集合:"+map);
// 这里集合中总监覆盖了职员
// 删除功能:
System.out.println(map.remove("小周"));
// 这里返回的是被删除的键所对应的值
System.out.println("做删除后的Map集合:"+map);
// map.clear();
// 判断功能:
System.out.println(map.containsKey("小李"));
System.out.println(map.containsValue("经理"));
System.out.println(map.isEmpty());
// 获取功能:
System.out.println(map.get("小王"));
// 获取一个不存在的键
System.out.println(map.get("小赵"));
}
}
输出结果:
(2)HashMap
根据哈希表进行存储的,允许出现null键和null键,可以保证键的唯一性
Map获取功能具体代码举例:
这也是Map遍历集合的第一种方式:调用keySet方法通过键找值
import java.util.Collection;
import java.util.HashMap;
import java.util.Set;
public class Demo2 {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
// 添加元素
map.put("小王", "职员");
map.put("小李", "经理");
map.put("小孙", "销售");
map.put("小周", "财务");
// 遍历集合
Set<String> keySet = map.keySet();//获取键并用Set集合存放
for (String keyvalue : keySet) {//遍历集合
String value = map.get(keyvalue);//获取键对应的值
System.out.println(keyvalue + ":" + value);
}
System.out.println("----------------------------------------");
// 返回包含值的Collection视图
Collection<String> collection = map.values();
for (String values : collection) {
System.out.println(values);
}
}
}
输出结果:
这里对于返回包含值的Collection集合,只可返回值,不可以使用值去输出对应的键。
Set<Map.Entry<K,V>> entrySet()方法举例:
这是Map集合遍历的第二种方式:一次性获取所有的键值对,然后依次获取每一个键值对的键和值
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Demo3 {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
// 添加元素
map.put("小王", "职员");
map.put("小李", "经理");
map.put("小孙", "销售");
map.put("小周", "财务");
// 一次性获取所有的键值对
Set<Map.Entry<String, String>> set = map.entrySet();
// 遍历集合获取每一个键值对
for (Map.Entry<String, String> keyvalue : set){
String key = keyvalue.getKey();
String value = keyvalue.getValue();
System.out.println(key+":"+value);
// 可以选择直接输出keyvalue,推荐使用上述方式
}
}
}
输出结果:
Entry通过查看源码发现是一个抽象类,其中定义了getKey()和getValue()方法,并在某一个类中实现了这两种方法,所以可以使用这两种方法获取每一个键值对的键和值。
使用HashMap存储自定义对象
代码举例:
如果key是自己自定义的一个对象,该类需要重写hashCode()方法和equals()方法
因为put方法的底层,添加元素的标准就是根据hashCode()方法和equals()的值来判断元素是否重复
学生类:
import java.util.Objects;
public class Students {
private String name;
private int age;
public Students() {
}
public Students(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;
Students students = (Students) o;
return age == students.age && Objects.equals(name, students.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student4{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试类:
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Demo7 {
public static void main(String[] args) {
//1、创建集合对象
HashMap<Students, String> map = new HashMap<>();
//2、创建学生对象
Students s1 = new Students("王昭君", 17);
Students s2 = new Students("貂蝉", 16);
Students s3 = new Students("杨玉环", 18);
Students s4 = new Students("西施", 19);
Students s5 = new Students("王昭君", 17);
//将元素添加到集合中
map.put(s1, "1111");
map.put(s2, "2222");
map.put(s3, "3333");
map.put(s4, "4444");
map.put(s5, "5555");
//遍历
Set<Map.Entry<Students, String>> entries = map.entrySet();
for (Map.Entry<Students, String> keyValue : entries) {
Students key = keyValue.getKey();
String value = keyValue.getValue();
System.out.println(key + ":" + value);
}
}
}
输出结果:
(3)TreeMap
TreeMap:键是基于红黑树结构存储的,可以保证键的唯一和排序
代码举例:TreeMap存储字符串并遍历
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class Demo4 {
public static void main(String[] args) {
TreeMap<String, String> map = new TreeMap<>();
// 添加元素
map.put("Bill", "职员");
map.put("Alan", "经理");
map.put("John", "销售");
map.put("Kate", "财务");
// 获取所有的键值对
Set<Map.Entry<String, String>> entries = map.entrySet();
// 遍历
for (Map.Entry<String, String> keyvalue : entries){
String key = keyvalue.getKey();
String value = keyvalue.getValue();
System.out.println(key+":"+value);
}
}
}
输出结果:
通过输出结果来看,TreeMap的输出是有序的。
TreeMap存储自定义对象并遍历:
若是仍然按照上面的写法会报错:ClassCastException: test.Test.src.com.tys.day20.Student cannot be cast to java.lang.Comparable
分析发现这是由于put方法在底层源码中需要向下转型,但是Student类中没有实现Comparable接口重写其中的CompareTo方法导致
解决办法:在Student类中实现Comparable接口并重写CompareTo方法即可,这里的CompareTo方法需要我们自己去重写方法体。
正确代码如下:
这里推荐使用匿名内部类的形式实现Comparator接口重写CompareTo方法
Student类:
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 +
'}';
}
}
这里是测试类:
import java.util.Comparator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class Demo5 {
public static void main(String[] args) {
TreeMap<Student, String> map = new TreeMap<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
// return 0;
int i=o1.getAge()-o2.getAge();
// 判断姓名是否一致
int i2=i==0?o1.getName().compareTo(o2.getName()):i;
return i2;
}
});
// 创建学生对象
Student s1 = new Student("赵云", 18);
Student s2 = new Student("马超", 17);
Student s3 = new Student("黄忠", 21);
Student s4 = new Student("关羽", 19);
Student s5 = new Student("赵云", 18);
// 添加元素
map.put(s1,"厉害");
map.put(s2,"强大");
map.put(s3,"忠心");
map.put(s4,"帅气");
map.put(s5,"厉害");
// 遍历集合
Set<Map.Entry<Student, String>> entries = map.entrySet();
for (Map.Entry<Student, String> keyvalue : entries){
Student key = keyvalue.getKey();
String value = keyvalue.getValue();
System.out.println(key+":"+value);
}
}
}
输出结果:
(4) LinkedHashMap
class LinkedHashMap extends HashMap implements Map{}
HashMap的一个子类,哈希表和链表实现的Map接口,具有可预测的迭代顺序
哈希表保证元素的唯一,保证键的唯一,链表保证是有序的(存储和取出的顺序一致)
代码举例:
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
public class Demo6 {
public static void main(String[] args) {
LinkedHashMap<String, String> map = new LinkedHashMap<>();
// 添加元素到集合
map.put("beijing","国际");
map.put("winter","冬季");
map.put("olympics","奥林匹克");
map.put("sports","运动会");
map.put("beijing","北京");
// 遍历集合
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> keyvalue : entries){
String key = keyvalue.getKey();
String value = keyvalue.getValue();
System.out.println(key+":"+value);
}
}
}
输出结果:
(5) HashMap与HashTable的区别:
1、HashMap与HashTable他们存储的单元都是一个个键值对
2、HashMap中的key和value可以为null值,而HashTable不允许。
3、HashTable是线程安全的,而HashMap是线程不安全的。
3、集合的嵌套遍历
需求:从键盘录入"aababcabcdabcde",获取字符串中每一个字母出现的次数要求结果:a(5)b(4)c(3)d(2)e(1)
代码实现:
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.TreeMap;
/*
需求:从键盘录入"aababcabcdabcde",获取字符串中每一个字母出现的次数要求结果:a(5)b(4)c(3)d(2)e(1)
*/
public class Demo9 {
public static void main(String[] args) {
// String s="aababcabcdabcde";
Scanner sc = new Scanner(System.in);
System.out.println("请输入你要统计的字符串:");
String s = sc.next();
TreeMap<Character, Integer> map = new TreeMap<>();
char[] chars = s.toCharArray();
for (Character ch:chars){
Integer i = map.get(ch);
if(i==null){
map.put(ch,1);
}else{
i++;
map.put(ch,i);
}
}
StringBuffer sb = new StringBuffer();
Set<Map.Entry<Character, Integer>> entries = map.entrySet();
for (Map.Entry<Character, Integer> keyvalue : entries){
Character key = keyvalue.getKey();
Integer value = keyvalue.getValue();
sb.append(key).append("(").append(value).append(")");
}
String s1 = sb.toString();
System.out.println(s1);
}
}
输出结果:
ArrayList嵌套TreeMap
代码举例:
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class Demo8 {
public static void main(String[] args) {
ArrayList<TreeMap<String, String>> list = new ArrayList<>();
TreeMap<String, String> map1 = new TreeMap<>();
// 添加元素
map1.put("邓超", "孙俪");
map1.put("张杰", "谢娜");
map1.put("李承铉", "戚薇");
map1.put("陈小春", "应采儿");
TreeMap<String, String> map2 = new TreeMap<>();
map2.put("刘备","孙尚香");
map2.put("周瑜","小乔");
map2.put("孙策","大乔");
list.add(map1);
list.add(map2);
// 嵌套遍历
for (TreeMap<String, String> maps : list){
Set<Map.Entry<String, String>> entries1 = maps.entrySet();
for (Map.Entry<String, String> keyvalue : entries1){
String key = keyvalue.getKey();
String value = keyvalue.getValue();
System.out.println(key+":"+value);
}
}
}
}
输出结果:
二、Collections工具类
针对集合操作的工具类
1、Collections与Collection的区别:
1、Collection是单列集合的顶层接口,有两大子类List/Set
2、Collections是一个针对于集合操作的工具类,可以对集合进行排序,还有查找(二分查找)
2、Collections中的方法
代码举例:
import java.util.ArrayList;
import java.util.Collections;
public class CollectionsDemo1 {
public static void main(String[] args) {
//创建List集合对象
ArrayList<Integer> list = new ArrayList<>();
//添加元素
list.add(10);
list.add(30);
list.add(20);
list.add(70);
list.add(40);
list.add(50);
System.out.println(list);
// System.out.println(list1);
System.out.println("========================================");
System.out.println("排序前的集合:"+list);
//public static <T> void sort(List<T> list)
Collections.sort(list);
System.out.println("排序后的集合:"+list);
System.out.println("========================================");
//排序后的集合:[10, 20, 30, 40, 50, 70]
//public static <T> binarySearch(List<?> list,T key):二分查找前提是有序
//二分查找,如果元素存在,返回该元素对应索引
System.out.println(Collections.binarySearch(list,70));
System.out.println(Collections.binarySearch(list,700));
//public static <T> T max(Collection<?> coll):查找最大值
System.out.println("最大值为:"+Collections.max(list));
//public static void reverse(List<?> list) 反转
Collections.reverse(list);
System.out.println("反转后的集合:"+list);
//public static void shuffle(List<?> list):随机置换
Collections.shuffle(list);
System.out.println("随机置换后的集合:"+list);
}
}
输出结果:
对于Collections中
static <T> List<T> synchronizedList(List<T> list) 返回由指定列表支持的同步(线程安全)列表。
单独举例:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CollectionsDemo1 {
public static void main(String[] args) {
//创建List集合对象
ArrayList<Integer> list = new ArrayList<>();
//将ArrayList转换成线程安全的集合,返回的集合和原本的是一个存储空间
List<Integer> list1 = Collections.synchronizedList(list);
System.out.println(list.hashCode());
System.out.println(list1.hashCode());
//添加元素
list1.add(10);
list1.add(30);
list1.add(20);
list1.add(70);
list1.add(40);
list1.add(50);
System.out.println(list);
System.out.println(list1);
}
}
输出结果: