目录
1.1.3.双列集合体系结构
1.1.3.1.Map的成员方法
1.1.3.2.Map集合的遍历方式
1.1.3.3.HashMap
1.1.3.4.LinkedHashMap
1.1.3.5.TreeMap
1.1.3.双列集合体系结构
双列集合的特点:
- 单列集合一次添加一个元素,双列集合一次添加一对(键值对)
- 键不能重复,值可以重复
- 键和值一一对应,每一个键只能找到自己的值
- 键+值我们称为键值对,或者Entry对象
Map是双列集合的顶层接口
1.1.3.1.Map的成员方法
//创建Map的实现类对象
//注意不能new Map,因为Map是接口,不能直接创建对象,只能创建其实现类HashMap的对象。
Map<String,String> m = new HashMap<>();
//put添加元素/覆盖元素
//如果键不存在,直接把键值对添加到map集合当中,put方法返回null
//如果键存在,那么会覆盖值,put方法返回被覆盖的值
String result = m.put("a","11");//null
String result = m.put("a","22");//11
//remove
//会返回被删除的键对应的值
String result = m.remove("a");//11
//clear
m.clear//{}
//containsKey判断键是否存在
boolean result = m.containsKey("a");//true
boolean result = m.containsKey("aa");//false
//containsKey判断值是否存在
boolean result = m.containsKey("11");//true
//isEmpty是否为空,空则返回true
//size
int size = m.size//2
1.1.3.2.Map集合的遍历方式
- 通过键找值遍历。调用map中的keySet方法,将所有键转为Set集合,然后再调用map中的get方法获取对应的值即可
- 通过键值对遍历。调用map中的entrySet方法,把键值对对象整体entry转为Set集合。对这个集合我们就可以采用增强for循环/Lambda表达式/迭代器遍历。
- 注意:此时 entries就是Set集合中的一个个数据,因此可以调用Set集合中的很多方法
Map<String,String> map = new HashMap<>();
map.put("a","11");
map.put("b","22");
//调用map中的entrySet方法,把键值对对象整体entry转为Set集合
Set<Map.Entry<String, String>> entries = map.entrySet();
//采用增强for循环。根据遍历到的entry对象,调用getKey和getValue方法分别获取其中的键或者值
for (Map.Entry<String, String> entry : entries) {
String value = entry.getValue();
System.out.println(value);
//采用迭代器遍历
Iterator<Map.Entry<String, String>> it = entries.iterator();
while(it.hasNext()){
String value = it.next().getValue();
System.out.println(value);
}
//采用forEach
entries.forEach(new Consumer<Map.Entry<String, String>>() {
@Override
public void accept(Map.Entry<String, String> stringStringEntry) {
System.out.println(stringStringEntry.getValue());
}
});
//Lambda表达式
entries.forEach(stringStringEntry ->System.out.println(stringStringEntry.getValue()));
- 通过Map集合当中自带的forEach遍历。
//采用forEach
map.forEach(new BiConsumer<String, String>() {
@Override
public void accept(String s, String s2) {
System.out.println(s);
}
});
//Lambda表达式
map.forEach((s, s2) ->System.out.println(s));
forEach底层原理:
//表示forEach方法中要传递BiConsumer这个接口的实现类的对象,对象名aciton
default void forEach(BiConsumer<? super K, ? super V> action) {
Objects.requireNonNull(action);
//实际上是将map调用entrySet()方法转换为集合,然后对这个集合使用增强for遍历
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch (IllegalStateException ise) {
throw new ConcurrentModificationException(ise);
}
action.accept(k, v);
}
}
1.1.3.3.HashMap
特点都由键所决定,和值没有关系
特点:无序、不重复、无索引。
底层数据结构:哈希表(数组+链表+红黑树)。依赖hashCode方法和equals方法确保键的唯一(与值没有任何关系)
- 确认元素存储位置:hashCode计算哈希值确认存储位置,同HashSet
- 确认元素是否存储:equals方法,确认键的属性(与值没有任何关系),如果为null则存储;如果不为null(表示有元素)则覆盖
- 如果键存储的是自定义对象*务必记得重写hashCode和equals方法,如果不重写,那么每添加一个就无脑存一个。因为底层是根据键的属性来确认元素是否存储的,如果对象的属性值相同,那么就不存,此时会进行值的覆盖;如果对象的属性值不同,那么就存。如果值存储自定义对象,则不需要重写
HashMap应用:用HashMap进行统计
例如,假设某个班级有80个学生,现在要组织秋游活动,有四个景点ABCD,每个学生只能选择一个景点,统计哪个景点得票数最多。现在用随机数模拟每个同学的投票,把投票结果存储在数组中,如[A,D,C,C,A,B,D,B,A,C,C·······]。请根据这个投票结果统计各个景点的得票数。
思路:
- 利用HashMap键值对的思想进行统计,键表示景点,值表示得票数
- 利用put覆盖的思想,如果景点(键)重复,那么该景点的得票数(值)就加一,用覆盖思想进行累加;如果不存在,那么直接按照(景点,1)这个键值对存储。
- 遍历HashMap的值,通过和初始变量MAX=0相比较得出最大值;
- 根据最大值,再遍历一次HashMap的键,找出最大值对应的景点。
1.1.3.4.LinkedHashMap
特点:有序、不重复、无索引。
- 有序:可以保证存和取的顺序是一致的。
底层数据结构:同LinkedHashSet。
1.1.3.5.TreeMap
特点:无序、不重复、无索引。
底层数据结构:同TreeSet。
可以对键进行排序。
TreeMap应用1:键是整数id,值是字符串表示商品名称,要求按照id的升序和降序进行排列。
- 思路:给TreeMap形参中传入一个Comparator的实现类的对象,重写排序规则。
TreeMap应用2:键是自定义对象学生,值是籍贯。学生属性有年龄、姓名。要求按照学生的年龄进行排序。
- 思路1:给TreeMap形参中传入一个Comparator的实现类的对象,重写排序规则。
- 思路2:在要定制排序的对象的JavaBean类中实现Comparable接口,注意该接口是泛型接口(泛型中输入什么类型,重写comparaTo方法时就用的是什么类型),然后重写接口中的方法,在方法中定制排序规则即可。
//在Sudent这个JavaBean类中
public class Student implements Comparable<Student> {
private int age;
private String name;
@Override
public int compareTo(Student o) {
int i = this.getAge() - o.getAge();
i = i == 0? this.getName().compareTo(o.getName()):i;
return i;
}
//注意:对于TreeMap和TreeSet,底层数据结构是红黑树,存与不存的关键看大小排序。如果存的是自定义的对象,那么必须为对象定制排序规则,否则会报错;而且在定制排序规则的时候,必须对全部属性都进行比较,比如学生这个对象有两个属性年龄和姓名,如果只对年龄定制排序规则,那么当两个对象年龄相同但是姓名不同,树结构也会认为这两个对象属于同一个元素,不让添加到集合当中。正确的做法是先对年龄进行定制排序,再对姓名进行定制排序,如果年龄相同、姓名也相同,那么必定就是同一个人,因此就不能添加到集合当中。
TreeMap应用3:统计一个字符串当中字符出现的次数,并按照次数由大到小输出键值对。
//我的代码。按照字符的大小输出键值对。如何按照值大小进行排序?
String str = "odnasdnasdbnofejnw";
TreeMap<Character,Integer> tm = new TreeMap<Character, Integer>(new Comparator<Character>() {
@Override
public int compare(Character o1, Character o2) {
int j = o1 - o2;
return j;
}
});
for (int i = 0; i < str.length(); i++) {
//如果添加的时候等于null,相当于这个键不存在,那么直接存储
if(tm.put(str.charAt(i),tm.get(str.charAt(i)))== null){
tm.put(str.charAt(i),1);
}else{//如果不是null,说明已经存在该键,那么取出这个键的值,使其加一再赋值回去
int count = tm.get(str.charAt(i));
count++;
tm.put(str.charAt(i),count);
}
}
System.out.println(tm);