目录

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.双列集合体系结构

Java 里面 键值对数组 java键值对集合用什么表示_python

双列集合的特点:

  • 单列集合一次添加一个元素,双列集合一次添加一对(键值对)
  • 键不能重复,值可以重复
  • 键和值一一对应,每一个键只能找到自己的值
  • 键+值我们称为键值对,或者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);