Java高级特性
第一章集合框架和泛型
技能目标:
会使用List接口及实现类
会使用Map接口及实现类
掌握Iterator接口的使用
了解泛型类、泛型接口使用
1.1认识集合
在开发应用程序时,如果想存储多个同类型的数据,可以使用数组来实现,但是使用数组存在以下一些明显的缺陷:
1.数组的长度固定不变,不能很好的适应元素数量动态变化的情况
2.可通过数组名.length获取数组的长度,却无法直接获取数组中的实际存储的元素个数
3.数组采用在内存中分配连续空间的存储方式存储,根据元素信息查找时效率比较低,需要多次比较
从以上分析可以看出数组在处理一些问题时存在明显的缺陷,针对数组的缺陷,Java提供了比数组更灵活、更实用的集合框架,可大大提高软件的开发效率,并且不同的集合可适用于不同应用场合。
从以上分析可以看出数组在处理一些问题时存在明显的缺陷,针对数组的缺陷,Java提供了比数组更灵活、更实用的集合框架,可大大提高软件的开发效率,并且不同的集合可适用不同应用场合。
Java集合框架提供了一套性能优良、适用方便的接口和类,他们都位于java.util包中,其中主要内容及彼此之间的关系如下:(画图)
从上图可以看出,Java集合类主要由Map接口和Collection接口派生而来的,其中Collection接口有两个常用接口,即List接口和Set接口,所以通常说Java集合框架由3大类接口构成(Map接口、List接口和Set接口)。本章内容就是围绕这个3大类接口进行的。
注意:
虚线框表示接口或抽象类,实现框表示开发中常用的实现类。
1.2List接口
Collection接口是最基本的集合接口,可以存储一组不唯一、无序的对象。List接口继承自Collection接口,是有序集合。用户可使用索引访问List接口中的元素,类似于数组。List接口允许存放重复元素,也就是List可以存储一组不唯一、有序的对象。
List接口常用的实现类有ArrayList和LinkedList。
1.使用ArrayList类动态存储数据
针对数组的一些缺陷,Java集合框架提供了ArrayList集合类,对数组进行了封装,实现了长度可变的数组,而且和数组采用相同的存储方式,在内存中分配了连续的空间,如下图(画图),所以,经常称ArrayList为动态数组。但是它不等同于数组,ArrayList集合中可以添加任何类型的数据,并且添加的数据都将转换成Object类型,而在数组中只能添加同一个数据类型的数据。
ArrayList类提供了很用于操作数据,如表1-1中列出的是ArrayList类的常用方法
表1-1 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集合中是否包含数据
package Test1;
import java.util.ArrayList;
/**
* @作者:Xem626
* @date: 2022/7/12 19:30
* @TODO
*/
public class Test1 {
public static void main(String[] args) {
/*
调用了ArrayList无参的构造方法,创建了集合对象,常用的ArrayList类的构造方法
还有一个带参数的重载版本,即ArrayList(int initialCapacity),
他构造一个具有指定容量的空列表
*/
ArrayList list=new ArrayList<>();
list.add("艾琳");
list.add("后羿");
list.add("守约");
//判断集合是否含有江南
boolean a=list.contains("江南");
System.out.println(a);
//把索引为0的元素移除
Object remove=list.remove("艾琳");
System.out.println("-----------------------");
//替换元素
list.set(0,"路明非");
for(int i=0;i<list.size();i++){
//取出来的数据是object类型,如需要使用,需要强制类型转换
String string= (String) list.get(i);
System.out.println(string);
}
System.out.println("------------------");
System.out.println(list.indexOf("路明非"));
//清空list元素
list.clear();
System.out.println("------------------");
//使用增强for循环比普通循环更简单,不需要考虑下标越界
for(Object object:list){
String name= (String) object;
System.out.println(name);
}
}
}
分析:
在示例1中,1的代码调用ArrayList的无参构造方法,创建集合对象。常用的ArrayList类的构造方法还有一个带参数的重载版本,即ArrayList(int initialCapacity),它构造一个具有指定初始容量的空列表。
在2的代码将list集合中索引为0的元素删除,list集合的下标是从0开始,也就是删除了"艾琳",集合中现有元素为"后羿"和“守约”。
在3的代码将list集合中索引为0的元素替换为"路明非",即将"后羿"替换为"路明非",集合中现有元素为"路明非"和"守约"。
在4的代码是使用for循环遍历集合,输出集合中所有的元素。list.get(i)取出集合中索引为i的元素,并强制转换为String类型。
在5的代码为输出元素"零"所在索引位置,因集合中没有该元素,所以输出结果为-1.
在6的代码块是使用增强for循环遍历集合,输出集合中没有该元素。增强for循环的语法在Java基础课程中讲过,这里不再赘述。可以看出,遍历集合时使用增强for循环比普通for循环在写法上更加简单方便,而且不用考虑下标越界的问题。
在7的代码来判断list集合是否空,因为前面执行了list.clear()操作,所以集合已经为空,输出啥也没有。
注意:
1.调用ArrayList类的add(Object obj)方法,添加到集合当中的数据将被转换为Object类型
2.使用ArrayList类之间,需要导入相应的接口和类,代码如下
import.java.util.ArrayList
例2:
需求:使用ArrayList集合存储新闻标题信息(包含ID、名称、创建者),输出新闻标题的总数量及每条新闻标题的名称
实现步骤:
1).创建ArrayList对象,并添加数据
2).获取新闻标题的总数
3)遍历集合对象,输出新闻标题名称
1.创建一个NewTitle类
package Test1;
/**
* @作者:Xem626
* @date: 2022/7/12 20:15
* @TODO
*/
public class NewTitle {
private int Id;
private String name;
private String create;
public int getId() {
return Id;
}
public void setId(int id) {
Id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCreate() {
return create;
}
public void setCreate(String create) {
this.create = create;
}
public NewTitle() {
}
public NewTitle(int id, String name, String create) {
this.Id = id;
this.name = name;
this.create = create;
}
}
2.实现代码
package Test1;
import java.util.ArrayList;
import java.util.List;
/**
* @作者:Xem626
* @date: 2022/7/12 20:17
* @TODO
*/
public class Test2 {
public static <newTitle> void main(String[] args) {
NewTitle newTitle = new NewTitle(1, "汽车", "管理员");
NewTitle newTitle1 = new NewTitle(2, "高考", "管理员");
//多态
//List list=new ArrayList();
ArrayList arrayList=new ArrayList();
arrayList.add(newTitle);
arrayList.add(newTitle1);
//获取新闻的标题总数
System.out.println("获取新闻标题总数:"+arrayList.size());
System.out.println("新闻标题为:");
for(Object object:arrayList){
//取出来的东西是NewTitle的object类型
NewTitle newTitle2=(NewTitle) object;
System.out.println(newTitle2.getName());
}
}
}
分析:
在示例2中,ArrayList集合中存储的是新闻标题对象。在ArrayList集合中可以存储任何类型的对象。其中代码 List list =new ArrayList();是将接口List的引用指向实现类ArrayList的对象。在编程中将接口的引用指向实现类的对象是Java实现多态的一种形式,也是软件开发中实现低耦合的方式以,这样的用法可以大大提高程序的灵活性。随着编程经验的积累,开发者对这个用法的理解会逐步加深。
ArrayList集合因为可以使用索引来直接获取元素,所以优点是遍历元素和随机访问元素的效率比较高。但是由于ArrayList集合采用了和数组相同的存储方式,在内存中分配连续的空间,因此在添加和删除非尾部元素时会导致所有元素的移动,这就造成在插入、删除等操作频繁的应用场景下使用ArrayList会导致性能低下。所以数据操作频繁,最好使用LinkedList存储数据
2.使用LinkedList类动态存储数据
LinkedList类是List接口的链接列表实现类。它支持实现所有List接口可选的列表的操作,并且允许元素值是任何数据,包括null。
LinkedList类采用链表存储方式存储数据,优点在于插入、删除元素时效率比较高,但是LinkedList类的查找效率很低
它除了包含ArrayList类所包含的方法外,还提供了如下表的一些方法,可以在LinkedList类的首部或尾部进行插入、删除操作。
表1.2
方法 | 说明 |
---|---|
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)实现需求
package Test1;
import java.util.LinkedList;
/**
* @作者:Xem626
* @date: 2022/7/12 20:51
* @TODO
*/
public class Test3 {
public static void main(String[] args) {
LinkedList list =new LinkedList();
NewTitle newTitle = new NewTitle(1, "汽车", "管理员");
NewTitle newTitle1 = new NewTitle(2, "高考", "管理员");
list.add(newTitle);
list.add(newTitle1);
NewTitle newTitle2=new NewTitle(3,"娱乐","管理员");
list.addFirst(newTitle2);
NewTitle newTitle3=new NewTitle(4,"体育","管理员");
list.addLast(newTitle3);
System.out.println("获取新闻标题总数:"+list.size());
System.out.println("新闻标题为:");
for(Object object:list){
NewTitle title=(NewTitle) object;
System.out.println(title.getName());
}
System.out.println("------------------");
//获取当前集合的第一个元素
Object first= list.getFirst();
NewTitle first1=(NewTitle) first;
System.out.println(first1.getName());
//获取尾部元素
Object last=list.getLast();
NewTitle last1=(NewTitle) last;
System.out.println(last1.getName());
System.out.println("------------------------");
//移除并返回第一个元素
Object o=list.removeFirst();
NewTitle o1=(NewTitle) o;
System.out.println(o1.getName());
System.out.println("------------------");
for(Object o2:list){
NewTitle title=(NewTitle) o2;
System.out.println(title.getName());
}
System.out.println("--------------------");
//移除并返回最后一个元素
Object o3=list.removeFirst();
NewTitle o4=(NewTitle) o3;
System.out.println(o4.getName());
System.out.println("------------------");
for(Object o5:list){
NewTitle title=(NewTitle) o5;
System.out.println(title.getName());
}
}
}
除了表1-2中列出的LinkedList类提供的方法外,LinkedList类和ArrayList类所包含的大部分方法都是一样的,主要的原因是因为他们都是List接口的实现类。由于ArrayList采用和数组一样的连续的顺序存储方法,当对数据频繁检索时效率较高,而LinkedList类采用链表存储方式,当数据添加,删除或修改比较多时,建议选择LinkedList类存储数据