1. 集合框架体系
集合是Java中提供的一种容器,可以用来存储多个数据,根据不同存储方式形成的体系结构,就叫做集合框架体系(掌握)。
每一种容器类底层拥有不同的底层算法。
既然数组可以存储多个数据,为什么要出现集合?
数组的长度是固定的,集合的长度是可变的。
使用Java类封装出一个个容器类,开发者只需要直接调用即可,不用再手动创建容器类。
集合中存储的数据,叫做元素,元素只能是对象(引用类型)。
1.1. 容器的分类(掌握)
根据容器的存储特点的不同,可以分成三种情况:
List(列表):允许记录添加顺序,允许元素重复。
Set(集合):不记录添加顺序,不允许元素重复。
Map(映射):容器中每一个元素都包含一对key和value,key不允许重复,value可以重复。严格上说,并不是容器(集合),是两个容器中元素映射关系。
注意:List和Set接口继承于Collection接口,Map接口不继承Collection接口。
Collection接口:泛指广义上集合,主要表示List和Set两种存储方式。
List接口:表示列表,规定了允许记录添加顺序,允许元素重复的规范。
Set接口:表示狭义上集合,规定了不记录添加顺序,不允许元素重复的规范。
Map接口:表示映射关系,规定了两个集合映射关系的规范。
注意:我们使用的容器接口或类都处于java.util包中。
1.2. List接口(重点)
List接口是Collection接口子接口,List接口定义了一种规范,要求该容器允许记录元素的添加顺序,也允许元素重复。那么List接口的实现类都会遵循这一种规范。
List集合存储特点:
允许元素重复
允许记录元素的添加先后顺序
该接口常用的实现类有:
ArrayList类:数组列表,表示数组结构,采用数组实现,开发中使用对多的实现类,重点。
LinkedList类:链表,表示双向列表和双向队列结构,采用链表实现,使用不多。
Stack类:栈,表示栈结构,采用数组实现,使用不多。
Vector类:向量,其实就是古老的ArrayList,采用数组实现,使用不多。
1.2.1. List常用API方法(必须记住)
添加操作
boolean add(Object e):将元素添加到列表的末尾
void add(int index, Object element):在列表的指定位置插入指定的元素
boolean addAll(Collection c):把c列表中的所有元素添加到当前列表中
删除操作
Object remove(int index):从列表中删除指定索引位置的元素,并返回被删除的元素
boolean removeAll(Collection c):从此列表中移除c列表中的所有元素
修改操作
- Object set(int index, Object ele):修改列表中指定索引位置的元素,返回被替换的旧元素
查询操作
int size():返回当前列表中元素个数
boolean isEmpty():判断当前列表中元素个数是否为0
Object get(int index):查询列表中指定索引位置对应的元素
Object[] toArray():把列表对象转换为Object数组
boolean contains(Object o):判断列表是否存在指定对象
注意,标红的是重度使用的方法。
1.2.2. ArrayList类(重点)
ArrayList类,基于数组算法的列表,通过查看源代码会发现底层其实就是一个Object数组。
需求1:操作List接口常用方法
public class ArrayListDemo1 {
public static void main(String[] args) {
//创建一个默认长度的列表对象
List list = new ArrayList();
//打印集合中元素的个数
System.out.println("元素数量:"+list.size());//0
//添加操作:向列表中添加4个元素
list.add("Will");
list.add(100);
list.add(true);
list.add("Lucy");
//查询操作:
System.out.println("列表中所有元素:"+list);//输出:[Will, 100, true, Lucy]
System.out.println("元素数量:"+list.size());//4
System.out.println("第一个元素:"+list.get(0));//Will
//修改操作:把索引为2的元素,替换为wolfcode
list.set(2, "wolfcode");
System.out.println("修改后:"+list);//输出:[Will, 100, wolfcode, Lucy]
//删除操作:删除索引为1的元素
list.remove(1);
System.out.println("删除后:"+list);//输出:[Will, wolfcode, Lucy]
}
}
需求2:创建四个User对象,存储在List中,分析内存图。
public class User {
private String name;
private int age;
//省略两个参数构造器、getter/setter方法、toString方法
}
public class ArrayListDemo2 {
public static void main(String[] args) {
List girls = new ArrayList();
User u1 = new User("西施", 18);
girls.add(u1);
girls.add(new User("王昭君",19));
girls.add(new User("貂蝉",20));
girls.add(new User("杨玉环",21));
System.out.println(girls);
//修改u1对象的名字和年龄
u1.setName("小施");
u1.setAge(17);
System.out.println(girls);
}
}
运行结果(观察变化):
[User [name=西施, age=18], User [name=王昭君, age=19], User [name=貂蝉, age=20], User [name=杨玉环, age=21]]
[User [name=小施, age=17], User [name=王昭君, age=19], User [name=貂蝉, age=20], User [name=杨玉环, age=21]]
内存分析,解释原因:
结论:集合类中存储的对象,都存储的是对象的引用,而不是对象本身。
1.2.3. LinkedList类(了解)
ArrayList类,基于数组算法的列表,通过查看源代码会发现底层其实就是一个Object数组。
LinkedList类,底层采用链表算法,实现了链表,队列,栈的数据结构。无论是链表还是队列主要操作的都是头和尾的元素,因此在LinkedList类中除了List接口的方法,还有很多操作头尾的方法。
void addFirst(Object e) 将指定元素插入此列表的开头。
void addLast(Object e) 将指定元素添加到此列表的结尾。
Object getFirst() 返回此列表的第一个元素。
Object getLast() 返回此列表的最后一个元素。
Object removeFirst() 移除并返回此列表的第一个元素。
Object removeLast() 移除并返回此列表的最后一个元素。
boolean offerFirst(Object e) 在此列表的开头插入指定的元素。
boolean offerLast(Object e) 在此列表末尾插入指定的元素。
Object peekFirst() 获取但不移除此列表的第一个元素;如果此列表为空,则返回 null。
Object peekLast() 获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null。
Object pollFirst() 获取并移除此列表的第一个元素;如果此列表为空,则返回 null。
Object pollLast() 获取并移除此列表的最后一个元素;如果此列表为空,则返回 null。
void push(Object e) 将元素推入此列表所表示的栈。
Object pop() 从此列表所表示的栈处弹出一个元素。
Object peek() 获取但不移除此列表的头(第一个元素)。
LinkedList之所以有这么多方法,是因为自身实现了多种数据结构,而不同的数据结构的操作方法名称不同,在开发中LinkedList使用不是很多,知道存储特点就可以了。
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList list = new LinkedList();
//添加元素
list.addFirst("A");
list.addFirst("B");
System.out.println(list);
list.addFirst("C");
System.out.println(list);
list.addLast("D");
System.out.println(list);
//获取元素
System.out.println("获取第一个元素:" + list.getFirst());//C
System.out.println("获取最后一个元素:" + list.getLast());//D
//删除元素
list.removeFirst();
System.out.println("删除第一个元素后:" + list);//[B, A, D]
list.removeLast();
System.out.println("删除最后一个元素后:" + list);//[B, A]
}
}
程序运行结果:
[B, A]
[C, B, A]
[C, B, A, D]
获取第一个元素:C
获取最后一个元素:D
删除第一个元素后:[B, A, D]
删除最后一个元素后:[B, A]
1.2.4. Stack和Vector类(了解)
Vector类:基于数组算法实现的列表,其实就是ArrayList类的前身。和ArrayList的区别在于方法使用synchronized修饰,所以相对于ArrayList来说,线程安全,但是效率就低了点。
Stack类:表示栈,是Vector类的子类,具有后进先出(LIFO)的特点,拥有push(入栈),pop(出栈)方法。
1.3. 泛型(会用即可)
1.3.1. 什么是泛型(了解)
其实就是一种类型参数,主要用于某个类或接口中数据类型不确定时,可以使用一个标识符来表示未知的数据类型,然后在使用该类或方法时指定该未知类型的真实类型。
泛型可用到的接口、类、方法中,将数据类型作为参数传递,其实更像是一个数据类型模板。
如果不使用泛型,从容器中获取出元素,需要做类型强转,也不能限制容器只能存储相同类型的元素。
List list = new ArrayList();
list.add("A");
list.add("B");
String ele = (String) list.get(0);
1.4. 自定义和使用泛型(了解)
定义泛型:使用一个标识符,比如T在类中表示一种未知的数据类型。
使用泛型:一般在创建对象时,给未知的类型设置一个具体的类型,当没有指定泛型时,默认类型为Object类型。
需求:定义一个类Point,x和y表示横纵坐标,分别使用String、Integer、Double表示坐标类型。
如果没有泛型需要设计三个类,如下:
定义泛型:
//在类上声明使用符号T,表示未知的类型
public class Point<T> {
private T x;
private T y;
//省略getter/setter
}
使用泛型:
//没有使用泛型,默认类型是Object
Point p1 = new Point();
Object x1 = p1.getX();
//使用String作为泛型类型
Point<String> p2 = new Point<String>();
String x2 = p2.getX();
//使用Integer作为泛型类型
Point<Integer> p3 = new Point<Integer>();
Integer x3 = p3.getX();
画图分析:
注意:这里仅仅是演示泛型类是怎么回事,并不是要求定义类都要使用泛型。
1.5. 在集合框架中使用泛型(掌握)
拿List接口和ArrayList类举例。
class ArrayList<E>{
public boolean add(E e){ }
public E get(int index){ }
}
此时的E也仅仅是一个占位符,表示元素(Element)的类型,那么当使用容器时给出泛型就表示该容器只能存储某种类型的数据。
//只能存储String类型的集合
List<String> list1 = new ArrayList<String>();
list1.add("A");
list1.add("B");
//只能存储Integer类型的集合
List<Integer> list2 = new ArrayList<Integer>();
list2.add(11);
list2.add(22);
因为前后两个泛型类型相同(也必须相同),泛型类型推断:
List<String> list1 = new ArrayList<String>();
可以简写为
List<String> list1 = new ArrayList<>();
通过反编译工具,会发现泛型其实是语法糖,也就是说编译之后,泛型就不存在了。
注意:泛型必须是引用类型,不能是基本数据类型(错误如下):
List<int> list = new ArrayList<int>();
泛型不存在继承的关系(错误如下):
List<Object> list = new ArrayList<String>(); //错误的
若要获得最好的学习效果,需要配合对应教学视频一起学习。需要完整教学视频,请参看https://ke.qq.com/course/272077。