文章目录
- 一、Collection
- 1. 概述
- 2. 常用方法
- 3. 集合遍历
- 4. 案例
- 二、List
- 1. 概述
- 2. 特有方法
- 3. 并发修改异常
- 4. 列表迭代器
- 5. 增强 for 循环
- 6. 数据结构
- 6.1 栈和队列
- 6.2 数组和链表
- 7. List 子类特点
- 7.1 LinkedList
一、Collection
集合类的特点:提供一种存储空间可变的存储模型,存储的数据容量可以随时发生改变。
1. 概述
Collection 集合是单例集合的顶层接口,它表示一组对象,这些对象也称为 Collection 的元素。JDK 不提供此接口的任何直接实现,它提供更具体的子接口(如 Set 和 List)实现。
如何创建 Collection 集合的对象?
① 采用多态的方式;
② 具体的实现类是 ArrayList。
//Test.java
package com.an;
import java.util.ArrayList;
import java.util.Collection;
public class Test {
public static void main(String[] args) {
Collection<String> c = new ArrayList<String>();
c.add("I");
c.add("love");
c.add("you");
System.out.println(c); //[I, love, you]
}
}
2. 常用方法
快捷键 Alt + 7 打开一个窗口,能够看到类的所有信息!
3. 集合遍历
Iterator:迭代器,集合的专用遍历方式。
迭代器是通过集合的 Iterator() 方法得到的,所以我们说它是依赖于集合而存在的。
Iterator 中的常用方法:
① next(),返回迭代中的下一个元素;
② hasNext(),如果迭代具有更多元素,则返回 true
//Test.java
package com.an;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Test {
public static void main(String[] args) {
Collection<String> c = new ArrayList<String>();
c.add("I");
c.add("love");
c.add("you");
Iterator<String> it = c.iterator();
while (it.hasNext()) {
String s = it.next();
System.out.println(s);
}
}
}
集合遍历步骤:
① 通过集合对象获取迭代器对象;
② 通过迭代器对象的 hasNext() 方法判断后面是否还有元素;
③ 通过迭代器对象的 next() 方法获取下一个元素。
4. 案例
需求:创建一个存储学生对象的集合,存储 3 个学生对象,使用程序实现在控制台遍历该集合。
思路:
① 定义学生类;
② 创建 Collection 集合对象;
③ 创建学生对象;
④ 把学生添加到集合;
⑤ 遍历集合(迭代器方式)。
//Student.java
package com.an;
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
//Test.java
package com.an;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Test {
public static void main(String[] args) {
Student s1 = new Student("张三", 12);
Student s2 = new Student("李四", 24);
Student s3 = new Student("王五", 19);
Collection<Student> c = new ArrayList<Student>();
c.add(s1);
c.add(s2);
c.add(s3);
Iterator<Student> it = c.iterator();
while (it.hasNext()) {
Student s = it.next();
System.out.println(s);
}
}
}
二、List
1. 概述
List 集合是有序集合,用户可以精确控制列表中每个元素的插入位置,用户可以通过整数索引访问元素,并搜索列表中的元素,与 Set 集合不同,列表通常允许重复的元素。
List 集合特点:
① 有序:存储和取出的元素顺序一致;
② 可重复:存储的元素可以重复。
List<String> list = new ArrayList<String>();
list.add("I");
list.add("love");
list.add("you");
list.add("love");
System.out.println(list); //[I, love, you, love]
2. 特有方法
List 集合的遍历是有两种方式的:
//方式一,采用迭代器遍历
Iterator<String> it = list.iterator();
while (it.hasNext()) {
Student s = it.next();
System.out.println(s);
}
//方式二,用for循环遍历
for (int i = 0; i < list.size; i++) {
String s = list.get(i);
System.out.println(s);
}
3. 并发修改异常
并发修改异常的出现场景:
给出一个集合,遍历该集合,得到每一个元素,看看有没有 “world” 这个元素,如果有,就添加一个 “javaee” 元素。
并发修改异常产生原因:迭代器遍历过程中,通过集合对象修改了集合中元素的长度,造成了迭代器获取元素中判断预期修改值和实际修改值不一致!
这时候我们只能用 for 循环进行遍历,不能使用迭代器遍历,因为在给集合添加元素的时候,迭代器内部会进行一个判断,如果预期修改值和实际修改值不一致,就会抛出并发异常,throw new ConcurrentModificationException()。
//正确应使用for循环遍历
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
if (s.equals("world")) {
list.add("javaee");
}
}
也就是说,如果我们所做的改动会影响到集合的长度,那么使用迭代器遍历集合时就会产生并发修改异常,如果长度未变则不会抛出异常!
4. 列表迭代器
ListIterator:列表迭代器。
特点:
(1)通过 List 集合的 listIterator() 方法得到,所以它是 List 集合特有的迭代器;
(2)用于允许程序员沿任一方向遍历列表的列表迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置。
常用方法:
① next(),返回迭代中的下一个元素;
② hasNext(),如果迭代具有更多元素,则返回 true;
③ previous(),返回列表中的上一个元素;
④ hasPrevious(),如果此列表迭代器在相反方向遍历列表时具有更多元素,则返回 true;
⑤ add(e),将指定的元素插入列表。
ListIterator<String> lit = l.listIterator();
while (lit.hasPrevious()) {
String s = lit.previous();
System.out.println(s);
}
迭代器的指针默认在最左边,所以直接使用逆向遍历是没有元素的,需要先使用正向遍历,反向才能输出,逆向遍历了解即可!
还记得我们上面的并发修改异常,不能用 Iterator 迭代器遍历添加元素,会抛出异常。接下来的 ListIterator 列表迭代器可以帮我们解决这个问题,也就是说,它是可以通过迭代器直接给列表添加元素的。
ListIterator<String> lit = list.listiterator();
while (lit.hasNext()) {
String s = lit.next();
if (s.equals("world")) {
lit.add("javaee");
}
}
System.out.println(list);
不会抛出并发修改异常,因为它的底层最终会把实际修改值赋值给预期修改值!
5. 增强 for 循环
增强 for:简化数组和 Collection 集合的遍历。
① 实现 Iterator 接口的类允许其对象成为增强型 for 语句的目标;
② 它是 JDK5 之后出现的,其内部原理是一个 Iterator 迭代器。
List<String> list = new ArrayList<String>();
list.add("My");
list.add("beautiful");
list.add("baby");
for (String s : list) {
System.out.println(s);
}
以下是一个案例,List 集合存储学生对象并遍历,这里学生类与上面的相同不变,所以只展示测试类。
//Test.java
package com.an;
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
Student s1 = new Student("刘德华", 60);
Student s2 = new Student("张学友", 58);
Student s3 = new Student("李易峰", 37);
List<Student> list = new ArrayList<Student>();
list.add(s1);
list.add(s2);
list.add(s3);
for (Student s : list) {
System.out.println(s);
}
}
}
List 集合的遍历有三种方式,增强 for 是最方便的遍历方式,如果在遍历过程中要使用索引,那么我们就用普通的 for 循环即可!
6. 数据结构
数据结构是计算机存储、组织数据的方式,是指相互之间存在一种或多种特定关系的数据元素的集合。精心选择的数据结构可以带来更高的运行或者存储效率。
6.1 栈和队列
① 栈是一种数据先进后出的模型。数据进入栈模型的过程称为进栈,数据离开栈模型的过程称为出栈。
② 队列是一种数据先进先出的模型。数据从后端进入队列模型的过程称为入队列,数据从前端离开队列模型的过程称为出队列。
6.2 数组和链表
① 数组是一种查询快,增删慢的模型。查询数据通过索引定位,查询任意数据耗时相同,查询效率高;删除数据时,要将原始数据删除,同时后面的每个数据前移,删除效率低;添加数据时,先将添加位置后的每个数据后移,再添加元素,添加效率极低。
② 链表是一种增删快、查询慢的模型。链表可以通过修改下一结点的地址,来实现数据的增加和删除操作,速度较快;但是想要查询某一数据,它是要从头(head)开始查询的,显然查询效率很低。
存储一个数据 A 保存在地址 11 的位置,再存储一个数据 C 保存在地址 37 的位置,再存储一个数据 D 保存在地址 96 的位置,如下图:
head 是头结点表示开始,^ 是结点指向空地址表示结束!
在数据 AC 之间添加一个数据 B,保存在地址 54 位置,则数据 B 对应的下一个数据地址指向数据 C,数据 A 对应的下一个数据地址指向数据 B,如下图:
删除操作同理,若要删除数据 BD 之间的数据 C,可以让数据 B 对应的下一个数据地址指向数据 D,再删除数据 C 即可。
7. List 子类特点
List 集合常用子类:ArrayList,LinkedList。
① ArrayList,底层数据结构是数组,查询快,增删慢;
② LinkedList,底层数据结构是链表,查询慢,增删快。
//Test.java
package com.an;
import java.util.ArrayList;
import java.util.LinkedList;
public class Test {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
list.add("s1");
list.add("s2");
list.add("s3");
for (String s : list) {
System.out.println(s);
}
LinkedList<String> li = new LinkedList<String>();
li.add("S1");
li.add("S2");
li.add("S3");
for (String s : li) {
System.out.println(s);
}
}
}
7.1 LinkedList
LinkedList 集合的特有功能:
//Test.java
package com.an;
import java.util.LinkedList;
public class Test {
public static void main(String[] args) {
LinkedList<String> li = new LinkedList<String>();
li.add("S1");
li.add("S2");
li.add("S3");
li.addFirst("what");
li.addLast("www");
System.out.println(li);
System.out.println(li.getFirst());
li.remove("S1");
System.out.println(li);
li.set(3, "hh");
System.out.println(li);
}
}
你可能会有疑问,这些方法能对列表的开头和结尾实施操作,那如果想操作中间部分怎么办呢?
不要忘了 LinkedList 可是 List 的一个子类,我们这里仅仅讲的是 LinkedList 的特有方法,想操作中间部分直接用 and、remove 和 set 等方法就行了,父类的方法子类都是可以直接拿来用的。