[关闭]
@liayun 2016-06-02T15:47:02.000000Z 字数 20425 阅读 1851

Java集合框架——Map

java基础


Map

Map与Collection的不同

Map集合中的常用方法

Map集合:该集合存储键值对,一对一对的往里存,而且要保证键的唯一性。
Map集合中的常用方法:

创建一个集合容器,使用Map接口的子类——HashMap

  1. Map<String, String> map = new HashMap<String, String>();
  1. map.put("01", "zhangsan1");
  2. map.put("02", "zhangsan2");
  3. map.put("03", "zhangsan3");

注意:添加元素时,如果出现相同的键,那么后添加的值会覆盖原有的键对应的值,并且put()方会法返回被覆盖的值。

  1. System.out.println("put:"+map.put("01", "zhangsan1")); // null,因为"01"键所对应的原来的值为null
  2. System.out.println("put:"+map.put("01", "wangwu")); // zhangsan1,因为此时"01"键所对应的原来的值为"zhangsan1"
  1. System.out.println("containsKey:"+map.containsKey("022")); // containsKey:false
  1. System.out.println("remove:"+map.remove("02")); // remove:zhangsan2
  1. System.out.println("get:"+map.get("023")); // get:null
  1. 所以,可以通过get()方法的返回值来判断一个键是否存在,通过返回null来判断。
  2. HashMap允许使用null值和null,如:
  1. map.put("04", null);
  2. System.out.println("get:"+map.get("04")); // get:null
  3. map.put(null, "haha");
  4. System.out.println("get:"+map.get(null)); // get:haha
  1. Collection<String> coll = map.values();
  2. System.out.println(coll);

Map集合中常用类

Map和Set很像,其实,Set底层就是使用了Map集合。

Map集合的两种取出方式

Map集合的两种取出方式:

  1. Set<K> keySet():将map所有的键存入到Set集合,因为Set集合具备迭代器,所以可以通过迭代方式取出所有的键,在根据get()方法,获取每一个键对应的值。Map集合的取出原理:将map集合转成set集合,在通过迭代器取出。
  2. Set<Map.Entry<K,V>> entrySet():将map集合中的映射关系存入到了set集合中,而这个关系的数据类型就是:Map.Entry

第一种取出方式:

  1. import java.util.*;
  2. class MapDemo {
  3. public static void main(String[] args) {
  4. Map<String, String> map = new HashMap<String, String>();
  5. map.put("02", "zhangsan2");
  6. map.put("03", "zhangsan3");
  7. map.put("01", "zhangsan1");
  8. map.put("04", "zhangsan4");
  9. // 先获取map集合的所有的键的Set集合,keySet()
  10. Set<String> ketSet = map.keySet();
  11. // 有了Set集合,就可以获取其迭代器
  12. Iterator<String> it = ketSet.iterator();
  13. while(it.hasNext()) {
  14. String key = it.next();
  15. // 有了键就可以通过map集合的get()方法获取其对应的值
  16. String value = map.get(key);
  17. System.out.println("key:"+key+", value:"+value);
  18. }
  19. }
  20. }

第二种取出方式:

  1. import java.util.*;
  2. class MapDemo {
  3. public static void main(String[] args) {
  4. Map<String, String> map = new HashMap<String, String>();
  5. map.put("02", "zhangsan2");
  6. map.put("03", "zhangsan3");
  7. map.put("01", "zhangsan1");
  8. map.put("04", "zhangsan4");
  9. // 将map集合中的映射关系取出,存入到set集合中
  10. Set<Map.Entry<String, String>> entrySet = map.entrySet();
  11. Iterator<Map.Entry<String, String>> it = entrySet.iterator();
  12. while(it.hasNext()) {
  13. Map.Entry<String, String> me = it.next();
  14. String key = me.getKey();
  15. String value = me.getValue();
  16. System.out.println(key+":"+value);
  17. }
  18. }
  19. }

Map.Entry,其实Entry也是一个接口,它是Map接口中的一个内部接口。源码我们可以理解为:

  1. interface Map {
  2. public static interface Entry {
  3. public abstract Object getKey();
  4. public abstract Object getValue();
  5. }
  6. }
  7. class HashMap implements Map {
  8. class HaHa implements Map.Entry {
  9. public Object getKey() {}
  10. public Object getValue() {}
  11. }
  12. }

练习一:每一个学生都有对应的归属地。学生Student,地址String。学生属性:姓名,年龄。注意:姓名和年龄相同的视为同一个学生。保证学生的唯一性。
解:
分析:

  1. 描述学生。
  2. 定义Map容器,将学生作为键,地址作为值存入。
  3. 获取map集合中的元素。

描述学生:

  1. class Student implements Comparable<Student> {
  2. private String name;
  3. private int age;
  4. Student(String name, int age) {
  5. this.name = name;
  6. this.age = age;
  7. }
  8. public String getName() {
  9. return name;
  10. }
  11. public int getAge() {
  12. return age;
  13. }
  14. public String toString() {
  15. return name + ":" + age;
  16. }
  17. public int hashCode() {
  18. return name.hashCode() + age * 34;
  19. }
  20. public boolean equals(Object obj) {
  21. if(!(obj instanceof Student))
  22. throw new ClassCastException("类型不匹配");
  23. Student s = (Student)obj;
  24. return this.name.equals(s.name) && this.age == s.age;
  25. }
  26. public int compareTo(Student s) {
  27. int num = new Integer(this.age).compareTo(new Integer(s.age));
  28. if(num == 0)
  29. return this.name.compareTo(s.name);
  30. return num;
  31. }
  32. }

定义Map容器,将学生作为键,地址作为值存入。

  1. HashMap<Student, String> hm = new HashMap<Student, String>();
  2. hm.put(new Student("lisi1", 21), "beijing");
  3. hm.put(new Student("lisi1", 21), "tianjin"); // 会覆盖掉键所对应的前一个值
  4. hm.put(new Student("lisi2", 22), "shanghai");
  5. hm.put(new Student("lisi3", 23), "nanjing");
  6. hm.put(new Student("lisi4", 24), "wuhan");

获取map集合中的元素。

  1. // 第一种取出方式 keySet()
  2. Iterator<Student> it = hm.keySet().iterator();
  3. while(it.hasNext()) {
  4. Student stu = it.next();
  5. String addr = hm.get(stu);
  6. System.out.println(stu+"..."+addr);
  7. }
  8. // 第二种取出方式 entrySet()
  9. Set<Map.Entry<Student, String>> entrySet = hm.entrySet();
  10. Iterator<Map.Entry<Student, String>> iter = entrySet.iterator();
  11. while(iter.hasNext()) {
  12. Map.Entry<Student, String> me = iter.next();
  13. Student stu = me.getKey();
  14. String addr = me.getValue();
  15. System.out.println(stu+".........."+addr);
  16. }

练习二:需求:对学生对象的姓名进行升序排序。
解:
分析:因为数据是以键值对的形式存在的。所以要使用可以排序的Map集合。TreeMap。
学生对象在练习一中我们已描述,在此省略。
定义一个按姓名排序的比较器:

  1. class StuNameComparator implements Comparator<Student> {
  2. public int compare(Student s1, Student s2) {
  3. int num = s1.getName().compareTo(s2.getName());
  4. if(num == 0)
  5. return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
  6. return num;
  7. }
  8. }

调用代码:

  1. class MapTest {
  2. public static void main(String[] args) {
  3. TreeMap<Student, String> tm = new TreeMap<Student, String>(new StuNameComparator());
  4. tm.put(new Student("blisi3", 23), "nanjing");
  5. tm.put(new Student("lisi1", 21), "beijing");
  6. tm.put(new Student("alisi4", 24), "wuhan");
  7. tm.put(new Student("lisi1", 21), "tianjin"); // 会覆盖掉键所对应的前一个值
  8. tm.put(new Student("lisi2", 22), "shanghai");
  9. Set<Map.Entry<Student, String>> entrySet = tm.entrySet();
  10. Iterator<Map.Entry<Student, String>> it = entrySet.iterator();
  11. while(it.hasNext()) {
  12. Map.Entry<Student, String> me = it.next();
  13. Student stu = me.getKey();
  14. String addr = me.getValue();
  15. System.out.println(stu+":::"+addr);
  16. }
  17. }
  18. }

练习三:"sdfgzxcvasdfxcvdf"获取该字符串中的字母出现的次数。希望打印结果:a(1)c(2)...
解:
分析:通过结果发现,每一个字母都有对应的次数。说明字母和次数之间都有映射关系。注意:当发现有映射关系时,可以选择map集合。因为map集合中存放的就是映射关系。
本题思路:

  1. 将字符串转换为字符数组。因为要对每一个字母进行操作。
  2. 定义一个Map集合,因为打印结果的字母有顺序,所以使用TreeMap集合。
  3. 遍历字符数组,将每一个字母作为键去查map集合。如果返回null,将该字母和1存入到map集合中。如果返回不是null,说明该字母在map集合中已经存在,并有对应次数。那么就获取该次数并进行自增,然后将该字母和自增后的次数存入到map集合中,覆盖掉原来键所对应的值。
  4. 将map集合中的数据变成指定的字符串返回。

我们可以定义一个方法来获取打印结果:

  1. public static String charCount(String str) {
  2. char[] chs = str.toCharArray();
  3. TreeMap<Character, Integer> tm = new TreeMap<Character, Integer>();
  4. for (int x = 0; x < chs.length; x++) {
  5. if(!(chs[x] >= 'a' && chs[x] <= 'z' || chs[x] >= 'A' && chs[x] <= 'Z'))
  6. continue; // 如果非字符,则继续循环
  7. Integer value = tm.get(chs[x]);
  8. if(value == null) {
  9. tm.put(chs[x], 1);
  10. } else {
  11. value = value + 1;
  12. tm.put(chs[x], value);
  13. }
  14. }
  15. StringBuilder sb = new StringBuilder();
  16. Set<Map.Entry<Character, Integer>> entrySet = tm.entrySet();
  17. Iterator<Map.Entry<Character, Integer>> it = entrySet.iterator();
  18. while(it.hasNext()) {
  19. Map.Entry<Character, Integer> me = it.next();
  20. Character ch = me.getKey();
  21. Integer value = me.getValue();
  22. sb.append(ch+"("+value+")");
  23. }
  24. return sb.toString();
  25. }

可以进一步优化为:

  1. public static String charCount(String str) {
  2. char[] chs = str.toCharArray();
  3. TreeMap<Character, Integer> tm = new TreeMap<Character, Integer>();
  4. int count = 0; // 定义一个变量记录次数
  5. for (int x = 0; x < chs.length; x++) {
  6. if(!(chs[x] >= 'a' && chs[x] <= 'z' || chs[x] >= 'A' && chs[x] <= 'Z'))
  7. continue; // 如果非字符,则继续循环
  8. Integer value = tm.get(chs[x]);
  9. if(value != null)
  10. count = value;
  11. count++;
  12. tm.put(chs[x], count);
  13. count = 0;
  14. }
  15. StringBuilder sb = new StringBuilder();
  16. Set<Map.Entry<Character, Integer>> entrySet = tm.entrySet();
  17. Iterator<Map.Entry<Character, Integer>> it = entrySet.iterator();
  18. while(it.hasNext()) {
  19. Map.Entry<Character, Integer> me = it.next();
  20. Character ch = me.getKey();
  21. Integer value = me.getValue();
  22. sb.append(ch+"("+value+")");
  23. }
  24. return sb.toString();
  25. }

调用代码:

  1. public static void main(String[] args) {
  2. System.out.println(charCount("ak+abf-cdk,abc1defa"));
  3. }

问:什么时候使用map集合呢?
答:当数据之间存在着映射关系时,就要先想到map集合。

map集合扩展知识——一对多关系

一对多的关系:一个学校有多个教室,每一个教室都有名称。一个教室有多个学生。假设学生属性:学号和姓名。

  1. import java.util.*;
  2. class MapDemo {
  3. public static void main(String[] args) {
  4. // 首先定义一个学校
  5. HashMap<String, HashMap<String, String>> czbk = new HashMap<String, HashMap<String, String>>();
  6. // 接着再定义两个教室,预热班和就业班
  7. HashMap<String, String> yure = new HashMap<String, String>();
  8. HashMap<String, String> jiuye = new HashMap<String, String>();
  9. // 学校和教室建立对应关系
  10. czbk.put("yureban", yure);
  11. czbk.put("jiuyeban", jiuye);
  12. // 教室里面装有学生
  13. yure.put("01", "zhangsan");
  14. yure.put("02", "lisi");
  15. jiuye.put("01", "zhaoliu");
  16. jiuye.put("02", "wangwu");
  17. // 遍历czbk集合,获取所有的教室
  18. Iterator<String> it = czbk.keySet().iterator();
  19. while(it.hasNext()) {
  20. String roomName = it.next();
  21. HashMap<String, String> room = czbk.get(roomName);
  22. System.out.println(roomName);
  23. getStudentInfo(room);
  24. }
  25. }
  26. public static void getStudentInfo(HashMap<String, String> roomMap) {
  27. Iterator<String> it = roomMap.keySet().iterator();
  28. while(it.hasNext()) {
  29. String id = it.next();
  30. String name = roomMap.get(id);
  31. System.out.println(id+"::"+name);
  32. }
  33. }
  34. }
  1. class Student {
  2. private String id;
  3. private String name;
  4. public Student(String id, String name) {
  5. this.id = id;
  6. this.name = name;
  7. }
  8. public String toString() {
  9. return id + ":::" + name;
  10. }
  11. }
  1. class MapDemo3 {
  2. public static void demo() {
  3. // 首先定义一个学校
  4. HashMap<String, List<Student>> czbk = new HashMap<String, List<Student>>();
  5. // 接着再定义两个教室,预热班和就业班,注意此时使用的是List集合
  6. List<Student> yure = new ArrayList<Student>();
  7. List<Student> jiuye = new ArrayList<Student>();
  8. // 学校和教室建立对应关系
  9. czbk.put("yureban", yure);
  10. czbk.put("jiuyeban", jiuye);
  11. // 教室里面装有学生
  12. yure.add(new Student("01", "zhangsan"));
  13. yure.add(new Student("04", "wangwu"));
  14. jiuye.add(new Student("01", "zhouqi"));
  15. jiuye.add(new Student("02", "zhaoliu"));
  16. Iterator<String> it = czbk.keySet().iterator();
  17. while(it.hasNext()) {
  18. String roomName = it.next();
  19. List<Student> room = czbk.get(roomName);
  20. System.out.println(roomName);
  21. getInfos(room);
  22. }
  23. }
  24. public static void getInfos(List<Student> list) {
  25. Iterator<Student> it = list.iterator();
  26. while(it.hasNext()) {
  27. Student s = it.next();
  28. System.out.println(s);
  29. }
  30. }
  31. public static void main(String[] args) {
  32. demo();
  33. }
  34. }

集合框架中的工具类

Collections

Collections常见方法

Arrays

Arrays:用于操作数组的工具类。里面都是静态方法。

Arrays的2个常见方法

集合变数组:Collections接口中的toArray()方法

例,Collections接口中的toArray()方法的应用。

  1. import java.util.*;
  2. class CollectionToArray {
  3. public static void main(String[] args) {
  4. ArrayList<String> al = new ArrayList<String>();
  5. al.add("abc1");
  6. al.add("abc2");
  7. al.add("abc3");
  8. String[] arr = al.toArray(new String[al.size()]);
  9. System.out.println(Arrays.toString(arr));
  10. }
  11. }
  1. 指定类型的数组到底要定义多长呢?
    当指定类型的数组长度小于了集合的size,那么该方法内部会创建一个新的数组,长度为集合的size。当指定类型的数组长度大于了集合的size,那么就不会新创建数组,而是使用传递进来的数组。所以创建一个刚刚好的数组最优。
  2. 为什么要将集合变数组?
    为了限定对元素的操作,不需要进行增删了。

JDK1.5新特性

高级for循环

格式:

  1. for(数据类型(一般是泛型类型) 变量名 : 被遍历的集合(Collection)或者数组) {
  2. }

例,遍历集合。

  1. ArrayList<String> al = new ArrayList<String>();
  2. al.add("abc1");
  3. al.add("abc2");
  4. al.add("abc3");

之前我们使用迭代器遍历:

  1. Iterator<String> it = al.iterator();
  2. while(it.hasNext()) {
  3. System.out.println(it.next());
  4. }

现在我们可使用高级For循环,就简化书写了。

  1. for(String s : al) {
  2. System.out.println(s);
  3. }

结论:高级For循环对集合进行遍历,只能获取集合中的元素,但是不能对集合进行操作。迭代器除了遍历,还可以进行remove()集合中元素的动作。如果使用ListIterator,还可以在遍历过程中进行对集合进行增删改查的操作。
对于数组int[] arr = {3,5,1};,我们可使用传统for循环:

  1. for (int x = 0; x < arr.length; x++) {
  2. System.out.println(arr[x]);
  3. }

还可使用高级For循环:

  1. for (int i : arr) {
  2. System.out.println("i:"+i);
  3. }

问:传统for和高级for有什么区别呢?
答:高级for有一个局限性,必须有被遍历的目标。
建议:在遍历数组的时候还是希望使用传统for,因为传统for可以定义角标。
对于Map集合,我们也可以使用高级For循环。

  1. HashMap<Integer, String> hm = new HashMap<Integer, String>();
  2. hm.put(1, "a");
  3. hm.put(2, "b");
  4. hm.put(3, "c");

第一种遍历方式:

  1. Set<Integer> keySet = hm.keySet();
  2. for (Integer i : keySet) {
  3. System.out.println(i+"::"+hm.get(i));
  4. }

第二种遍历方式:

  1. for (Map.Entry<Integer, String> me : hm.entrySet()) {
  2. System.out.println(me.getKey()+"---"+me.getValue());
  3. }

可变参数

JDK1.5版本出现的新特性。
可变参数:其实就是一种数组参数的简写形式,不用每一次都手动的建立数组对象,只要将要操作的元素作为参数传递即可,隐式地将这些参数封装成了数组。

  1. class ParamMethodDemo {
  2. public static void main(String[] args) {
  3. show("gaga",2,3,4,5,6);
  4. }
  5. public static void show(String str, int... arr) {
  6. System.out.println(arr.length);
  7. }
  8. }

注意:方法的可变参数在使用时,可变参数一定要定义在参数列表的最后面。

静态导入

例,

  1. import java.util.*;
  2. import static java.util.Arrays.*; // 导入的是Arrays类中的所有静态成员。
  3. import static java.lang.System.*; // 导入了System类中所有静态成员。
  4. class StaticImport extends Object {
  5. public static void main(String[] args) {
  6. out.println("hello");
  7. int[] arr = {3,1,5};
  8. sort(arr);
  9. int index = binarySearch(arr, 1);
  10. System.out.println(Arrays.toString(arr)); // 此时不能省略Arrays,方法重名时,指定具备所属的对象或类。
  11. System.out.println("index="+index);
  12. }
  13. }

注意:

  1. 当类名重名时,需要指定具体的包名。
  2. 当方法重名时,指定具备所属的对象或类。
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注