文章目录
(一)认识集合
在开发应用程序时,如果想存储多个同类型的数据,可以使用数组来实现,但是使用数组存在以下一些明显的缺陷:
1.数组的长度固定不变,不能很好的适应元素数量动态变化的情况
2.可通过数组名.length获取数组的长度,却无法直接获取数组中的实际存储的元素个数
3.数组采用在内存中分配连续空间的存储方式存储,根据元素信息查找时效率比较低,需要多次比较
从以上分析可以看出数组在处理一些问题时存在明显的缺陷,针对数组的缺陷,Java提供了比数组更灵活、更实用的集合框架,可大大提高软件的开发效率,并且不同的集合可适用于不同应用场合。
从以上分析可以看出数组在处理一些问题时存在明显的缺陷,针对数组的缺陷,Java提供了比数组更灵活、更实用的集合框架,可大大提高软件的开发效率,并且不同的集合可适用不同应用场合。
Java集合框架提供了一套性能优良、适用方便的接口和类,他们都位于java.util包中。Java集合类主要由Map接口和Collection接口派生而来的,其中Collection接口有两个常用接口,即List接口和Set接口,所以通常说Java集合框架由3大类接口构成(Map接口、List接口和Set接口)。
数组和集合的区别:
A:长度区别:数组的长度固定,集合长度可变。
B:内容不同:数组存储的是同一种类型的元素,而集合可以存储不同类型的元素。
C:元素的数据类型问题:数组可以存储基本数据类型,也可以存储引用数据类型,集合中存储的类型最后转换Object。
Collection:是集合的一个顶级集合
Collection的功能概述:
1.添加功能:
boolean add(Object object) 添加一个元素
boolean addAll(Collection c):添加一个集合
2.删除功能:
void clear():移除所有的元素
boolean remove(Object o):移除元素
boolean removeAll(Collection c)移除一个集合元素
3.判断功能:
boolean contains(Object o) 判断集合中是否包含着指定元素
boolean containsAll(Collection c):判断集合中是否包含指定的集合元素(一个还是所有) boolean isEmpty():判断集合是否为空
4.获取功能
Iterator 对象:循环遍历
5.长度
int size() :元素的个数 面试题:数组中有length,字符串有,集合中有没有?没有
6.交集功能
boolean retainAll(Collection c):两个集合都有的元素?返回的boolean值又是什么意思
7.把集合转换为数组
Object[] toArray()
(二)List接口
Collection接口是最基本的集合接口,可以存储一组不唯一、无序的对象。List接口继承自Collection接口,是有序集合。用户可使用索引访问List接口中的元素,类似于数组。List接口允许存放重复元素,也就是List可以存储一组不唯一、有序的对象。
List接口常用的实现类有ArrayList和LinkedList。
List集合的特有功能
A.添加元素的功能
void add(int index,Object object):在指定的位置添加元素
B.获取功能
Object get(int index):获取指定位置的元素
C.列表迭代器
LisIterator LisIterator():List集合中特有的迭代器
D.删除功能
Object remove(int index):根据索引删除元素,返回被删除的元素
E:修改功能
Object set(int index, Object element):根据索引修改元素,返回被修改的元素
ArrayList类
针对数组的一些缺陷,Java集合框架提供了ArrayList集合类,对数组进行了封装,实现了长度可变的数组,而且和数组采用相同的存储方式,在内存中分配了连续的空间,如下图1.2(画图),所以,经常称ArrayList为动态数组。但是它不等同于数组,ArrayList集合中可以添加任何类型的数据,并且添加的数据都将转换成Object类型,而在数组中只能添加同一个数据类型的数据。
ArrayList类提供了很用于操作数据,如表中列出的是ArrayList类的常用方法。
ArrayList类的常用方法:
方法 | 说明 |
---|---|
boolean add(Object o) | 在列表的末尾添加元素o,起始索引位置从0开始 |
void add(int index,Object o) | 在指定的索引位置添加元素o,在索引位置必须介于0和列表中元素个数之间 |
int size() | 返回列表中的元素个数 |
Object get(int index) | 返回指定索引位置处的元素,取出的元素是Object类型,使用前需要进行强制类型转换 |
void set(int index,Object obj) | 将index索引位置的元素替换为obj元素 |
boolean contains(Object o) | 判断列表中是否存在指定元素o |
int indexOf(Object obj) | 返回元素在集合出现的索引位置 |
方法 | 说明 |
---|---|
boolean remove(Object o) | 从列表中删除元素o |
Object remove(int index) | 从列表中删除指定位置的元素,起始索引位置从0开始 |
示例1:
使用ArrayList常用方法动态操作数据
实现步骤如下:
1).导入ArrayList对象,并添加数据
2)创建ArrayList对象,并添加数据
3)判断集合中是否包含某个元素
4)移除索引为0的元素
5)把索引为1的元素替换为其他元素
6)输出某个元素所在的索引位置
7)清空ArrayList集合中的数据
8)判断ArrayList集合中是否包含数据
public static void main(String[] args) {
ArrayList list =new ArrayList(); //1
list.add("张三丰");
list.add("郭靖");
list.add("杨过");
//判断集合中是否包含“李莫愁”
System.out.println(list.contains("李莫愁"));
//把索引为0的数据移除,对应步骤(4)
list.remove(0); //2
System.out.println("------------");
list.set(1,"黄蓉"); //3
for (int i=0;i<list.size();i++){ //4
String name =(String)list.get(i);
System.out.println(name);
}
System.out.println("------");
System.out.println(list.indexOf("小龙女")); //5
list.clear(); //清空list中的数据
System.out.println("------------");
for (Object object:list){
String name = (String) object;
System.out.println(name);
}
System.out.println(list.isEmpty()); //7
}
分析:
在示例1中,1的代码调用ArrayList的无参构造方法,创建集合对象。常用的ArrayList类的构造方法还有一个带参数的重载版本,即ArrayList(int initialCapacity),它构造一个具有指定初始容量的空列表。
在2的代码将list集合中索引为0的元素删除,list集合的下标是从0开始,也就是删除了"张三丰",集合中现有元素为"郭靖"和“杨过”。
在3的代码将list集合中索引为1的元素替换为"黄蓉",即将"杨过"替换为"黄蓉",集合中现有元素为"郭靖"和"黄蓉"。
在4的代码是使用for循环遍历集合,输出集合中所有的元素。list.get(i)取出集合中索引为i的元素,并强制转换为String类型。
在5的代码为输出元素"小龙女"所在索引位置,因集合中没有该元素,所以输出结果为-1.
在6的代码块是使用增强for循环遍历集合,输出集合中没有该元素。增强for循环的语法在Java基础课程中讲过,这里不再赘述。可以看出,遍历集合时使用增强for循环比普通for循环在写法上更加简单方便,而且不用考虑下标越界的问题。
在7的代码来判断list集合是否空,因为前面执行了list.clear()操作,所以集合已经为空,输出true。
注意:
1.调用ArrayList类的add(Object obj)方法,添加到集合当中的数据将被转换为Object类型
2.使用ArrayList类之间,需要导入相应的接口和类,代码如下
import.java.util.ArrayList
import.java.util.ArrayList
示例2:
需求:使用ArrayList集合存储新闻标题信息(包含ID、名称、创建者),输出新闻标题的总数量及每条新闻标题的名称
实现步骤:
1).创建ArrayList对象,并添加数据
2).获取新闻标题的总数
3)遍历集合对象,输出新闻标题名称
1.创建一个NewTitle类
public class NewTitle {
private int id;
private String name;
private String createName;
//省略getter等方法
2.实现代码
public static void main(String[] args) {
//创建一个新闻标题对象,
NewTitle car = new NewTitle(1, "汽车", "管理员");
NewTitle test = new NewTitle(2, "高考", "管理员");
List list =new ArrayList();
//按照顺序依次添加新闻标题
list.add(car);
list.add(test);
//获取新闻标题的总数
System.out.println("新闻标题数目为:"+list.size()+"条");
System.out.println("新闻的标题名称为");
//遍历集合
for (Object object:list){
NewTitle tile = (NewTitle) object;
System.out.println(tile.getName());
}
}
分析:
在示例2中,ArrayList集合中存储的是新闻标题对象。在ArrayList集合中可以存储任何类型的对象。其中代码 List list =new ArrayList();是将接口List的引用指向实现类ArrayList的对象。在编程中将接口的引用指向实现类的对象是Java实现多态的一种形式,也是软件开发中实现低耦合的方式以,这样的用法可以大大提高程序的灵活性。随着编程经验的积累,开发者对这个用法的理解会逐步加深。
ArrayList集合因为可以使用索引来直接获取元素,所以优点是遍历元素和随机访问元素的效率比较高。但是由于ArrayList集合采用了和数组相同的存储方式,在内存中分配连续的空间,因此在添加和删除非尾部元素时会导致所有元素的移动,这就造成在插入、删除等操作频繁的应用场景下使用ArrayList会导致性能低下。所以数据操作频繁,最好使用LinkedList存储数据
Linkedlist类
LinkedList类是List接口的链接列表实现类。它支持实现所有List接口可选的列表的操作,并且允许元素值是任何数据,包括null。
LinkedList类采用链表存储方式存储数据,如图下(画图),优点在于插入、删除元素时效率比较高,但是LinkedList类的查找效率很低
它除了包含ArrayList类所包含的方法外,还提供了如下表的一些方法,可以在LinkedList类的首部或尾部进行插入、删除操作。
方法 | 说明 |
---|---|
void addFirst(Object obj) | 将指定元素出入到当前集合的首部 |
void addLast(Object obj) | 将指定元素插入到当前集合的尾部 |
Object getFirst() | 获得当前集合的第一个元素 |
Object getLast() | 获得当前集合的最后一个元素 |
Object removeFirst() | 移除并返回当前集合的第一个元素 |
Object removeLast() | 移除并返回当前集合的最后一个元素 |
示例3:
需求:
使用LinkedList集合存储新闻标题(包含ID、名称、创建者),实现获取、添加及删除头条和末条新闻标题信息功能,并遍历集合
实现步骤如下:
1.创建LinkedList对象,并添加数据
2.添加头条和末条新闻标题
3.获取头条和末条新闻标题信息
4.删除头条和末条新闻标题
(1)借用上面案例的NewTiltle的业务模型
(2)实现需求
public static void main(String[] args) {
//创建一个新闻标题对象,
NewTitle car = new NewTitle(1, "汽车", "管理员");
NewTitle test = new NewTitle(2, "高考", "管理员");
List list =new ArrayList();
//按照顺序依次添加新闻标题
list.add(car);
list.add(test);
//获取新闻标题的总数
System.out.println("新闻标题数目为:"+list.size()+"条");
System.out.println("新闻的标题名称为");
//遍历集合
for (Object object:list){
NewTitle tile = (NewTitle) object;
System.out.println(tile.getName());
}
}
除了表1-2中列出的LinkedList类提供的方法外,LinkedList类和ArrayList类所包含的大部分方法都是一样的,主要的原因是因为他们都是List接口的实现类。由于ArrayList采用和数组一样的连续的顺序存储方法,当对数据频繁检索时效率较高,而LinkedList类采用链表存储方式,当数据添加,删除或修改比较多时,建议选择LinkedList类存储数据
LinkedList解答
1.什么是链表结构
链表是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每个节点里存到下一个节点指针(标志、路标)
链表分类:
单向链表
双向链表
队列:Queue接口:
存储特点:先进先出
Queue的子接口Deque接口:
也是线型表的一种,称之为双端队列,两端都支持存储操作
ArrayList和LinkedList区别
1.底层数据结构:ArrayList底层使用的是数组;LinkedList底层使用的是双向链表;
2.插入和删除元素操作:ArrayList采用的是数组存储,所以插入和删除元素是跟元素的位置有关系。LinkedList采用的是链表存储,删除元素是不受元素位置影响的;如果是要在指定位置i插入和删除的话((add(int index,E 因为需要先移动再插入。
3.随机访问:ArrayList对于随机元素访问的效率明显比LinkedList高。随机访问就是通过元素的索引来获取元素(也就是set和get(int index)方法)。
4.线程不安全:ArrayList和LinkedList都是不同步的,也就是说都是线程不安全的。
5.接口实现:,而LinkedList实现了Deque可以当做队列使用
(三)Set接口
Set接口是Collection接口另外一个常用子接口,Set接口描述的是一种比较简单的集。集合中的对象并不按特定的方式排序,并且不能保存重复的对象,也就是说Set接口可以存储一组唯一、无序的对象。
Set接口常用的实现类有HashSet。
HashSet类
加入现在需要很多数据总查找某个数据,LinkedList类就无需考虑了,它的数据结构决定了它的查找效率低下。如果使用ArrayList类,在部知道数据的索引且需要全部遍历的情况下,效率一样很低下。为此Java集合框架提供了一个查找效率高的集合类HashSet。HashSet类实现了Set接口,是使用Set集合时最常用的一个实现类。HashSet。HashSet类实现了Set接口,是使用Set集合时最常用的一个实现类。
HashSet集合的特点:
1).集合内的元素是无序排列
2).HashSet类是非线程安全
3)允许集合元素为null。
HashSet类的常用方法
方法 | 说明 |
---|---|
boolean add(Object o) | 如果Set中尚未包含指定元素o,则添加指定元素o |
void clear() | 从set中移除所有的元素 |
int size() | 返回Set中的元素的数量(Set的容量) |
boolean isEmpty() | 如果Set不包含任何元素,则返回true |
boolean contains(Object o) | 如果Set包含指定元素o,则返回true |
boolean remove(Object o) | 如果指定元素o存在于set中,则将移除 |
示例4
需求:使用HashSet类的常用方法存储操作新闻标题信息,并返回遍历集合
实现步骤:
(1).创建HashSet对象,并添加数据
(2).获取新闻标题的总数
(3).判断集合中是否包含汽车新闻标题
(4).移除对象
(5).判断集合是否为空
(6).遍历集合
public static void main(String[] args) {
//创建一个新闻标题对象,
NewTitle car = new NewTitle(1, "汽车", "管理员");
NewTitle test = new NewTitle(2, "高考", "管理员");
//创建存储新闻标题的集合对象
Set newsTitle = new HashSet();
//按照顺序依次添加新闻标题
newsTitle.add(car);
newsTitle.add(test);
//获取新闻标题的总数
System.out.println("新闻标题数目为:"+newsTitle.size()+"条");
//判断集合中是否含汽车新闻标题
System.out.println("汽车新闻是否存在:"+newsTitle.contains(car));
//移除对象
newsTitle.remove(test);
System.out.println("教育对象删除");
System.out.println("集合是否为空:"+newsTitle.isEmpty());
//遍历所有的新闻标题
System.out.println("遍历所有的新闻标题");
for (Object object:newsTitle){
NewTitle title = (NewTitle) object;
System.out.println(title.getName());
}
}
存储自定义对象,并且保证元素的唯一性
HashSet底层依赖的是hashCode()和equals()方法。
而这两个方法我们在自定义类中没有重写,所以,默认使用的是Object类。
这个时候,他们的哈希值是不会一样的,根本就不会继续判断,执行了添加操作。
要在自定义存储的类中覆写,hashCode()和equals(),可以解决。
下面学生类有name,age属性,重写hashcode,equals。重写后,元素就是唯一的。
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Student)) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
LinkedHashSet
LinkedHashSet 是HashSet子类
1.底层的数据结构:
数组+双向链表/红黑树(当链表节点数>8同时数组元素>=64时,会把链表变成红黑树)
2.查询速度快,增删速度也不慢
3.无索引:指的是不能通过索引来获得元素
4.元素不可重复:依赖所属类的hashCode()和equals()方法
5.线程不安全,不同同步,但是效率高
6有序的:怎么存就是怎么取(因为加了一个链表)
(四)使用迭代器遍历集合
Iterator iterator():迭代器,集合的专用遍历方式
Object next():获取元素,并移动到下个位置
boolean hasNext():如果仍有元素,则返回true
Collection list = new ArrayList();
list.add("apple");
list.add("banana");
list.add("lemon");
Iterator iterator = list.iterator();
while (iterator.hasNext()){
String a=(String)iterator.next();
System.out.println(a);
}
//创建集合Collection,多态
Collection c=new ArrayList();
//创建学生
Student s1=new Student();
s1.setName("周杰伦");
s1.setAge(38);
Student s2 =new Student("刘德华",58);
Student s3=new Student("周润发",58);
Student s4=new Student("林明",58);
Student s5=new Student("郭富城",50);
//学生添加到集合中
c.add(s1);
c.add(s2);
c.add(s3);
c.add(s4);
c.add(s5);
//遍历
//首先获得迭代器对象
Iterator it = c.iterator();
while (it.hasNext()){
//进行强制类型转换,把Object对象转换为Student;
Student student =(Student)it.next();
System.out.println("学生的姓名:"+student.getName()+"学生的姓名:"+student.getAge());
}
(五)Map接口
Map接口存储一组成对的键(key)——值(value)对象,提供key到value的映射,通过key来检索。Map接口中的key不要求有序,不允许重复。value同样不要求有序,但允许重复
方法 | 说明 |
---|---|
Object put(Object key,Object value) | 将相互关联的一个key与value放入该集合中,如果 此Map接口中已经包含了Key对应的Value,则旧值将被替换 |
Object remove(Object key) | 从当前结合中移除与指定key相关的映射 并返回该key关联的旧Value,如果Key没有关联,则返回null |
Object get(Object key) | 获得与key相关的value ,如果该key不关联任何非null值,则返回null |
boolean contains(Object key) | 判断集合中是否存在这个key |
boolean isEmpty() | 判断集合中是否存在元素 |
void clear() | 清除集合中所有的元素 |
int size() | 返回集合中元素的数量 |
Set keySet() | 获得所有key的集合 |
Collection values() | 获取所有value的集合 |
Map接口中存储的数据都是键——值对,例如,一个身份证号码对应一个人,其中身份证号码就是key,与此号码对应的人都是value。
使用HashMap动态存储数据
常用的map的实现类HashMap,其优点是查询指定元素效率高
(六)Collections类
Collections类是Java提供的一个集合操作工具类,它包含了大量的静态的方法,实现了对集合元素的排序、查找和替换操作
注意:Collections和Collection是不同的,前者是集合的操作类,后者是集合的接口
对集合元素排序与查找
排序是针对集合的一个常见需求。要排序就要知道两个元素哪个大哪个小。在Java中,如果想实现一个类的对象之间比较大小,那么这个类就要实现Comparable。此接口强行对实现它的每个类的对象进行整体排序。这种整体排序被称为类的自然排序,类的comparaTo()方法被称为它的自然比较方法。此方法用于比较此对象与指定对象的顺序,如果该对象小于、大于或等于指定对象,则分别返回负整数、零或正整数。
CompareTo()方法的定义语法格式如下:
int compareTo(Object obj);
其中:
参数:obj即要比较的对象
返回值: 负整数、零或整数,根据此对象的大小,等于还是大于指定对象返回不同的值
实现此接口的对象列表(和数组)可以通过Collections.sort()方法(和Arrays.sort()方法)进行自动排序
示例8:
学生类Student实现了Comparable接口,重写了compareTo()方法,通过比较学号实现对象之间的大小比较
实现步骤:
(1)创建Student类
(2)添加属性学号number(int)、姓名name(String)和性别gender(String)
(3)实现Comparable接口、compareTo()方法
Student类
public class Student implements Comparable{
private int number=0; //学号
private String name=""; //姓名
private String gender=""; //性别
//省略set和get方法,有参构造和无参构造
//实现comparable接口重写comparableTo方法
@Override
public int compareTo(Object obj) {
Student student = (Student) obj;
//如果学号相同,那么两者就是相等的
if (this.number==student.getNumber()) {
return 0;
}else if (this.number>student.getNumber()) { //如果这个学生的学号大于传入学生的学号
return 1;
}else {
return -1;
}
}
}
分析:
元素之间可以比较大小之后,就可以使用Collections类的sort()方法对元素进行排序操作了,前面介绍过List接口和Map接口,Map接口本身是无序的,所以不能对Map接口做排序操作;但是List接口是有序的,所以可以对List接口进行排序。注意List接口存放的元素,必须是实现Comparable接口的元素才可以
示例9
使用Collections类的静态方法sort()和binarySearch()对List集合进型排序与查找
实现步骤:
(1)导入相关类
(2)初始化数据
(3)遍历排序前的集合并输出
(4)使用Collection类的sort()方法排序
(5)遍历排序后集合并输出
(6)查找排序后某元素的索引
public static void main(String[] args) {
//先创建4个学生,对其学号设值
Student student = new Student();
student.setNumber(1);
Student student1 =new Student();
student1.setNumber(2);
Student student2 =new Student();
student2.setNumber(3);
Student student3 =new Student();
student3.setNumber(4);
//创建集合,把学生添加到集合中
ArrayList list =new ArrayList();
list.add(student2);
list.add(student3);
list.add(student);
list.add(student1);
System.out.println("---排序前----");
Iterator iterator = list.iterator();
while (iterator.hasNext()){
Student stu= (Student) iterator.next();
System.out.println(stu.getNumber());
}
//使用Collections类的sort()方法对list集合进行排序
System.out.println("----排序后----");
Collections.sort(list);
Iterator iterator1 =list.iterator();
while (iterator1.hasNext()){
Student stu1= (Student) iterator1.next();
System.out.println(stu1.getNumber());
}
//使用Collection类的binarySearch()方法对list集合进行查找
int index =Collections.binarySearch(list,student); //1
System.out.println("student3的索引值:"+index);
}
分析:1处的代码使用Collections类的binarySearch()方法对list集合进行查找,因为studnet的学号是1,所以索引值是0.
替换集合元素
若有个需求,需要把一个list集合中所有的元素换为相同的元素,则可以使用Collections类的静态方法fill()来实现。
示例10:
使用Collections类的静态方法fill()替换List集合中所有元素为相同的元素。
实现步骤如下:
(1)导入相关类,初始化数据
(2)使用Collections类的fill()方法替换集合中的元素
(3)遍历输出替换后的集合
public static void main(String[] args) {
List list =new ArrayList();
list.add("张三丰");
list.add("杨过");
list.add("郭靖");
Collections.fill(list,"东方不败"); //此次替换是把所有的元素替换为同一个元素
Iterator iterator = list.iterator();
while (iterator.hasNext()){
String name= (String) iterator.next();
System.out.println(name);
}
//list集合删除指定元素位置,再在指定位置添加元素。
list.remove(0);
list.add(0,"杨过");
System.out.println(list);
}
(七)泛型
泛型是JDK1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数,使代码可以应用于多种类型。简单来说,Java语言引入泛型的好处是安全简单的,且所强制转换都是自动和隐士进行的,提高了代码的重用率。
1.泛型的定义
将对象的类型作为参数,指定到其他类或者方法上,从而保证类型转换的安全性和稳定性,这就是泛型。泛型的本质就是参数化类型。
泛型的定义语法格式如下:
类1 或者接口 <类型实参> 对象 =new 类2<类型实参>();
注意:
首先,“类2”可以是"类1"本身,可以是"类1"的子类,还可以是接口的实现类;其次“类2”的类型实参必须与“类1”中的类型实参相同。
例如:
ArrayList<String> list =new ArrayList<String>();
上述代码表示创建一个ArrayLsit集合,但规定该集合中的存储的元素类必须为String类型
2.泛型在集合中的应用
前面学习List接口时已经提到,其add()方法的参数是Object类型,不管把什么对象放入List接口及其子接口或实现类中,都会被转换为Object类型。在通过get()方法取出集合中元素时必须进行强制类型转换,不仅繁琐而且容易出现ClassCastException异常。Map接口中使用put方法和get()方法存取对象时,以及使用Iterator的next()方法获取元素时存在同样问题。JDK1.5中拖过引入泛型有效解决了这个问题。JDK1.5中已经改写了集合框架中所有的接口和类,增加了对泛型的支持,也就是泛型集合。
背景示例
public static void main(String[] args) {
ArrayList arrayList =new ArrayList();
arrayList.add("jack");
arrayList.add(true);
for (int i = 0; i < arrayList.size(); i++) {
Object o = arrayList.get(i);
String s= (String) o;
System.out.println(s);
}
}
运行结果:报出ClassCastException
使用泛型集合在创建集合对象时指定集合中元素的类型,从集合中取出元素时无需进行强制类型转换,并且如果把非指定集合中元素的类型,从集合中取出元素时无需进行强制类型转换,并且如果非指定类型对象放入集合,会出现编译错误。
List和ArrayList的泛型形式是List和ArrayList,ArrayList与ArrayList类常用方法基本一样,示例11演示了List和ArrayList的用法。
示例11
使用ArrayList的泛型形式改进示例2
实现步骤如下:
public static void main(String[] args) {
//创建一个新闻标题对象,
NewTitle car = new NewTitle(1, "汽车", "管理员");
NewTitle test = new NewTitle(2, "高考", "管理员");
ArrayList<NewTitle> list =new ArrayList<>();
list.add(car);
list.add(test);
Iterator<NewTitle> iterator = list.iterator();
while (iterator.hasNext()){
NewTitle newTitle = iterator.next();
System.out.println("新闻的标题的名称:"+newTitle.getName());
}
}
分析:
示例11中通过指定了ArrayList中元素的类型,代码中指定了ArrayList中只能添加NewTitle类型的数据,如果添加其他类型数据,将会出现编译错误,ArrayList中只能添加NewTitle类型的数据,如果添加到集合中后不再转换为Object类型,这在一定程度上保证了代码安全性,并且数据添加到集合后不再转为Object类型,保存的是指定的数据类型,所以在集合中获取数据时也再不需要进行强制类型转换。
同样的,Map与HashMap也它们的泛型形式,如:Map<K,V>和HashMap<K,V>。因为他们的每个元素都包含两个部分,即key,和value,所以,在应用泛型时,要同时指定key的类型和value的类型,K表示key的类型,V表示value类型。
HashMap<K,V>操作数据的方法与HashMap基本一样。
示例12:
使用HashMap的泛型形式改进示例7
public static void main(String[] args) {
//创建学员对象
Student student1 =new Student("李明","男");
Student student2 =new Student("刘丽","女");
HashMap<String, Student> map = new HashMap<>();
map.put("Jack",student1);
map.put("Rose",student2);
for (String key:map.keySet()){
System.out.println(key);
}
for (Student student:map.values()){
System.out.println(student.getName()+",性别是"+student.getSex());
}
}
分析:
通过<String,Student>指定了Map集合的数据类型,在使用put()方法存储数据时,Map集合的key必须为String类型,value必须为Student类型的数据,而在遍历键集的for循环中,变量key的类型不再是Object,而是String;在遍历值集的for循环中,变量value类型不再是Object,而是Student,同样,Map.get(key)得到的值也是Student类型数据,不再需要进行强制类型转换。
当然,其他的集合类,如前面讲到的LinkedList、HashSet等都有自己的泛型形式,用法和ArrayLsit、HashMap的泛型形式类似。
泛型使集合的使用更方便,也提升了安全:
1.存储数据时进行严格类型检查,确保只有合适类型的对象才能存储在集合中。
2.从集合中检索对象时,减少了强制类型转换。
3.深入泛型
在集合中是使用泛型只是泛型多种应用的一种,在接口、类、方法等方面也有着泛型的广泛应用。
泛型的本质就是参数化类型,参数化类型的重要性在于允许创建一些类、接口和方法,其所操作的数据类型被定义为参数,可以在真正使用时指定具体类型。
1.参数化类型:参数化类型包含一个类或者接口,以及实际的类型参数列表
ArrayList<String> list = new ArrayList<>();
//ArrayList<String>这个就是参数化类型,ArrayList就是原生类型
参数类型包含接口的:就是指参数化类型中是接口
2.类型变量:是一种非限定性标识符,用来指定类、接口或者方法的类型
比如常用:T(Java的类型)、E(表示元素)、K、V
泛型类
泛型类简单的说就是一个或者多个类型参数的类
定义泛型类的语法格式如下:
访问修饰符 class className
TypeList表示类型参数列表,每个类型变量之间以逗号分隔。
例如:
* T表示类型的形参
* T是用于创建对象时来指定具体的数据类型呢
* T是由外部使用类时来指定的
*/
public class Generic<T> {
private T key;
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
public Generic(T key) {
this.key = key;
}
@Override
public String toString() {
return "Generic{" +
"key=" + key +
'}';
}
}
创建泛型类实例的语法格式如下:
new className(argList)
a.TypeList表示定义的类型参数列表,每个类型变量之间以逗号分隔。(JDK1.7后可以省略)
b.argList表示实际传递的类型参数列表,每个类型变量之间同样以逗号隔开
从泛型类派生子类
子类也是泛型类,子类和父类的泛型类型要一致
class ChildGeneric<T> extends Generic<T>
泛型接口
语法格式:
interface 接口名称 <泛型标识,泛型标识,...>{
泛型标识 方法名称();
}
泛型接口的使用:
1.如果实现类不是泛型类,接口要明确数据类型。
2.如果实现类也是泛型类,实现类和接口的泛型类型要一致。
泛型方法
泛型类,是在实例化类的时候指明泛型
泛型方法,是在调用方法的时候指明泛型的具体类型
语法:
修饰符<T,E,…> 返回值类型 方法名(形参列表){
方法体;
}
1).public与返回值中间非常重要,可以理解为声明此方法为泛型方法
2).只有声明的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法
3).标明该方法将使用泛型T,此时才可以在方法中使用泛型类型T
4).与泛型的定义一样,此处的可以随便写为任意标识,常见的如T,E,K,V 等形式的参数常用于表示泛 型
5).泛型类中的普通的成员方法不支持静态关键字static修饰,泛型方法支持。
泛型方法的可变参数:
语法格式:
public <E> void print(E...e){
for(E e1:e){
System.out.println(e);
}
}
泛型方法的总结:
泛型方法使方法独立于类而产生变化。
如果使用static方法使用泛型能力,就必须使其称为泛型方法。
类型通配符
类型通配符一般使用“?”代替具体的类型是实参,所以类型通配符是类型实参,而不是类型的形参。
通配符的上限
语法格式:
类/接口 <? entends 实参类型>
要求该泛型的类型,只能是实参类型,或实参类型的子类型。
类型通配符的下限
语法:
类/接口 <? super 实参类型>
要求该泛型的类型,只能是实参类型,或实参类型的父类类型