> 文章列表 > day20_Map

day20_Map

day20_Map

今日内容

零、 复习昨日
一、作业
二、比较器排序
三、Collections
四、Map
五、HashMap[重点]
六、TreeMap

零、 复习昨日

晨考

一、作业

4 创建一个Teacher类 属性age name salary 创建10Teacher对象 装入TreeSet中通过设置Teacher类 保证age大的在Treeset前面  age相同 salary小的在Treeset前面salary相同 name长度小的在Treeset前面
package com.qf.homework;/*** --- 天道酬勤 ---** @author QiuShiju* @desc*/
public class Teacher implements Comparable<Teacher>{private int age;private String name;private double salary;// set get toString 构造省略..@Overridepublic int compareTo(Teacher o) {// 先判断年龄if (o.getAge() - this.age < 0) {// o-this 倒序return -1;} else if (o.getAge() - this.age > 0) {// this-o正序return 1;} else{ // 如果年龄相同,再判断工资if (this.salary - o.getSalary() > 0) {return 1;} else if (this.salary - o.getSalary() < 0) {return -1;} else { // 如果工资相同,再判断名字长度// 名字长度一致要保留return this.name.length() - o.getName().length() >= 0 ? 1:-1;}}}
}public static void main(String[] args) {TreeSet<Teacher> set = new TreeSet<>( );set.add(new Teacher(18,"张三",4000));set.add(new Teacher(18,"张小三",4000));set.add(new Teacher(18,"张三",3000));set.add(new Teacher(20,"张三",1000));set.add(new Teacher(16,"张三",1000));set.add(new Teacher(19,"张三",5000));set.add(new Teacher(19,"张三",2000));set.add(new Teacher(17,"张三",1000));for (Teacher teacher : set) {System.out.println(teacher );}}

二、比较器排序

TreeSet是会对元素进行排序去重,有两种实现方案

  • 使用空参构造方法创建出的TreeSet,底层使用自然排序,即元素要实现Comparable接口才能实现排序
  • 第二种方案: 可以使用有参构造,在创建TreeSet集合时,传入一个Comparator 比较器,这样存入的元素就会按照该比较器指定的排序方案排序( 不再使用默认的自然排序)

TreeSet(Comparator comparator) 构造一个新的空 TreeSet,它根据指定比较器进行排序。

使用步骤

  • 自定义类实现Comparator 接口
  • 重写compar(T o1,T o2)方法
    • o1 就是之前compareTo方法中的this,即正在存储的元素
    • o2 就是之前compareTo方法中的o,即以前存储过的元素
    • 方法返回值与之前compareTo方法的返回值一样
      • 返回0 去重
      • 返回负数放左边
      • 返回正数放右边
  • 在创建TreeSet时,创建该比较器对象,传入TreeSet的构造方法

改造老师工资题目:

// Teacher类不需要再实现Comparable接口
public class Teacher {private int age;private String name;private double salary;// 正常封装...
}
// 自定义类实现比较器接口
public class MyTeacherComparator implements Comparator<Teacher> {/*** @param o1 就是当前正在存储的,即this* @param o2 就是之前存过的,即o* @return*/@Overridepublic int compare(Teacher o1, Teacher o2) {// System.out.println("o1 -->" + o1  );// System.out.println("o2 -->" + o2  );// 按照年龄升序return o1.getAge() - o2.getAge() > 0 ? 1:-1;}
}
// 测试,在创建TreeSet传入比较器对象public static void main(String[] args) {// 创建TreeSet指定了一个比较器,此时对元素排序会按照我指定的进行TreeSet<Teacher> set = new TreeSet<>(new MyTeacherComparator( ));set.add(new Teacher(18,"张三",4000));set.add(new Teacher(18,"张小三",4000));set.add(new Teacher(18,"张三",3000));set.add(new Teacher(20,"张三",1000));set.add(new Teacher(16,"张三",1000));set.add(new Teacher(19,"张三",5000));set.add(new Teacher(19,"张三",2000));set.add(new Teacher(17,"张三",1000));for (Teacher teacher : set) {System.out.println(teacher );}}

总结: 当无法更改源代码时,只能选择比较器排序

三、Collections

类似于与Arrays,Collections是集合的工具类,方法都是静态的

  • Collections.reverse(List<?> list) 反转
  • Collections.shuffle(List<?> list) 混洗
  • Collections.sort(List<?> list) 排序
    public static void main(String[] args) {ArrayList<Integer> list = new ArrayList<>( );list.add(3);list.add(5);list.add(2);list.add(1);list.add(4);System.out.println(list );System.out.println("-----------------" );// 反转Collections.reverse(list);System.out.println(list );System.out.println("-----------------" );// 排序Collections.sort(list);System.out.println(list );System.out.println("-----------------" );// 混洗(打乱重排)Collections.shuffle(list);System.out.println(list );}

四、Map<K,V>

Map代表双列集合,一次存储一对键值对(K,V)
Map是接口,代表是键映射到值的对象,一个Map不能包含重复的键,值允许重复.每个键最多只能映射到一个值,即可以通过键找到值,但是不能通过值找键.

方法都是非常常见的方法,但是Map是接口无法演示

Map有两个常用实现类

  • HashMap
  • TreeMap

五、HashMap[重点]

HashMap是Map的实现类,现在JDK8及以后底层是由数组+链表+红黑树实现
并允许使用 null 值和 null

HashMap存储的元素是不保证迭代顺序,存储的键不允许重复,值允许重复


除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同


补充: Hashtable是线程安全的map集合,效率低 ; HashMap是线程不安全的,效率高
ConcurrentHashMap 即安全又高效的Map集合

HashMap的容量和扩容: 初始容量16,加载因子0.75 阈值是 16 * 0.75,达到阈值扩容至原来的2倍
ps: 昨天学习的HashSet所有特性,其实就是HashMap的特性,包括去重原理

5.1 方法演示

构造方法

HashMap()
构造一个具有默认初始容量 (16) 和默认加载因子 (0.75) 的空 HashMap。
HashMap(int initialCapacity)
构造一个带指定初始容量和默认加载因子 (0.75) 的空 HashMap。
HashMap(int initialCapacity, float loadFactor)
构造一个带指定初始容量和加载因子的空 HashMap。
HashMap(Map<? extends K,? extends V> m)
构造一个映射关系与指定 Map 相同的新 HashMap。

方法

每个都很重要!!!

    public static void main(String[] args) {// 创建HashMapHashMap<Integer,String> map = new HashMap<>();// 添加元素 V put(K k,V v) ,返回的是此key之前映射的valueString v1 = map.put(21,"A");// 同时存储键和值String v2 = map.put(21,"AA");System.out.println(v1 );System.out.println(v2 );map.put(13,"AA");map.put(24,"B");map.put(44,"D");// 键不能重复,值可以重复// 顺序是不保证System.out.println(map );// 只有根据key获得value,找不到返回nullString value = map.get(22);System.out.println(value );// 判断是否包含指定键System.out.println("是否包含键21 ? "+map.containsKey(21));// 判断是否包含指定值System.out.println("是否包含值A ? "+map.containsValue("A") );// 移除,根据键移除整个键值对,返回valueString remove = map.remove(21);System.out.println("移除键21,返回对应的值: " +remove);System.out.println("移除后键值对后: "+map );// 大小,键值对的个数System.out.println("集合键值对个数: "+map.size() );// 清空map.clear();System.out.println("清空后,集合大小: "+map.size() );// 判断是否为空System.out.println("集合是否为空: " + map.isEmpty( ));}

5.2 迭代/遍历

Map 接口提供三种collection 视图,允许以键集、值集或键-值映射关系集的形式查看某个映射的内容

  • Set keySet() 键集,返回一个Set集合,其中只有键
  • Collection values() 值集,返回一个Collection集合,其中只有值
  • Set<Map.Entry<K,V>> entrySet() 键值映射集,返回一个Set集合,其中放着key-value对象

5.2.1 键集

 		// 键集,返回一个集合,只有键Set<Integer> keySet = map.keySet();Iterator<Integer> iterator = keySet.iterator( );while (iterator.hasNext( )) {System.out.println(iterator.next() );}System.out.println("------------" );for (Integer key : keySet) {System.out.println(key );}

5.2.2 值集

       // 值集,返回一个集合,只有值Collection<String> values = map.values();Iterator<String> iterator1 = values.iterator( );while (iterator1.hasNext()) {System.out.println(iterator1.next() );}System.out.println("-------------" );for(String value : values) {System.out.println(value );}

5.2.3 键值映射集 [非常重要]

Entry是Map接口中的内部接口,代表是一个键值对,即包含键和值.
且该Entry接口中提供了关于操作单个键,值的方法

  • K getKey()
  • V getValue()
        // 调用entrySet方法返回Set集合,集合中存储的是Map.Entry// Entry是Map的内部接口,代表的是一项(键值对)Set<Map.Entry<Integer,String>> entrySet =  map.entrySet();Iterator<Map.Entry<Integer,String>> iterator2 = entrySet.iterator();while (iterator2.hasNext()) {// 从迭代器取出来的是Entry对象Map.Entry<Integer,String> entry = iterator2.next();// 通过entry对象可以获得键和值Integer key = entry.getKey( );String value = entry.getValue( );System.out.println(key +"-->" +value);}System.out.println("===============" );// 增强forfor(Map.Entry<Integer,String> entry : entrySet) {Integer key = entry.getKey( );String value = entry.getValue( );System.out.println(key +"-->" +value);}

day20_Map

5.3 去重原理

HashMap的键去重其实就是昨天讲的HashSet的去重,因为HashSet底层就是HashMap

  1. 在创建HashSet时,其实在底层创建了HashMap

    day20_Map

  2. 在向set中添加元素时,其实是向map的key上添加

    day20_Map

所以HashMap的键的去重原理就是

  • 向键存储数据时,先调用键的hashcode()方法
  • 如果hashcode值不一样则直接存储
  • 如果hashcode值一样,再调用元素的equals()方法
  • 如果equals方法返回false,则存储
  • 如果equals方法返回true,则不存储

5.4 HashMap的应用

场景一: 适合有关联映射的场景

  • 电话 110 --> 警察
  • 行政区划 0371 --> 河南
  • 简称 豫 --> 河南
  • 身份 41011111 —> zhangsan
  public static void main(String[] args) {HashMap<String, String> m = new HashMap<>( );m.put("河南","豫");m.put("河北","冀");m.put("山西","晋");m.put("陕西","陕");m.put("安徽","皖");}
/*** 练习: 映射关系* key  --> value* java --> 5个学生* html --> 5个学生*/
HashMap<String, List<Student>> map = new HashMap<>( );ArrayList<Student> list = new ArrayList<>( );
list.add(new Student(18,"张1"));
list.add(new Student(18,"张2"));
list.add(new Student(18,"张3"));
map.put("java",list);ArrayList<Student> list2 = new ArrayList<>( );
list2.add(new Student(19,"李1"));
list2.add(new Student(19,"李2"));
list2.add(new Student(19,"李3"));
map.put("html",list2);Set<Map.Entry<String, List<Student>>> entrySet = map.entrySet( );
for (Map.Entry<String,List<Student>> entry:entrySet) {String key = entry.getKey( );List<Student> value = entry.getValue( );for (Student student : value) {System.out.println("班级:"+key +"学生"+student);}
}

设计方法,传入字符串,输出该字符串中每个字符出现的次数,使用HashMap实现
例如: “abcHelloabcWorld”,输出 a出现2次,b出现2次,l出现3次,H出现1次

    // a --> 2// b --> 2public static void cishu(String str) {// key存字符,value存次数HashMap<String, Integer> map = new HashMap<>( );// 方案1: split("")// 方案2: toCharArray()// 方案3: 遍历字符串char[] chars = str.toCharArray( );for (char c : chars) {String s = String.valueOf(c);// 判断map是否有改字符boolean b = map.containsKey(s);if (!b) {// 不包含,则第一次存入map.put(s,1);}else{ // 之前有过该字符,次数+1Integer count = map.get(s);count++;map.put(s,count);}}Set<Map.Entry<String, Integer>> entrySet = map.entrySet( );for (Map.Entry<String, Integer> entry : entrySet) {String key = entry.getKey( );Integer value = entry.getValue( );System.out.println("字符:"+key+"-->"+value+"次" );}}

六、LinkedHashMap

LinkedHashMap既有HashMap 键不允许的特点,又有Linked链表结构的特点即会有顺序

 public static void main(String[] args) {LinkedHashMap<String, String> map = new LinkedHashMap<>( );map.put("甘肃","甘");map.put("江西","赣");map.put("江西","赣");map.put("湖南","湘");map.put("湖南","湘");map.put("湖北","鄂");Set<Map.Entry<String, String>> set = map.entrySet( );for (Map.Entry<String, String> entry : set) {String key = entry.getKey( );String value = entry.getValue( );System.out.println(key+"-->"+value );}}

七、TreeMap

TreeMap底层是红黑树(平衡二叉树的一种)
同样式存储键值对,键不允许重复且还会排序
默认是根据键元素的自然顺序排序或者,根据创建TreeMap时指定的Comparator比较器来排序

7.1 方法演示

// 自行演示,创建,放入,,大小,获得,遍历// 演示特殊方法, 第一个,最后一个,> ,< ,截取一部分

7.2 TreeMap排序去重原理

昨天学习的TreeSet的底层其实就是TreeMap

  • 创建TreeSet时,创建TreeMap

    day20_Map

  • 向set集合添加元素时,其实是向TreeMap的键添加元素

    day20_Map

即TreeMap的排序去重原理是什么?其实如果自然排序就是compareTo(),如果是比较器排序就是compar()方法

  • 方法返回0 去重
  • 方法返回负数 放在树左侧
  • 方法返回正数 放在树的右侧

八、总结

集合中最最重要的: ArrayList,HashMap ,关于它俩所有知识必须会

LinkedList,TreeSet,TreeMap 属性了解即可,主要记住特性以及原理

了解两个 LinkedHashSet LinkedHashMap