双列集合
特点:
- 双列集合一次需要存一对数据,分别为键和值
- 键不能重复,值可以重复
- 键和值是一一对应的,每个键只能找到自己对应的值
- 键+值这个整体 称为:键值对 键值对对象 Entry对象
Map集合的常用方法
java
public class MapDemo1 {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
//调用put添加元素 put:添加|覆盖
map.put("王老吉","凉茶");
map.put("加多宝","凉茶");
map.put("脉动","功能型饮料");
map.put("可口可乐","碳酸饮料");
map.put("特仑苏","纯牛奶");
String ValueRet = map.put("特仑苏","纯牛马"); //输出
System.out.println(ValueRet); //纯牛奶
//删除
String Ret = map.remove("加多宝");
System.out.println(Ret); //凉茶
System.out.println(map);
//{脉动=功能型饮料, 特仑苏=纯牛马, 王老吉=凉茶, 可口可乐=碳酸饮料}
//获取集合长度
int length = map.size();
System.out.println(length); //4
//查找指定的键是否存在
boolean a = map.containsKey("可口可乐");
System.out.println(a); //true
boolean b = map.containsKey("雪碧");
System.out.println(b); //false
//查找指定的值是否存在
boolean c = map.containsValue("纯牛马");
System.out.println(c); //true
boolean d = map.containsValue("QQ糖");
System.out.println(d); //false
//判断集合是否为空
boolean e = map.isEmpty();
System.out.println(e); //false
}
}
Map集合的遍历方式
1.键找值
java
public class MapDemo2 {
public static void main(String[] args) {
//创建HashMap集合
Map<String,String> map = new HashMap<>();
//添加数据
map.put("tom","汤姆");
map.put("peter","彼得");
map.put("jack","杰克");
map.put("smith","史密斯");
//Map集合的第一种遍历方式 键找值
// 调用 map.keySet() 获取所有的键
Set<String> keys = map.keySet();
//遍历单列集合 得到每一个键
System.out.println("=============迭代器=============");
Iterator<String> it = keys.iterator(); //获取单列集合迭代器
while (it.hasNext()){
String s = it.next();
System.out.println("key="+s+"\tvalue="+map.get(s)); //map.get(s):通过传入s(键)返回对应的值
}
System.out.println("=============增强for===========");
for (String key : keys) {
String value = map.get(key);
System.out.println("键="+key+"\t值="+value);
}
System.out.println("=============lambda===========");
keys.forEach( s->System.out.println(s+"="+map.get(s)));
}
}
2.键值对
java
public class MapDemo3 {
public static void main(String[] args) {
//Map集合的第二种遍历方式 键值对
Map<String,String> map = new HashMap<>();
map.put("tom","汤姆");
map.put("peter","彼得");
map.put("jack","杰克");
map.put("smith","史密斯");
//通过一个方法 map.entrySet() 获取所有的键值对对象,返回一个set集合
Set<Map.Entry<String, String>> entries = map.entrySet();
//遍历entries这个集合,去得到里面每个键值对对象
for (Map.Entry<String, String> entry : entries) {
//利用entry调用get方法获取键和值
String key = entry.getKey();
String value = entry.getValue();
System.out.println("键="+key+" 值="+value);
}
Iterator<Map.Entry<String, String>> it = entries.iterator();
while (it.hasNext()){
Map.Entry<String, String> s = it.next();
System.out.println(s.getKey()+"="+s.getValue());
}
System.out.println("===========lambda===========");
entries.forEach(s -> System.out.println(s.getKey()+"="+s.getValue()));
}
}
3.lambda表达式
java
public class MapDemo4 {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put("tom","汤姆");
map.put("peter","彼得");
map.put("jack","杰克");
map.put("smith","史密斯");
//利用lambda表达式进行遍历 map.forEach
map.forEach((String key, String value) -> System.out.println(key+"="+value));
}
}
HashMap
- 是Map里面的一个实现类
- 没有额外需要学习的特有方法,直接使用Map里面的方法就可以了
- 特点都是由键决定的:无序 不重复 无索引
- HashMap跟HashSet底层原理是一模一样的,都是哈希表结构
总结
- HashMap底层是哈希表结构的
- 依赖hashCode方法和equals方法保证键的唯一
- 如果键存储的是自定义对象,需要重写hashCode和equals方法
- 如果值存储自定义对象,不需要重写hashCode和equals方法
HashMap测试题
- 创建一个HashMap集合,键是学生对象(Student),值是籍贯(String)
存储三个键值对元素,并遍历要求:同姓名,同年龄认为是同一个学生
java
public class HashMapDemo1 {
public static void main(String[] args) {
Map<Student3, String> map = new HashMap<>();
Student3 s1 = new Student3("zhangsan", 18);
Student3 s2 = new Student3("lisi", 19);
Student3 s3 = new Student3("wangwu", 17);
Student3 s4 = new Student3("zhangsan", 18);
map.put(s1,"娄底");
map.put(s2,"上海");
map.put(s3,"长沙");
map.put(s4,"昆明");
Set<Student3> s = map.keySet(); //获取键的单列集合
Iterator<Student3> it = s.iterator(); //获取单列集合的迭代器
while(it.hasNext()){
Student3 st = it.next();
System.out.println(st+"="+map.get(st)); // map.get(st):通过键找值
}
}
}
class Student3{
private String name;
private int age;
public Student3(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 "Student3{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student3 student3 = (Student3) o;
return age == student3.age && Objects.equals(name, student3.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
- 某个班级80名学生,现在需要组成秋游活动,班长提供了四个景点依次是 (A、B、C、D),
每个学生只能选择一个景点,请统计出最终哪个景点想去的人数最多。
java
public class HashMapDemo2 {
public static void main(String[] args) {
//定义一个数组, 存储四个景点
String arr[] = {"A","B","C","D"};
//利用随机数模拟80个学生的投票 并把投票的结果存储起来
ArrayList<String> list = new ArrayList<>();
Random r = new Random();
for (int i = 0; i < 80; i++) {
int index = r.nextInt(arr.length); //随机范围为0-3
list.add(arr[index]); //将投票景点存入list集合中
}
HashMap<String, Integer> hm = new HashMap<>();
for (String name : list) {
//判断当前的景点在map当中是否存在
if (hm.containsKey(name)){
//存在
//先获取当前景点已经被投票的次数
int count = hm.get(name);
//表示当前景点又被添加了一次
count++;
//把新的次数再次添加到集合中
hm.put(name,count);
}else {
//不存在
hm.put(name,1);
}
}
System.out.println(hm);
//求最大值
int max = 0;
Set<Map.Entry<String, Integer>> entries = hm.entrySet();
for (Map.Entry<String, Integer> entry : entries) {
int count = entry.getValue(); //如果
if (count > max){
max = count;
}
}
System.out.println("最大值是"+max);
//判断哪个景点和最大值的次数一样,如果一样则打印出来
for (Map.Entry<String, Integer> entry : entries) {
if (entry.getValue() == max){
System.out.println("被选择最多次数的景点是"+entry.getKey());
}
}
}
}
LinkedHashMap
由键决定:有序 不重复 无索引
有序指的是保证存储和取出的元素顺序一致
底层:底层数据结构依然是哈希表,只是每个键值对元素又额外的多了一个双向链表的机制记录存储的顺序
java
public class LinkedHashMap1 {
public static void main(String[] args) {
/*
由键决定:有序 不重复 无索引
有序指的是保证存储和取出的元素顺序一致
底层:底层数据结构依然是哈希表,
只是每个键值对元素又额外的多了一个双向链表的机制记录存储的顺序
*/
//创建集合
Map<String,Integer> lhm = new LinkedHashMap<>();
//添加元素
lhm.put("tom",11);
lhm.put("tom",111);
lhm.put("jack",12);
lhm.put("peter",13);
System.out.println(lhm); //打印集合:有序,不重复
//{tom=111, jack=12, peter=13} tom=111: put方法同样有两个功能 添加/覆盖
}
}
TreeMap
TreeMap跟TreeSet底层原理一样,都是红黑树结构的
由键决定特性: 不重复 无索引 可排序
可排序:对键进行排序
注意:默认按照键的从小到大进行排序,也可以自己规定键的排序规则
两种排序规则
实现Comparable接口,指定比较规则
java
public class TreeMapDemo1 {
public static void main(String[] args) {
/*
需求1:
整数表示id键:
值:字符串表示商品名称
要求:按照id的升序排列、按照id的降序排列
*/
Map<Integer,String> tm = new TreeMap<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
//o1:表示当前要添加的元素
//o2:表示已经在红黑树中存在的元素
//return o1 - o2; 升序
return o2 - o1;
}
});
tm.put(1,"QQ糖");
tm.put(3,"AD钙");
tm.put(2,"旺仔牛奶");
tm.put(2,"忘崽牛奶");
System.out.println(tm); //按照键的大小排序
//输出:{3=AD钙, 2=忘崽牛奶, 1=QQ糖}
}
}
创建集合时传递Comparator比较器对象,指定比较规则
java
import java.util.*;
/**
* @author hyk~
*/
public class TreeMapDemo2 {
public static void main(String[] args) {
/*
需求2:
键:学生对象
值:籍贯
要求:按照学生年龄的升序排列,年龄一样按照姓名的字母排列,同姓名年龄视为同一个人。
*/
Student5 s1 = new Student5("jack",10);
Student5 s2 = new Student5("tom",15);
Student5 s3 = new Student5("peter",14);
Student5 s4 = new Student5("jack",10);
TreeMap<Student5, String> tm = new TreeMap<>();
tm.put(s1,"长沙");
tm.put(s2,"怀化");
tm.put(s3,"娄底");
tm.put(s4,"上海");
for (Map.Entry<Student5, String> entry : tm.entrySet()) {
Student5 key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "=" + value);
}
}
}
class Student5 implements Comparable<Student5>{
private String name;
private int age;
public Student5(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 "Student5{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student5 student5 = (Student5) o;
return age == student5.age && Objects.equals(name, student5.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public int compareTo(Student5 o) {
/*
this: 表示当前要添加的元素
o: 表示已经在红黑树中存在的元素
如果返回是负数:要添加的元素是小的 存左边
如果返回是正数:要添加的元素是大的 存右边
如果返回是 0 :要添加的元素已经存在,不存入
*/
int num = this.getAge() - o.getAge();
//如果年龄相差为0 则说明年龄一样 按名字排序
num = num == 0 ? this.getName().compareTo(o.getName()) : num;
return num;
}
}
测试题:
java
public class TreeMapDemo3 {
/*
需求:字符串 aababcabcdabcde 请统计字符串中每一个字符出现的次数,
并按照以下格式输出输出结果:
a(5) b(4)c(3) d(2) e(1)
统计思想:利用map集合进行统计
如果没有要求对结果进行排序:默认使用HashMap(效率高)
如果要求对结果进行排序,使用TreeMap(可排序)
*/
public static void main(String[] args) {
//定义字符串
String str = "aababcabcdabcde";
//创建集合
Map<Character,Integer> tm = new TreeMap<>();
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
//charAt() 方法是 String 类的一个成员方法,
// 用于获取字符串中指定位置的字符。
// 它的作用是返回字符串中指定索引位置处的字符。
if (tm.containsKey(c)){
//存在 表示当前字符又出现了一次
int count = tm.get(c); //tm.get(c):返回的是值
count++;
tm.put(c,count);
}else{
//不存在 表示当前字符是第一次出现 直接添加
tm.put(c,1);
}
}
tm.forEach((Character character, Integer integer)-> System.out.print(character +"("+integer+") "));
//a(5) b(4) c(3) d(2) e(1)
}
}