Map 双列集合语法详解
一、双列集合的特点
| 单列集合 |
|---|
| “服部平次” |
| “黑羽快斗” |
| “江户川柯南” |
| 双列集合 | |
|---|---|
| **key** | **value** |
| "江户川柯南" | "毛利兰" |
| "黑羽快斗" | "中森青子" |
| "服部平次" | "荷叶" |
- 双列集合一次需要存一对数据,分别为键和值
- 键不可以重复,值可以重复
- 键和值是一一对应的,每一个键只能找到自己对应的值
- 键 + 值 这个整体我们称之为"键值对"或者"键值对对象",在Java中叫做"Entry对象"
二、Map的常见API
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() 集合的长度,也就是集合中键值对的个数
publicclassMapDemo1{publicstaticvoidmain(String[]args){Map<String,String>m=newHashMap<>();/** * 添加元素 * put方法的细节: * 在添加数据的时候,如果键不存在,那么直接把键值对对象添加到map集合当中,方法返回null * 在添加数据的时候,如果键是存在的,那么会把原有的键值对对象覆盖,会把覆盖的值进行返回 */Stringvalue1=m.put("江户川柯南","毛利兰");System.out.println(value1);// nullm.put("黑羽快斗","中森青子");m.put("服部平次","荷叶");m.put("源源","甜甜");Stringvalue=m.put("江户川柯南","灰原哀");System.out.println(value);// 毛利兰/** * 删除元素: * remove:根据键删除键值对,返回删除的值 * clear:清空集合 */Stringresult=m.remove("服部平次");System.out.println(result);// 荷叶m.clear();/** * 判断是否包含: * containsKey:根据键判断元素是否存在 * containsValue:根据值判断元素是否存在 */booleankeyResult=m.containsKey("源源");booleankeyResult1=m.containsKey("源源6");System.out.println(keyResult);//trueSystem.out.println(keyResult1);//falsebooleankeyResult2=m.containsValue("甜甜");System.out.println(keyResult2);//true/** * 判断是否为空 */m.clear();booleanresult=m.isEmpty();System.out.println(result);// true/** * 获取集合的长度 * size */intsize=m.size();System.out.println(size);System.out.println(m);}}
三、Map的遍历方式
- 键找值
- 键值对
- Lambda表达式
publicstaticvoidmain(String[]args){Map<String,String>map=newHashMap<>();map.put("江户川柯南","毛利兰");map.put("黑羽快斗","中森青子");map.put("服部平次","荷叶");map.put("源源","甜甜");// 1.键找值Set<String>keys=map.keySet();//------------------------------------------//增强forfor(Stringkey:keys){// 利用map集合中的键获取对应的值 getStringvalue=map.get(key);System.out.println(key+"="+value);}//------------------------------------------//迭代器Iterator<String>it=keys.iterator();while(it.hasNext()){Stringkey=it.next();Stringvalue=map.get(key);System.out.println(key+"="+value);}//------------------------------------------//Lambda表达式keys.forEach(key->{Stringvalue=map.get(key);System.out.println(key+"="+value);});//------------------------------------------//2.键值对//Set无序集合,不可重复Set<Map.Entry<String,String>>entries=map.entrySet();for(Map.Entry<String,String>entry:entries){Stringkey=entry.getKey();Stringvalue=entry.getValue();System.out.println(key+"="+value);}Iterator<Map.Entry<String,String>>it=entries.iterator();while(it.hasNext()){Map.Entry<String,String>next=it.next();Stringkey=next.getKey();Stringvalue=next.getValue();System.out.println(key+"="+value);}entries.forEach(entry->{Stringkey=entry.getKey();Stringvalue=entry.getValue();System.out.println(key+"="+value);});//3.Lambda表达式map.forEach(newBiConsumer<String,String>(){@Overridepublicvoidaccept(Stringkey,Stringvalue){System.out.println(key+"="+value);}});map.forEach((key,value)->System.out.println(key+"="+value));}
四、HashMap
1. HashMap的特点
- HashMap是Map里面的一个实现类
- 没有额外需要学习的特有方法,直接使用Map里面的方法就可以了
- 特点都是由键决定的 : 无序、不重复、无索引
- HashMap和HashSet底层原理是一模一样的,都是哈希表结构
✏️ HashMap
- HashMap底层是哈希表结构的,插入键值对对象时,会用键计算哈希值,放入指定位置,与值无关
- 依赖hashCode方法和equals方法保证键的唯一
- 如果键存储的是自定义对象,需要重写hashCode和equals方法
- 如果值存储自定义对象,不需要重写hashCode和equals方法
publicclassStudent{privateStringname;privateIntegerage;publicStudent(){}publicStudent(Stringname,Integerage){this.name=name;this.age=age;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicIntegergetAge(){returnage;}publicvoidsetAge(Integerage){this.age=age;}@OverridepublicStringtoString(){return"Student{"+"name='"+name+'\''+", age="+age+'}';}@Overridepublicbooleanequals(Objecto){if(o==null||getClass()!=o.getClass())returnfalse;Studentstudent=(Student)o;returnObjects.equals(name,student.name)&&Objects.equals(age,student.age);}@OverridepublicinthashCode(){returnObjects.hash(name,age);}}publicclassHashMapDemo1{publicstaticvoidmain(String[]args){HashMap<Student,String>hm=newHashMap<>();Students1=newStudent("源源",19);Students2=newStudent("甜甜",18);Students3=newStudent("柯南",17);hm.put(s1,"安徽");hm.put(s2,"浙江");hm.put(s3,"上海");hm.put(s3,"东京");// 遍历集合Set<Student>students=hm.keySet();students.forEach(s->{Stringvalue=hm.get(s);System.out.println(s+value);});System.out.println("------------------------");Set<Map.Entry<Student,String>>entries=hm.entrySet();entries.forEach(entry->{Studentkey=entry.getKey();Stringvalue=entry.getValue();System.out.println(key+value);});System.out.println("--------------------------");hm.forEach((key,value)->System.out.println(key+value));}}
五、LinkedHashMap
1. LinkedHashMap的特点
- 由键决定 : 有序、不重复、无索引
- 这里的有序指的是保证存储和取出的元素顺序一致
- 原理 : 底层数据结构是依然哈希表,只是每个键值对元素又额外多了一个双向链表的机制记录存储的顺序
publicstaticvoidmain(String[]args){LinkedHashMap<String,Integer>lhm=newLinkedHashMap<>();lhm.put("源源",1126);lhm.put("甜甜",0101);lhm.put("柯南",9568);lhm.put("灰原哀",2237);System.out.println(lhm);// {源源=1126, 甜甜=65, 柯南=9568, 灰原哀=2237} 顺序一致!!!}
六、TreeMap
1. TreeMap的特点 :
- TreeMap和TreeSet底层原理一样,都是红黑树结构的
- 由键决定特性 : 不重复、无索引、可排序
- 可排序 : 对键进行排序
- 注意 : 默认按照键的从小到大进行排序,也可以自己规定键的排序规则
2. 两种排序规则的书写
- 实现Comparable接口,指定比较规则
- 创建集合时传递Comparator比较器对象,指定比较规则
🚨 注意
两中方式都书写的话,按照第二种Comparator比较器对象为准
❗ 提醒
Java中已经实现了Integer和Double类型的Comparable接口,默认按照升序排序的String类型按照字母在ASCII码表中对应的数字升序进行排列
例子 :
键 : 学生对象 值 : 所在城市 按照学生年龄升序排列,年龄一样按照姓名的字母排序,同姓名年龄视为同一个人publicclassStudentimplementsComparable<Student>{privateStringname;privateIntegerage;publicStudent(){}publicStudent(Stringname,Integerage){this.name=name;this.age=age;}publicIntegergetAge(){returnage;}publicvoidsetAge(Integerage){this.age=age;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}@OverridepublicStringtoString(){return"Student{"+"name='"+name+'\''+", age="+age+'}';}@OverridepublicintcompareTo(Studento){inti=this.getAge()-o.getAge();i=i==0?this.getName().compareTo(o.getName()):i;returni;}}publicclassTreeMapDemo1{publicstaticvoidmain(String[]args){TreeMap<Student,String>tm=newTreeMap<>();Students1=newStudent("源源",19);Students2=newStudent("甜甜",18);Students3=newStudent("柯南",17);tm.put(s1,"安徽");tm.put(s2,"浙江");tm.put(s3,"东京");System.out.println(tm);// {Student{name='柯南', age=17}=东京, Student{name='甜甜', age=18}=浙江, Student{name='源源', age=19}=安徽}}}
七、总结
| 集合 | 底层结构 | 键特点 | 使用场景 |
|---|---|---|---|
| HashMap | 哈希表 | 无序、不重复 | 大多数日常存储查询(首选) |
| LinkedHashMap | 哈希表 + 双向链表 | 存取有序、不重复 | 需要保证存入顺序 |
| TreeMap | 红黑树 | 按键排序、不重复 | 需要对键自动排序 |
Map 为什么键不能重复?
底层添加元素时,先判断哈希值,再 equals 比较,相同则覆盖 value。
为什么自定义对象做 key 必须重写 hashCode 和 equals?
Object 原生 hashCode 根据内存地址,内容相同不同对象哈希值不一样,会判定为两个 key。
TreeMap 键对象如果不实现比较规则会报错:
ClassCastException