day20_Map
今日内容
零、 复习昨日
一、作业
二、比较器排序
三、Collections
四、Map
五、HashMap[重点]
六、TreeMap
零、 复习昨日
晨考
一、作业
4 创建一个Teacher类 属性age name salary 创建10个Teacher对象 装入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);}
5.3 去重原理
HashMap的
键去重
其实就是昨天讲的HashSet的去重,因为HashSet底层就是HashMap
在创建HashSet时,其实在底层创建了HashMap
在向set中添加元素时,其实是向map的key上添加
所以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
向set集合添加元素时,其实是向TreeMap的键添加元素
即TreeMap的排序去重原理是什么?其实如果自然排序就是compareTo(),如果是比较器排序就是compar()方法
- 方法返回0 去重
- 方法返回负数 放在树左侧
- 方法返回正数 放在树的右侧
八、总结
集合中最最重要的: ArrayList,HashMap ,关于它俩所有知识必须会
LinkedList,TreeSet,TreeMap 属性了解即可,主要记住特性以及原理
了解两个 LinkedHashSet LinkedHashMap