A019_集合框架Map_泛型

1.内容介绍

Map-HashMap(重点掌握)
Properties(重点掌握)
Collections 工具类(掌握)
泛型(掌握)

2.Map

2.1.什么是Map

也是一种容器,和前几天学习的有啥区别
API文档描述:将键映射到值的对象。一个映射(一个Map对象)不能包含重复的键;每个键最多只能映射到一个值 Collection接口下
 键(key) : 本质就是一个数据
 值(value) : 本质也是一个数据
 键值对: 就是一对(和两个是有区别的),一对一般是有某种关系
 映射 : 上面说的一个映射就看成是一个Map对象 key对应value是一种映射关系
映射如下图:通过左边的key可以找到右边的value
在这里插入图片描述
小结: 什么是Map
Map也是一种容器的接口,里面存储的是一对一对的数据,每添加一次,添加的是两个(一对)数据
Collection体系,存储在都是单个单个的数据,数据之间没有关系的
Map和Collection是两个没有关系并列的体系(主要从代码结构来看)
Map的声明: public interface Map{}

2.2.Map的应用场景

例如QQ的账号+密码:想象场景-需要使用一个集合搜集各位100人的QQ号码和密码;
银行账户和密码
游戏账号和密码
搜集各位的姓名+女朋友的姓名 (情侣关系)
坐标(x,y)
…

2.3.Map基本结构及存储结构
Map   接口
	|--  TreeMap  : 判断重复的规则和TreeSet一样
		 TreeMap中的键(key)要么是具有自然排序能力;或者TreeMap中提供一个比较器
		 如果key是自然或者定制排序,返回值是0表示,key是重复的,如果是正数,则按照key的升序排序,否则是降序			
	|--  HashMap  : key判断键是否重复和HashSet判断重复一样:1. hashCode  2.equals()
			
	|--  HashTable  : 就是一个线程安全的HashMap
	|--  LinkedHashMap : 就是一个内部维护了一个链表的HashMap,可以保证存入和取出顺序
	|--	 Properties : 特殊的HashTable,只不过key-value都是String类型,一般用来做配置文件

Map基本使用
Map存储结构分析
在这里插入图片描述

2.4.Map的中的重要方法
基本方法列表:
1、Object put(Object key,Object value);   如果当前添加的这个key已经存在了,会覆盖这个键之前的值,返回的是之前的value值
2、boolean containsKey(Object key) 		 判断Map集合中是否包含当前的传入的key
3、boolean containsValue(Object value)	判断Map集合中是否包含当前的传入的value
4、Object get(Object key) ;				根据指定的键key找到对应的值,如果没有找到返回 null
5、remove(Object key)  ;    			 删除指定key对应的那一对

重点方法列表:
6、Collection  values()   	返回一个Map中的所有的value值  Collection集合
7、Set keySet()    			返回所有的key值的集合(key是唯一的,所以返回的是Set集合)
8、Set entrySet()  			返回所有的entry对象(key是唯一的,所以返回的是Set集合)

问题:为什么第一个是返回Collection  二三个返回的是Set?
key   entry都是不能够重复的--- Set
2.5.Map的遍历方式 :
  1. 如果只是获取所有一个值,推荐使用 values()方法
  2. 如果只是获取所有的key,推荐使用keySet()方法
  3. 如果获取所有的key-value,就使用entrySet(),不要用先通过keySet()获取key,再用get()获取具体的value,这样会导致二次查询,效率低下

说明: 只考虑我们自己把放进去的key-value都一个一个拿出来
1、foreach 失败了!
原因:Map和Iterable没有任何关系
2、普通for/ while / do-while 失败
原因: Map中没有通过索引来获得数据的方法
3、迭代器 失败!
原因: ① Map没有实现Iterable接口就没有其中获得迭代器的方法;
② Map中也没有自己设计一个获得迭代器的方法
4、遍历方式一:
① 先获得所有的key -> set
② 遍历set ,通过调用Object get(Object key) 获得对应的value

Map m = new HashMap();
m.put("小明", 90);
m.put("小马", 60);
m.put("小郭", 60);
Set keys = m.keySet();
for (Object k : keys) {
	System.out.println(k+"<---->"+m.get(k));
}

5、遍历方式二:
① 得到所有的Entry对象 ,通过调用方法 Set entrySet();
② 遍历上面的entry 的集合,依次从每一个Entry对象中取出对应的key-value

Map m = new HashMap();
m.put("小明", 90);
m.put("小马", 60);
m.put("小郭", 60);
Set entrys = m.entrySet();
for (Object en : entrys) {
	// 需要把en强制转成 Entry类型
	Entry e = (Entry)en;
	System.out.println(e.getKey()+"--"+e.getValue());
}
2.6.Map 特点及使用场景选择:

1.HashMap中k的值没有顺序,常用来做统计。 根据hashCode保存jdk1.8以后hashMap源码 变了,底层优化了
在这里插入图片描述
表示当相同hash值相同的元素大于8个时候,存储结构会变成红黑树来存储,而之前是链表结构
2.LinkedHashMap它内部维护了一个链表,保持Key插入的顺序。迭代的时候,也是按照插入顺序迭代。
3.TreeMap的顺序是Key的自然顺序(如整数从小到大,默认是按键值的升序排序),也可以指定比较函数。但不是插入的顺序。Comparator 也不支持null键,支持null value
4.Hashtable与 HashMap类似,它继承自Dictionary类、不同的是:它不允许记录的键或者值为null;它支持线程的同步、即任一时刻只有一个线程能写Hashtable,因此也导致了 Hashtable在写入时会比较慢。
注意:LinkedHashMap在遍历的时候会比HashMap慢,不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会 比LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,和值无关,而HashMap的遍历速度和他的容量有关

2.7.Map的使用选择

一般情况下我们选用HashMap,因为HashMap的键值对在取出时是随机的,其依据键的hashCode和键的equals方法存取数据,具有很快的访问速度,所以在Map中插入、删除及索引元素时其是效率最高的实现。而TreeMap的键值对在取出时是排过序的,所以效率会低点
1、Map中,HashMap具有超高的访问速度,如果我们只是在Map 中插入、删除和定位元素,而无关线程安全或者同步问题,HashMap 是最好的选择。
2、如果考虑线程安全或者写入速度的话,可以使用HashTable,JDK8后建议使用ConcurrentHashMap替代HashTable,既能获取超高的访问速度,又能保证线程安全
3、如果想按怎么存的顺序怎么取,比如队列形式,排队,那么使用LinkedHashMap
4、如果需要让Map按照key进行升序或者降序排序,那就用TreeMap

2.8.Map小结

3.Properties

3.1.什么是Properties
1、继承体系
Map  接口  : 存储键值对
	|--- Hashtable  哈希表,线程安全的(和HashMap差不多,很多时候就是用HashMap替代)
	|--- Properties 类表示了一个持久的属性集
2、从上面的关系来看,Properties就可以当成一个特殊的Map进行使用
3、一般来说在子类中会拓展子类特有的一些功能,有哪些特性?
	1)就是一个特殊的Map,Map中的所有方法,可以调用的;
	2)放入Properties中的key-value都是String类型
	3)针对于第二点Properties中提供了特殊的存值和取值的方法,尽量不要用Map的方法
4、作用:
	1)将内存中的数据写入到硬盘中
	2)将硬盘中的数据读取到内存中
3.2.Properties重要方法

1、Object setProperty(String key, String value)
2、String getProperty(String key)
3、String getProperty(String key, String defaultValue) ; 如果通过key没有找到对应的键值对,返回后面的defaultValue

Properties pp = new Properties();//获得一个Properties对象
		
pp.setProperty("XXXX", "OOOO");//给Properties对象设置值
pp.setProperty("YYYY", "JJJJ");
		
System.out.println(pp.size());
System.out.println(pp);
		
System.out.println(pp.getProperty("XXXX"));//从Properties对象根据key获得值

Most important下面才是最重要的
4、void list(PrintStream out) 简单理解: 此方法可以把Properties中的数据写入磁盘文件
5、void load(InputStream inStream) 简单理解: 可以把磁盘文件中的数据读取到Properties中来

3.3.Properties的应用场景(读写资源文件)

Properties 中list方法的值持久化到文件中

Properties pp = new Properties();
pp.setProperty("XXXX", "OOOO");
pp.setProperty("YYYY", "JJJJ");
PrintStream ps = new PrintStream("F:/qq.txt");
pp.list(ps);

Properties 中load方法讲文件中的值加载到Properties 对象中

FileInputStream fis = new FileInputStream("D:/eclipse-jee-juno-SR2-win32-x86_64/eclipse/dropins/easyexplore.link")
pp.load(fis);
System.out.println(pp);
		
String property = pp.getProperty("path");
System.out.println(property);
3.4.Properties小结

4.Collections 工具类

1、static Object max(Collection coll) 返回容器中的最大值,内部会调用coll中的对象的compareTo方法进行比较大小—自然排序)
2、static Object max(Collection coll,Comparator com) 根据比较器指定的比较规则,返回容器中的最大值,内部会调用比较器的比较方法Compare方法
3、…其他详细查看API

5.泛型

5.1.什么是泛型(体验)
5.1.1.泛型介绍

泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java语言引入泛型的好处是安全简单。
简单的理解:就是在设计类(接口等)的时候,没有给规定具体是什么类型的参数,在实例化或调用的时候再传入具体的类型,告诉编译器这是什么类型。
注意:

  1. 泛型不会影响程序的运行的速度,因为在编译期就会直接编译成具体的类型。
  2. 使用泛型的方法或实例化泛型的类,只能用传入引用数据类型,不能传入基本数据类型
5.1.2.规则限制

1、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。
2、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
3、泛型的类型参数可以有多个。
4、泛型的参数类型可以使用extends语句,例如。习惯上称为“有界类型”。
5、泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName(“java.lang.String”);

5.1.3.泛型作用

泛型的作用:在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率。

// 准备一个List用来存储公司员工对象 假设有一个类 Employee

List  emp = new ArrayList();
emp.add(new Employee("张三"));
emp.add(new Employee("李三"));
emp.add(new Employee("王三"));

由于List中可以存储任意类型的数据,因此可以这样

emp.add("王麻子");
emp.add(new Dog("旺财"));

// 写一个发工资的方法

for(Object o : emp){
	Employee ep = (Employee)o;
	System.out.println("给"+ep.getName() + 50000);
}

上面代码可能引发问题: 当我们获得一个字符串 或者一条狗的时候可能就调用不出来getName()方法
结果: 从业务上来讲,emp集合中只应该放Employee类型的数据,但是List可以放任意的类型的数据,在编译的时候我们无法阻止

怎么办??? 使用泛型 —> 限定我们上面的emp中只能够放Employee
List emp = new ArrayList();

5.2.泛型类(接口)的设计

需求:设计一个点类Point,其中设计字段 x y;
问题:里面的字段x y 使用什么类型好呢? double int 。。。
效果:需要设计上面的类,满足不同的人的需求,有些希望使用double int…

class Point{
    double x;
    double y;
}
class Point{
    int x;
    int y;
}
.......

一个类本质看成是一个模板,然后希望是一个通用的模板:

class Point{
    数据类型 x;
    数据类型 y;
}  

希望在设计类的时候,其中的数据类型我们就写一个符号,泛指所有的类型,但是不指具体的某种类型
当有人使用我们上面的模板来创建一个对象的时候他们自己可以指定自己的类型

怎么做:

class Point<数据类型>{    // 表示在类声明了一个符号  数据类型,是一种泛指
   数据类型 x;
   数据类型 y;
}  

规范的写法应该是使用一些字母(常见的字母):
 E   Element   元素
 T   Type    类型
 K   Key      键
 V   Value   值
也可以支持多个泛型参数例如Map<K,V>

5.3.泛型具体的使用案例

1、把学生对象存入TreeSet中,自然排序 或者 比较器
2、统计字符串中字母个数的示例

public class Student implements Comparable<Student>{
	@Override
	public int compareTo(Student o) {
		// TODO Auto-generated method stub
		return 0;
	}
}
5.4.泛型的上限、下限
public class Test {
	/*
	 * 泛型的上限下限
	 * ? 号:表示通配符,不确定的类型,可以表示任意类型【? extends Object】;
			?  extends Number : 表示通配符 ?的[上限],必须是Number及Number的子类
			?  super    Number : 表示通配符 ?的[下限],必须是Number及Number的父类
	 * */
	public static void main(String[] args) {
		ArrayList<Object> lsit1 = new ArrayList<>();
		ArrayList<Number> lsit2 = new ArrayList<>();
		ArrayList<Integer> lsit3 = new ArrayList<>();
		ArrayList<Double> lsit4 = new ArrayList<>();
		ArrayList<String> lsit5 = new ArrayList<>();
		
		//get1(lsit1);//<?  extends Number>:最高可以传入的类型是Number及其子类类型
		//get2(list2);//<?  super Number> :最低可以传入本类类型Number及其父类类型

	}
	public static void get1(ArrayList<? extends Number> list){}
	public static void get2(ArrayList<? super Number> list){}
}
5.5.泛型方法的设计与使用
5.5.1.什么是泛型方法

1、就是把泛型类型的声明放在某个方法上面,只需将泛型参数列表置于返回值前;只能够本方法上或者内部可以使用这个符号;
2、泛型类,声明的泛型类型可以使用到整个类的
3、泛型方法来说,是再调用方法的时候传入的实际参数为真实的类型

5.5.2.泛型方法的设计与使用
public static T[] getArr(T[] arr){
	//传统想法的错误方法,不能够创建泛型数组
	T[] newArr = new T[arr.length];//在创建数组的时候,必须要知道具体类型的
	System.arraycopy(arr, 0, newArr, 0, arr.length);
	Arrays.sort(newArr);
	return newArr;
}

如果不加泛型: 在类设计上面。因为被static修饰的方法,是在类加载的时候就加载的到静态区中,而这时候加载到jvm中的T类型,JVM根本就不知道是什么类型。

解决方法:在方法上加一个泛型

public static <T> T[] getArr(T[] arr){
	T[] copyOf = (T[])Arrays.copyOf(arr, arr.length, arr.getClass());
	Arrays.sort(copyOf);
	return copyOf;
}
5.6.小结
5.6.1.限制泛型

1)<T extends Collection>这里的限定使用关键字extends,后面可以是类也可以是接口。但这里的extends已经不是继承的含义了,应该理解为T类型是实现Collection接口的类型,或者T是继承了XX类的类型。
2)<T extends SomeClass & interface1 & interface2 & interface3>

5.6.2.通配符泛型

1、如果只指定了<?>,而没有extends,则默认是允许Object及其下的任何Java类了。也就是任意类。
2、通配符泛型不单可以向上限制,如<? extends Collection>,还可以向下限制,如<? super Double>,表示类型只能接受Double及其上层父类类型,如Number、Object类型的实例。
3、泛型类定义可以有多个泛型参数,中间用逗号隔开,还可以定义泛型接口,泛型方法。这些都与泛型类中泛型的使用规则类似。

6.课程总结

6.1.重点

1、Map的内部结构和基本使用
2、Map的遍历方式
3、学习Properties的学习套路
4、泛型的实际开发场景使用

6.2.难点

1、Map的遍历方式
2、结合泛型使用Map进行遍历,尤其是嵌套

7.常见面试题

1、请说说HashSet的实现原理
2、请说说ArrayList和LinkedList的区别
3、请说说你常用的集合框架
4、请说说Hashtable的实现原理

8.课后练习

1、有如下的一个字符串 String str = “AAAABBBBCCCDDDEFF”;
① 请统计出其中每一个字母出现的次数,在控制台打印如下的格式:
A----3
B----4
C----1
D----2
② 请打印出字母次数最多的那一对

9.每日一练

1.请写出for循环的语法结构和执行流程
2.请说出以下数组,哪些是正确的,哪些是错误的
A,float[]=new float[3];
B, float f2[]=new float[];
C, float[] f1=new float[3];
D, boolean[] b={“true”,“false”,“true”};
E, double f4[]={1,3,5};

10.扩展知识

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值