13. JavaAPI_集合框架 (第十六天)

一    Map概述

1、初步认识Map集合

Map和Collection一样,也是属于 Java Collections Framework的顶层接口。

首先看API文档的介绍:

接口 Map<K,V> — 其中 “K代表Key(键),V代表Value(值)”,

Map对象每次能存键、值两个元素,键值间有映射关系,

一个映射不能包含重复的键;每个键最多只能映射到一个值。

K — 此映射所维护的键的类型

V — 映射值的类型

Map集合存储键值对是 一对一对往里存,而且要保证键的唯一性。

2、Map集合的“增删改查”

|---- 添加
     |---- put(K key,V value)将指定的值与此映射中的指定键关联(可选操作)。
     |----putAll(Map<? extends K,? extends V> m) 从指定映射中将所有映射关系复制到此映射中(可选操作)。
|---- 删除
     |----clear()   清空
     |----remove(Object key) 如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
|---- 判断
     |----containsKey(Object key)  是否包含某个key
     |----containsValue(Object value)  是否包含某个value
     |----isEmpty()如果此映射未包含键-值映射关系,则返回true。
|---- 获取
     |----get(Object key) 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回null
     |----int size()返回此映射中的键-值映射关系数。
     |----Collection<V> values()返回此映射中包含的值的 Collection 视图。


     |----Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射关系的 Set 视图。
     |----Set<K> keySet()返回此映射中包含的键的 Set 视图。

二    Map子类对象特点

我们要了解Map集合的三个子类对象,Hashtable、HashMap、TreeMap,后两个比较常见。

|---- Map

      |---- Hashtable:底层是哈希表数据结构,不可以存入null键null值。(JDK1.0出现的,线程同步)

                        此类实现一个哈希表,该哈希表将键映射到相应的值。任何非 null 对象都可以用作键或值。
                        为了成功地在哈希表中存储和获取对象,用作键的对象必须实现 hashCode 方法和 equals 方法。

      |---- HashMap:   底层是哈希表数据结构, 此实现提供所有可选的映射操作,允许使用null键和null值,该集合不同步。(JDK1.2出现,效率更高)

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

                        HashMap不保证映射的顺序,特别是它不保证该顺序一直不变。

      |---- TreeMap:  底层是二叉树数据结构,线程不同步。可以用于给Map集合中的键进行排序。

                        TreeMap根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。

我们可以发现,Map集合和Set集合很类似,这是因为Set底层就使用了Map集合。

另外,面试中“Hashtable和HashMap有什么区别”这个问题经常会被问到,请留意。

三    Map共性方法

概述中已经提到,这里简单以代码说明:

import java.util.*;

public class TreeMapDemo {

	public static void main(String[] args) {

		Map<String, String> tm = new HashMap<String, String>();
		// 添加元素
		tm.put("12", "AAA");
		tm.put("13", "ABA");
		tm.put("12", "ACA");
		tm.put("22", "AAD");
		tm.put(null, "NULL1");
		tm.put(null, "NULL2");
		tm.put("16", null);
		// 看“12”那个键。添加元素,如果出现相同的键。那么后面添加的值会覆盖原有键对应的值。
		// put方法会返回被覆盖的值。

		System.out.println(tm.size()); // Key为null的映射关系是不算在里面的。

		System.out.println(tm.containsKey("12")); // true
		System.out.println(tm.containsValue("ABA")); // true

		System.out.println(tm.get("12")); // ACA
		// 通过get方法的返回值来判断一个键是否存在。通过返回null来判断
		System.out.println(tm.get("null")); // 不是返回NULL1或者NULL2,总是返回null

		// values()方法:获取map集合中所有的值。
		Collection<String> coll = tm.values();
		System.out.println(coll); // //{null=NULL2, 22=AAD, 16=null, 13=ABA}
		System.out.println(tm); // {null=NULL2, 22=AAD, 16=null, 13=ABA, 12=ACA}

		System.out.println(tm.remove("12")); // ACA,注意返回的是被移除那个键的值。
		System.out.println(tm); // {null=NULL2, 22=AAD, 16=null, 13=ABA}
	}

}

输出:

5
true
true
ACA
null
[NULL2, AAD, null, ABA, ACA]
{null=NULL2, 22=AAD, 16=null, 13=ABA, 12=ACA}
ACA
{null=NULL2, 22=AAD, 16=null, 13=ABA}

四   Map -KeySet

Map集合取出里面的对象,有两种方式(就是键、键值对各对应一种):KeySet()和entrySet()。

先了解KeySet,API文档说:“Set<K> keySet() 返回此映射中包含的键的 Set 视图。”

也就是:将map中所有的键存入到Set集合。

因为Set有迭代器,所以可以用迭代方式取出所有的键,再根据get方法。获得每一个键对应的值。

示例代码:

import java.util.*;

public class TreeMapDemo {

    public static void main(String[] args) {

        Map<String, String> map = new HashMap<String, String>();
        map.put("12", "AAA");
        map.put("13", "ABA");
        map.put("12", "ACA");
        map.put("22", "AAD");

        // 先获取map集合的所有键的Set集合,keySet()
        Set<String> theMapSet = map.keySet();
        // 有了Set集合,就可以获取其迭代器。
        Iterator<String> it = theMapSet.iterator();

        while (it.hasNext()) {
        // 有了键可以通过map集合的get方法获取其对应的值
        String key = it.next();
        System.out.println("Key : " + key + "    Value : " + map.get(key));
        }
    }
}

输出:

Key : 22    Value : AAD

Key : 13    Value : ABA

Key : 12    Value : ACA

这种keySet()的取出方式,是将Map集合转为Set集合,然后通过迭代器取出。

五   Map - entrySet

1、认识entrySet()方法

现在介绍另外一种取出方式,entrySet()。

先看API文档:

可以看出,entrySet()将Map集合中的映射关系存入到了set集合中,而这个关系的数据类型就是:Map.Entry。

Map.Entry<K,V>:映射项(键-值对)。再看Map.Entry<K,V>接口的方法:

那么关系对象 Map.Entry 获取到后,就可以通过,Map.Entry中getKey()和getValue()方法获取关系中的键和值

(Map.Entry 其实Entry也是一个接口,她是接口中的一个内部接口)

比喻:就相当于把“结婚证书”这样的关系取出来了,所以既不是丈夫类型也不是妻子类型,而是夫妻关系(Map.Entry)类型。

2、示例代码

import java.util.*;

public class TreeMapDemo {

	public static void main(String[] args) {

		Map<String, String> map = new HashMap<String, String>();
		map.put("12", "AAA");
		map.put("13", "ABA");
		map.put("12", "ACA");
		map.put("22", "AAD");

		// 将Map集合中的映射关系取出,存入到Set集合中。
		Set<Map.Entry<String, String>> entrySet = map.entrySet(); // 将Map集合中的映射关系取出,这个关系是Map.Entry类型
		Iterator<Map.Entry<String, String>> it = entrySet.iterator();

		while (it.hasNext()) {
			Map.Entry<String, String> me = it.next();
			String key = me.getKey(); // 那么关系对象Map.Entry获取到后
			String value = me.getValue(); // ,就可以通过Map.Entry中getKey和getValue方法获取关系中的键和值
			System.out.println(key + ":" + value);
		}
	}
}

/*
 * Map.Entry其实Entry也是一个接口,它是Map接口中的一个内部接口。
 */

/*
 * interface Map{ public static interface Entry{ //有了Map集合才有关系,所以Entry是内部接口
 * public abstract Object getKey();
 * public abstract Object getValue();
 * } 
 * }
 */

输出:

22:AAD
13:ABA
12:ACA

六   Map练习

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

1、描述学生
2、定义Map容器,将学生作为键,地址作为值,存入
3、获取map集合中元素
 */
import java.util.*;

public class Demo {
	public static void main(String[] args) {
		HashMap<Student, String> map = new HashMap<Student, String>();
		map.put(new Student("lisi1", 21), "beijing");
		map.put(new Student("lisi2", 22), "shanghai");
		map.put(new Student("lisi3", 23), "nanjing");
		map.put(new Student("lisi4", 24), "wuhan");
		sop("第一种");
		Set<Student> setMap = map.keySet();
		Iterator<Student> it = setMap.iterator();
		while (it.hasNext()) {
			Student st = it.next();
			sop("name:" + st.getName() + " value:" + st.getAge());
		}
		sop("第二种");
		Map<Student, String> map2 = new HashMap<Student, String>();
		map2.put(new Student("lisi1", 21), "beijing");
		map2.put(new Student("lisi2", 22), "shanghai");
		map2.put(new Student("lisi3", 23), "nanjing");
		map2.put(new Student("lisi4", 24), "wuhan");
		// 将Map集合中的映射关系取出。存入到Set集合中。
		Set<Map.Entry<Student, String>> entrySet = map2.entrySet();
		Iterator<Map.Entry<Student, String>> it2 = entrySet.iterator();
		while (it2.hasNext()) {
			Map.Entry<Student, String> me = it2.next();
			sop("name:" + me.getKey().getName() + " value:"
					+ me.getKey().getAge() + " address:" + me.getValue());
		}
	}

	public static void sop(Object obj) {
		System.out.println(obj);
	}
}

class Student implements Comparable {
	private String name;
	private int age;

	Student(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public int getAge() {
		return age;
	}

	public String toString() {
		return name + ":" + age;
	}

	public int hashCode() {
		return name.hashCode() + age * 34;
	}

	public boolean equals(Object obj) {
		if ((obj instanceof Student))
			throw new ClassCastException("类型不匹配");
		Student s = (Student) obj;
		return this.name.equals(s.name) && this.age == s.age;
	}

	public int compareTo(Object obj) {
		Student s = (Student) obj;
		int num = new Integer(this.age).compareTo(new Integer(s.age));
		if (num == 0)
			return this.name.compareTo(s.name);
		return num;
	}
}

七   TreeMap练习

练习:对学生对象的年龄进行升序排序

/*
需求:对学生对象的年龄进行升序排序

因为数据是以键值对形式存在的
所以要使用可以排序的Map集合,TreeMap
 */
import java.util.*;

public class Demo {
	public static void main(String[] args) {
		Map<Student, String> map = new TreeMap<Student, String>();
		map.put(new Student("lisi1", 23), "beijing");
		map.put(new Student("lisi2", 22), "shanghai");
		map.put(new Student("lisi3", 21), "nanjing");
		map.put(new Student("lisi4", 24), "wuhan");
		// 将Map集合中的映射关系取出。存入到Set集合中。
		Set<Map.Entry<Student, String>> entrySet = map.entrySet();
		Iterator<Map.Entry<Student, String>> it = entrySet.iterator();
		while (it.hasNext()) {
			Map.Entry<Student, String> me = it.next();
			sop("name:" + me.getKey().getName() + " value:"
					+ me.getKey().getAge() + " address:" + me.getValue());
		}
	}

	public static void sop(Object obj) {
		System.out.println(obj);
	}
}

class Student implements Comparable {
	private String name;
	private int age;

	Student(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public int getAge() {
		return age;
	}

	public String toString() {
		return name + ":" + age;
	}

	public int hashCode() {
		return name.hashCode() + age * 34;
	}

	public boolean equals(Object obj) {
		if ((obj instanceof Student))
			throw new ClassCastException("类型不匹配");
		Student s = (Student) obj;
		return this.name.equals(s.name) && this.age == s.age;
	}

	public int compareTo(Object obj) {
		Student s = (Student) obj;
		int num = new Integer(this.age).compareTo(new Integer(s.age));
		if (num == 0)
			return this.name.compareTo(s.name);
		return num;
	}
}

输出:

name:lisi3 value:21 address:nanjing
name:lisi2 value:22 address:shanghai
name:lisi1 value:23 address:beijing
name:lisi4 value:24 address:wuhan

八   TreeMap练习 - 字母出现的次数

"sdfgzxcvasdfxcvdf"获取该字符串中的字母出现的次数

希望打印结果  a(1)c(2)

思考:

通过结果发现,每一个字母都有对应的次数,说明字母和次数之间都有映射关系

注意,当发现有映射关系时,可以选择map集合。因为map集合中存放的就是映射关系。

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


思路:
(1) 将字符串转换成字符数组,因为要对每一个字母进行操作
(2) 定义一个map集合,因为打印结果的字母有顺序,所以使用TreeMap集合
(3) 遍历字符数组,

  • 将每一个字母作为键去查map集合
  • 如果返回null,将该字母和1存入到集合中
  • 如果返回不是null,说明该字母在map集合已存在,并有已存在的次数,那么获取该次数并自增,
  •     然后将该字母和自增后的次数存入到map集合中,覆盖掉原键所对应的值

(4) 将map集合中的数据编程指定的字符串形式返回

import java.util.*;

public class Demo {
	public static void main(String[] args) {
		String sb = charCount("aabfcdabcdefa");
		sop(sb);
	}

	public static String charCount(String str) {
		char[] chs = str.toCharArray();
		TreeMap<Character, Integer> tm = new TreeMap<Character, Integer>();
		for (int x = 0; x < chs.length; x++) {
			if (!(chs[x] >= 'a' && chs[x] <= 'z' || chs[x] >= 'A'
					&& chs[x] <= 'Z'))
				continue;
			Integer value = tm.get(chs[x]);
			int count = 0;
			if (value != null)
				count = value;
			count++;
			tm.put(chs[x], count);
		}
		StringBuilder sb = new StringBuilder();
		Set<Map.Entry<Character, Integer>> entryset = new TreeSet<Map.Entry<Character, Integer>>(
				new myCompare());
		entryset = tm.entrySet();
		Iterator<Map.Entry<Character, Integer>> it = entryset.iterator();
		while (it.hasNext()) {
			Map.Entry<Character, Integer> me = it.next();
			Character ch = me.getKey();
			Integer value = me.getValue();
			sb.append(ch + "(" + value + ") ");
		}
		return sb.toString();
	}

	public static void sop(Object obj) {
		System.out.println(obj);
	}
}

class myCompare implements Comparator {
	public int compare(Object o1, Object o2) {
		return 1;
		// o1 = (Map.Entry)o1;
		// o2 = (Map.Entry)o2;
		// int value =
		// (Integer)(o1.getValue()).compareTo((Integer)(o2.getValue()));
		// if(value==0){
		// value = (Character)(o1.getKey()).compareTo((Character)(o2.getKey()));
		// }
	}
}

输出:a(4) b(2) c(2) d(2) e(1) f(2)

九   Map扩展

import java.util.*;

public class Demo {

    public static void main(String[] args) {
        FirstMethod();
        AnotherMethod();
    }

    public static void FirstMethod() {
        HashMap<String, HashMap<String, String>> school = new HashMap<String, HashMap<String, String>>();
        HashMap<String, String> yure = new HashMap<String, String>(); // 预热班
        HashMap<String, String> jiuye = new HashMap<String, String>();// 就业班

        yure.put("01", "zhangsan");
        yure.put("02", "lisi");

        jiuye.put("01", "wangwu");
        jiuye.put("02", "zhaoliu");

        school.put("yure", yure);
        school.put("jiuye", jiuye);

        // 取出某个班级的学生
        // getStudentInfo(yure);

        // 遍历school集合。获取所有的教室。
        Iterator<String> it = school.keySet().iterator();
        while (it.hasNext()) {
            String roomName = it.next();
            sop(roomName);
            getStudentInfo1(school.get(roomName)); // 其实就是循环嵌套
        }
    }

    public static void getStudentInfo1(HashMap<String, String> roomMap) {
        Iterator<String> it = roomMap.keySet().iterator();
        while (it.hasNext()) {
            String id = it.next();
            String name = roomMap.get(id);
            System.out.println(id + ":" + name);
        }
    }

    public static void AnotherMethod() {
        HashMap<String, List<Student>> school = new HashMap<String, List<Student>>();
        List yure = new ArrayList<Student>(); // 预热班
        List jiuye = new ArrayList<Student>();// 就业班
        school.put("yure", yure);
        school.put("jiuye", jiuye);
        yure.add(new Student("01", "zhangsan"));
        yure.add(new Student("02", "lisi"));
        jiuye.add(new Student("01", "wangwu"));
        jiuye.add(new Student("02", "zhaoliu"));

        Iterator<String> it = school.keySet().iterator();
        while (it.hasNext()) {
            String roomName = it.next();
            List<Student> room = school.get(roomName);
            System.out.println(roomName);
            getStudentInfo2(room);
        }
    }

    public static void getStudentInfo2(List<Student> list) {
        Iterator<Student> it = list.iterator();
        while (it.hasNext()) {
            Student s = it.next();
            System.out.println(s.getId() + " : " + s.getName());
        }
    }

    public static void sop(Object obj) {
        System.out.println(obj);
    }
}

class Student {

    private String id;
    private String name;

    public Student(String id, String name) {
        super();
        this.id = id;
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

通过上面的例子了解map的扩展知识,map之所以结合被使用,是因为具备映射关系。

假设我们学校有“预热班”和“就业班”,两个班又有自己从“01”学号开始的学生,那么用map怎么存呢?

因为学生本身有"学号-姓名"的映射,"班级-学生-"又有映射关系,

所以这里不同以往,有两个映射关系,因为集合本身也是对象,

所以我们可以将"学生"映射关系作为值传入"班级-学生-"的关系中。

也就是:map("所在班级",“学号-姓名”);这样的map。

以此实现了FirstMethod()那样的代码,之后再改进为AnotherMethod()那样的代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值