从零开始的java学习Day11----------基础篇(Java中的双列集合、泛型、可变类型)
Map集合
Collection中的集合称为单列集合,Map中的集合称为双列集合
Map中的集合,元素是成对存在的(理解为夫妻)。
每个元素由键与值两部分组成,通过键可以找到所对应的值。
注意:Map中的集合中键不可以重复,但值可以重复;每个键只能对应一个值。(如果添加元素时,键重复了,就会覆盖之前的键值对)
Map常用子类
HashMap<K,V>:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
LinkedHashMap<K,V>:HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
注意:这里的重写是指自定义类的,系统自带的例如:String,Integer就不需要重写
Map接口中的常用方法
put (K key, V value): 把指定的键与指定的值添加到Map集合中,如果添加的时候,键相同,则覆盖相同键的键值对
remove (Object key): 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
get (Object key) 根据指定的键,在Map集合中获取对应的值。
containsKey (Object key): 如果此映射包含指定键的映射关系,则返回 true。
containsValue(Object value): 如果此映射将一个或多个键映射到指定值,则返回 true。
size(): 获取Map集合中键值对的个数
keySet(): 获取Map集合中所有的键,存储到Set集合中。
values(): 获取map集合中所有的值,存到collection集合
entrySet(): 获取到Map集合中所有的键值对对象的集合(Set集合)。
上诉方法字面意思,都很简单,这里着重讲下下面两个方法:
keySet()
该方法可以获取整个Map集合对象里的key值,返回一个set集合的对象,该对象内存储的这个Map对象里的所有Key
格式:
对象名.keySet() 返回一个set对象,可以用现成的来接收,也可以new一个新的set对象来接收
entrySet()
该方法可以把Map对象里的所有键值对封装成一个个Map.Entry接口实现类的对象,再把这个Map.Entry对象存入一个set集合后返回
格式:
对象名.entrySet() 返回一个set对象,该set对象内装着一个个原Map集合里的键值对封装成的Map.Entry接口实现类对象
Map.Entry是Map接口的内部接口,该类中有方法
getKey():获取Entry对象中的键。
getValue():获取Entry对象中的值
遍历Map集合
想要遍历Map集合,有遍历该集合的键或者键值对两种方法
分别采用了上诉的keySet和entrySet方法
方法一:通过keySet方法得到该Map集合的Key(键)的Set集合,再遍历该set集合 ,得到每个Key,执行对象名.get(key)方法,得到该map集合的value(值)。
方法二:通过entrySet()方法得到该Map集合的键值对的set集合,再遍历该set集合,遍历执行Map.entry接口里的方法,getKey()和getValue分别得到键和值
HashMap
存储无序,哈希表结构
LinkedHashMap
存储有序,链表+哈希表结构
注意:他们两个都可以使用上述Map集合的方法,再次强调,如果是自定义的类的对象要存入Map集合,记得要重写hashCode和equals方法
JDK9对集合添加的优化
List,Set,Map,JDK9增加了静态of方法可以快速创建他们的对象,并赋值,但是需要注意的是,该对象创建后就不可以改变了(不能添加或者删除元素)
格式示例:
List<String> l =List.of("Aa","BB"); //创建了类型为List的l对象,并存入了"Aa"和"BB"字符串,下面两个以此类推
Set<String> s =Set.of("Aa","BB");
Map<String,Integer> m =Map.of("Aa",1,"BB",2);
注意:
1:of()方法只是Map,List,Set这三个接口的静态方法,其父类接口和子类实现并没有这类方法,比如 HashSet,ArrayList等
2:返回的集合是不可变的
泛型
- 泛型的意思其实就是一个位置的数据类型的意思一般用来表示,可以在类或方法中预支的使用未知的类型(实际处理参数时再给予具体类型,只支持引用数据类型)
- 定义的时候加<>
- 基本数据类型传入,会自动被装箱成他的包装类
- 泛型没有继承和多态,不符合就是不符合
泛型的好处
1.可以降运行时期的错误,转移到编译时期(提示编译失败)
2.避免了类型强转的麻烦
泛型类(在类上定义泛型)
在定义类的时候,加上(E可以任意改变,里面再使用时前后对应就行),创建对象的时候,传入指定类型,类中使用时,就固定为该类型
整个类可以使用
格式:
public class 类名 <E>{
private E e;
public E fangFa1(E e){}
}
//创建该对象的时候确定泛型的类型,例如这里传入String,那么该对象内的e就会是String对象,fanFa1的返回值类型和形参也会是String类型
泛型方法(在方法上定义泛型)
类似泛型类,在定义方法的时候加上泛型,调用方法的时候,再确定泛型的类型
当前方法可以使用
格式:
public <E> E method(E e){
return e;
}//此方法就使用了泛型,在调用方法时,传入的参数e是什么类型,返回值类型E也会跟着改变
泛型接口(在定义上定义泛型)
同泛型类,在定义接口时加上泛型。
格式:
public interface InteA <E>{
}//定义了一个泛型类,可以在接口中使用未知类型E
泛型接口有两个方式确定泛型的类型
- 1.在定义实现类的时候,确定泛型类型
格式:
public class Inet2A implements InteA<String>{
}这里就代表该接口在该实现类中的泛型类型为String
- 2.在创建对象时,确定泛型类型
格式:
public class Inte3A<E> implements InteA<E>{
}这里代表,创建该实现类的时候也不确定泛型类型,直到创建对象的时候再确定泛型的类型(注意,如果实现类实现接口时不写<>,则接口中使用的泛型类型为Object)
泛型的通配符
- 当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。
- 在实际使用中,不知道使用什么类型来接收泛型的时候,此时可以使用?,?表示未知通配符。但是一旦使用?做类型传递,就不能再使用原对象的方法了(例如集合就不能再往里面存储数据了)。
通配符高级使用—受限泛型
之设置泛型的时候,没有加上限制,实际是任何类型都能接收,但是在Java中,泛型是可以指定一个泛型的上限和下限
泛型的上限:
格式:
类型名称 <? extends 类 > 对象名称
意义: 只能接收该类型及其子类
泛型的下限:
格式:
类型名称 <? super 类 > 对象名称
意义: 只能接收该类型及其父类型
可变参数
在JDK1.5之后,如果我们定义一个方法需要接受多个参数,并且多个参数类型一致,我们可以对其简化成如下格式:
修饰符 返回值类型 方法名(参数类型... 形参名){ }
其实这个书写完全等价于:
修饰符 返回值类型 方法名(参数类型[] 形参名){ }
只是后面这种定义,你必须把参数先存进数组,再传递数组,而前面的,你直接传值进来,系统会自动封装成一个数组,在方法内使用的时候,还是当做一个数组使用
优势:不用创建数组
注意:如果方法有多个参数,参数中包含可变参数,可变参数一定要写在参数列表的末尾位置。