目录
主要内容:
1、Map接口
2、HashMap类
3、LinkedHashMap类
4、TreeMap类
5、HashTable类
6、Collections工具类
一、Map接口:
1.1 概述:
public interface Map<K,V>
将键映射到值的对象。
一个映射不能包含重复的键;【键唯一】
每个键最多只能映射到一个值。
Map和Collection的区别:
Collocation是单列的集合;Map是双列的集合。
Collection集合中List集合元素是不唯一的、Set集合元素是唯一的;Map的键(Key)是唯一的。
Collection集合的数据结构是针对元素有效;Map集合的数据结构值对键有效,跟值无关。
1.2 Map接口的方法:
1、添加:
V put(K key,V value)
将指定的值与此映射中的指定键关联(可选操作)。如果此映射以前包含一个该键的映射关系,则用指定值替换旧值(当且仅当 m.containsKey(k) 返回 true 时,才能说映射 m 包含键 k 的映射关系)。
2、删除:
V remove(Object key):如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
void clear():从此映射中移除所有映射关系(可选操作)。此调用返回后,该映射将为空。
3、长度:
int size()
返回此映射中的键-值映射关系数。如果该映射包含的元素大于 Integer.MAX_VALUE,则返回 Integer.MAX_VALUE。
4、获取功能:
V get(Object key):返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
Set<K> keySet():返回此映射中包含的键的 Set 视图。
Set<Map.Entry<K,V>> entrySet():返回此映射中包含的映射关系的 Set 视图。
Collection<V> values():返回此映射中包含的值的 Collection 视图。
5、判断功能:
boolean isEmpty():如果此映射未包含键-值映射关系,则返回 true。
boolean containsKey(Object key:如果此映射包含指定键的映射关系,则返回 true。
boolean containsValue(Object value):如果此映射将一个或多个键映射到指定值,则返回 true。
public class MapDemo01 { public static void main(String[] args) { // 1、添加 Map<Integer, String> map = new HashMap<Integer, String>(); map.put(1, "zhangqiang"); map.put(2, "wangjian"); map.put(3, null); // 3、长度:int size() System.out.println("初始化的元素个数:" + map.size()); // 2、删除 // map.remove(3); // System.out.println("删除3-null之后的元素个数为:" + map.size()); // map.clear(); // System.out.println("执行clear之后的元素个数为:" + map.size()); // 4、获取功能: String stu = map.get(2); System.out.println("stu:" + stu); Set<Integer> keys = map.keySet(); // 全部的key组成集合 for (Integer key : keys) { System.out.println("key:" + key); } // 遍历map集合中全部的value Set<Map.Entry<Integer, String>> values = map.entrySet(); for (Map.Entry<Integer, String> value : values) { System.out.println("value:" + value.getValue()); } // 获取集合中所有值的集合 Collection<String> values2 = map.values(); // 转换数组遍历或转换成Iterator Iterator<String> iterator = values2.iterator(); while (iterator.hasNext()) { System.out.println("value2:" + iterator.next()); } // 5、判断功能 System.out.println(map.isEmpty()); System.out.println(map.containsKey(4)); System.out.println(map.containsValue("yougen")); } } |
1.3 Map集合的遍历:
Map集合不能直接使用Iterator,因为Map接口中未声明跟Iterator相关的方法。
方式一:使用Map的entrySet()方法
方式二:使用Map的keySet()方法和get(Object key)方法
public class MapDemo02 { public static void main(String[] args) { Map<Integer, String> table = new HashMap<Integer, String>(); table.put(1, "小亮"); table.put(2, "克弘"); table.put(3, "俊杰"); table.put(4, "欧阳");
// keySet()——获取全部key // values()——获取全部value // entrySet()——返回值是Set<Map.Entry<Integer,String>>类型 // 现在要求获取全部的key-value // 1、第一种方式; // print1(table);
// 2、第二种方式: print2(table); }
public static void print2(Map<Integer, String> map) { Set<Integer> keySet = map.keySet(); for (Integer key : keySet) { System.out.println(key + "=" + map.get(key)); } }
public static void print1(Map<Integer, String> map) { Set<Map.Entry<Integer, String>> entrys = map.entrySet(); for (Map.Entry<Integer, String> en : entrys) { System.out.println(en); } } }
|
补充:Entry类的源码分析
Ctrl+Shift+T 找类库中和用户自定义的类或接口
Ctrl+O 在类中查找方法
1、打印Set集合时,可以直接使用集合名称,而不需要遍历。
public static void printMap(Map<Integer, String> map) { // 1、先获得全部的key,再根据key获取value Set<Integer> keySet = map.keySet(); for (Integer key : keySet) { System.out.println(key + "=" + map.get(key)); } System.out.println("--------------------"); // 2、第二种方式: Set<Map.Entry<Integer, String>> entrySet = map.entrySet(); // HashSet类实现Set接口; HashSet extends AbstractSet extends // AbstractCollection;AbstractCollection类中实现了toString方法,打印set集合可以直接使用集合名称 for (Map.Entry<Integer, String> en : entrySet) { System.out.println(en); } System.out.println(entrySet); // toString() } |
2、Map接口中的Entry接口是由HashMap中内部类Entry实现的:
1.4 案例分析(自定义类作为Map的键):
HashMap集合键是Student、值是String的案例:
注意:必须要重写Student类(用户自定义类)的equals方法和hashCode方法。
public class Student { private String name; private Integer age;
@Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Student [name="); builder.append(name); builder.append(", age="); builder.append(age); builder.append("]"); return builder.toString(); }
@Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((age == null) ? 0 : age.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; }
@Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Student other = (Student) obj; if (age == null) { if (other.age != null) return false; } else if (!age.equals(other.age)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; }
public Student() { super(); }
public Student(String name, Integer age) { super(); this.name = name; this.age = age; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; } }
|
public class MapDemo02 { // 以Student为键,String为值的Map集合 public static void main(String[] args) { Map<Student, String> work = new HashMap<Student, String>(); // 直接向下复制整行:Ctrl+Down; // 修改打开preferences,搜索keys:copy-lines work.put(new Student("有根", 24), "深圳"); work.put(new Student("惠明", 23), "北京"); work.put(new Student("惠明", 23), "北京"); work.put(new Student("王健", 23), "上海"); // Map集合也可以直接打印 System.out.println(work); // toString() } } |
1.5 LinkedHashMap类:
Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。此实现与 HashMap 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序)。注意,如果在映射中重新插入 键,则插入顺序不受影响。
public class MapDemo03 { // 以Student为键,String为值的Map集合,用LinkedHashMap实现 public static void main(String[] args) { Map<Student, String> work = new LinkedHashMap<Student, String>(); work.put(new Student("王健", 23), "上海"); work.put(new Student("惠明", 23), "北京"); work.put(new Student("有根", 24), "深圳");
work.put(new Student("惠明", 23), "广州"); System.out.println(work); // toString() } } |
1.6 TreeMap类:
TreeMap集合键是Person、值是String的案例
1、Person类实现Comparable接口保证顺序:
public class Person implements Comparable<Person> { private String name; private Integer age;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public Person(String name, Integer age) { super(); this.name = name; this.age = age; }
public Person() { super(); // TODO Auto-generated constructor stub }
@Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Person [name="); builder.append(name); builder.append(", age="); builder.append(age); builder.append("]"); return builder.toString(); }
@Override public int compareTo(Person person) { // this VS person // 假设先比较年龄(升序),如果年龄相同则比较姓名 int num = this.getAge() - person.getAge(); int num2 = this.getName().compareTo(person.getName()); return num == 0 ? (num2 == 0 ? 1 : num2) : num; } } |
public class TreeMapDemo04 { public static void main(String[] args) { Map<Person, String> work = new TreeMap<Person, String>(); work.put(new Person("有根", 22), "深圳"); work.put(new Person("小亮", 20), "上海"); work.put(new Person("发生", 21), "广州"); work.put(new Person("克弘", 21), "广州"); System.out.println(work); } } |
2、使用TreeMap的有参构造器:
TreeMap(Comparator<? super K> comparator)
构造一个新的、空的树映射,该映射根据给定比较器进行排序。
public class Boss { private String name; private Integer age;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public Boss(String name, Integer age) { super(); this.name = name; this.age = age; }
public Boss() { super(); // TODO Auto-generated constructor stub }
@Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Boss [name="); builder.append(name); builder.append(", age="); builder.append(age); builder.append("]"); return builder.toString(); } } |
public class TreeMapDemo05 { public static void main(String[] args) { Map<Boss, String> work = new TreeMap<Boss, String>( new Comparator<Boss>() { // 匿名类 @Override public int compare(Boss boss1, Boss boss2) { // 先比较年龄,如果年龄相同再比较姓名 int num1 = boss1.getAge() - boss2.getAge(); int num2 = boss1.getName().compareTo(boss2.getName()); return num1 == 0 ? (num2 == 0 ? 1 : num2) : num1; } }); work.put(new Boss("发生", 31), "广州"); work.put(new Boss("有根", 32), "深圳"); work.put(new Boss("小亮", 30), "上海"); work.put(new Boss("克弘", 31), "广州"); System.out.println(work); } } |
1.7 集合嵌套之HashMap嵌套HashMap:
public class HashMapHashMapDemo06 { /* * 15年之后,统计大家的就业情况:学生来自不同的班级(期)【“表-班级”的映射】,每个表之下会有“学生-公司”映射 * * Map<Map<学生对象,公司>,班级> */ public static void main(String[] args) { // 定义一个Map代表Java76期班 Map<Boss, String> java76 = new HashMap<Boss, String>(); java76.put(new Boss("王总", 37), "华为"); java76.put(new Boss("许总", 38), "Tencent");
// 定义一个map代表UI75期班 Map<Boss, String> ui75 = new HashMap<Boss, String>(); ui75.put(new Boss("王书记", 35), "应用科技学院"); ui75.put(new Boss("赵总", 36), "阿里巴巴");
// 定义Map集合,代表所有学生的统计 Map<Map<Boss, String>, String> total = new HashMap<Map<Boss, String>, String>(); total.put(java76, "Java76班"); total.put(ui75, "UI75班");
// System.out.println(total); // 遍历双列集合 for (Map<Boss, String> map : total.keySet()) { String className = total.get(map); // 获取到班级名称 // map代表total的键,同时也代表某一个班级的全部就业情况 for (Boss boss : map.keySet()) { String company = map.get(boss); // 获取到现在的就业公司 System.out.println("boss:" + boss + ";company:" + company + ";className:" + className); } } } } |
1.8 Hashtable类:
面试:HashMap和Hashtable的异同?
1、null键和null值的区别:
HashMap中可以存储null键和null值;Hashtable不允许存储null键和null值。
2、线程安全和效率上区别:
HashMap是JDK1.2版本提出的,线程不安全的,效率高;Hashtable是JDK1.0版本提出的,线程安全的,效率低。
public class HashtableDemo07 { public static void main(String[] args) { // 在HashMap中,键值对的值允许为null,键也允许为null,甚至键和值都可以为null Map<String, Integer> map1 = new HashMap<String, Integer>(); map1.put("王老板", 7); map1.put("许老板", null); map1.put(null, null); System.out.println(map1);
// 在Hashtable中,。不允许存储null键和null值 Map<String, Integer> map2 = new Hashtable<String, Integer>(); map2.put("钟总", 20); // map2.put("马总", null); // 错误,值不允许为null // map2.put(null, 21); // 错误,键不允许为null System.out.println(map2); } } |
1.9 课后练习:
需求:统计字符串中每个字符出现的次数。
如”aaaaabbbbbcccaass”,打印以下结果:
a:7
b:5
c:3
s:2
public class CountChar { /* * 需求:统计字符串中每个字符出现的次数。 如”aaaaabbbbbcccaass”,打印以下结果: a:7,b:5,c:3,s:2 */
// 1、定义一个需要统计的字符串(键盘录入) // 2、字符串转换成字符数组 // 3、定义一个Map,存储“字符-次数”对应关系 // 4、遍历数组,目的是要把字符和次数存储到map集合中 // 5、先判断map集合中的key是否包含当前字符,如果不包含,就把该字符存储到map中,次数设置为1;如果包含,把map集合中该字符key对应的value加1 // 6、打印 public static void main(String[] args) { String str = "abdcslabbbcdss"; char[] arr = str.toCharArray(); // 字符数组 Map<Character, Integer> map = new HashMap<Character, Integer>(); for (char ch : arr) { // ch代表当前指定的某个字符 if (map.containsKey(ch)) { // map中包含该字符 map.put(ch, map.get(ch) + 1); } else { // map不包含该字符 map.put(ch, 1); } } // 打印 // System.out.println(map); for (Character key : map.keySet()) { System.out.println(key + ":" + map.get(key)); } } }
|
二、Collections工具类:
2.1 Collections工具类概述:
public class Collections extends Object
此类完全由在 collection 上进行操作或返回 collection 的静态方法组成。
public static <T> void sort(List<T> list) 排序(自然升序)
public static <T> int binarySearch(List<?> list,T key) 使用二分搜索法搜索指定列表,以获得指定对象。(如果搜索键包含在列表中,则返回搜索键的索引;否则返回 (-(插入点) - 1)。 插入点 被定义为将键插入列表的那一点:即第一个大于此键的元素索引;如果列表中的所有元素都小于指定的键,则为 list.size()。注意,这保证了当且仅当此键被找到时,返回的值将 >= 0。)
public static <T> T max(Collection<?> coll)
public static void reverse(List<?> list)
public static void shuffle(List<?> list)
public class Demo01 { public static void main(String[] args) {
List<String> list = new ArrayList<String>(); list.add("A"); list.add("B"); list.add("D"); list.add("E"); list.add("I"); list.add("C");
System.out.println(list); // 1、排序,与Arrays.sort(char[] arr)排序类似 // Collections.sort(list); // System.out.println(list);
// 2、二分搜索法 // int indexB = Collections.binarySearch(list, "B"); // System.out.println("index of B:" + indexB); // int indexG = Collections.binarySearch(list, "G"); // System.out.println("index of G:" + indexG);
// 3、最值max min // System.out.println("max:" + Collections.max(list));
// 4、反转:reverse // Collections.reverse(list); // System.out.println(list);
// 5、洗牌:shuffle // Collections.shuffle(list); // System.out.println(list); // [E, D, C, A, B, I] // [D, B, A, E, I, C] } }
|
2.2 应用实例——斗地主(V1.0):
public class Demo02 { // 模拟斗地主,洗牌、发牌、看牌。牌没有顺序 // 1、创建一个集合,把牌装进去 // 2、洗牌:shuffle // 3、发牌:三个人+3张底牌 // 4、看牌(打印) public static void main(String[] args) { // 一、买了一副牌 List<String> poker = new ArrayList<String>(); // 四种花色 String[] color = { "黑桃", "红桃", "梅花", "方片" }; // 13个数字 String[] num = { "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K" }; for (String cl : color) { for (String nu : num) { poker.add(cl + nu); } } // System.out.println(poker); poker.add("大王"); poker.add("小王"); // System.out.println(poker); // 二、洗牌: Collections.shuffle(poker); // 三、发牌 List<String> player1 = new ArrayList<String>(); List<String> player2 = new ArrayList<String>(); List<String> player3 = new ArrayList<String>(); List<String> other = new ArrayList<String>(); for (int i = 0; i < poker.size(); i++) { if (i < 3) { other.add(poker.get(i)); } else { // i>=3 if (i % 3 == 0) { player1.add(poker.get(i)); } else if (i % 3 == 1) { player2.add(poker.get(i)); } else { player3.add(poker.get(i)); } } } // 四、看牌 System.out.println("player1:" + player1); System.out.println("player2:" + player2); System.out.println("player3:" + player3); System.out.println("other:" + other); } } |
使用List集合实现的,不能保证顺序?
2.3 应用实例——斗地主(v2.0):
public class Demo03 { // 模拟斗地主,洗牌、发牌、看牌。牌没有顺序 // 1、创建一个集合,把牌装进去 // 2、洗牌:shuffle // 3、发牌:三个人+3张底牌 // 4、看牌(打印) public static void main(String[] args) { // 一、创建牌 Map<Integer, String> poker = new HashMap<Integer, String>(); // 花色 String[] color = { "方片", "梅花", "红桃", "黑桃" }; // 数字 String[] num = { "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2" }; List<Integer> list = new ArrayList<Integer>(); int index = 0; // 索引 // 拼接 for (String n : num) { for (String c : color) { list.add(index); // 把0-51添加到list中 poker.put(index, c + n); index++; } } // 有前面的52张牌 // System.out.println(index); list.add(index); poker.put(index, "小王"); index++; list.add(index); poker.put(index, "大王"); // System.out.println(list);
// 二、洗牌 Collections.shuffle(list); // System.out.println(list);
// 三、发牌 Set<Integer> player1 = new TreeSet<Integer>(); Set<Integer> player2 = new TreeSet<Integer>(); Set<Integer> player3 = new TreeSet<Integer>(); Set<Integer> other = new TreeSet<Integer>(); for (int i = 0; i < list.size(); i++) { if (i >= list.size() - 3) { other.add(list.get(i)); } else { if (i % 3 == 0) { player1.add(list.get(i)); } else if (i % 3 == 1) { player2.add(list.get(i)); } else { player3.add(list.get(i)); } } }
// 四、看牌 show(player1, poker, "惠明的牌"); show(player2, poker, "有根的牌"); show(player3, poker, "发生的牌"); show(other, poker, "底牌"); }
public static void show(Set<Integer> set, Map<Integer, String> map, String player) { System.out.println(player + "是:"); StringBuilder sb = new StringBuilder(); for (Integer key : set) { sb.append(map.get(key) + ","); } System.out.println(sb.deleteCharAt(sb.lastIndexOf(","))); } }
|