目录
集合概述
集合和数组都是容器
数组的特点
数组定义完成并启动后,类型确定、长度固定
在进行增删数据操作的时候,数组是不太合适的,增删数据都需要放弃原有数组或者移位
数组适合的场景
当业务数据的个数是固定的,且都是同一批数据类型的时候,可以采用定义数组存储
集合的特点
集合的大小不固定,启动可以动态变化,类型也可以选择不固定。集合更像气球
集合非常适合做元素的增删操作
注意:集合只能存储引用类型数据,如果要存储基本数据类型可以选用包装类
集合适用的场景
数据个数不确定,需要进行增删元素的时候
1、数组和集合的元素存储的个数问题。
● 数组定义后类型确定,长度固定
● 集合类型可以不固定,大小是可变的。
2、数组和集合存储元素的类型问题。
● 数组可以存储基本类型和引用类型的数据。
● 集合只能存储引用数据类型的数据。
3、数组和集合适合的场景
● 数组适合做数据个数和类型确定的场景。
● 集合适合做数据个数不确定,且要做增删元素的场景。
Collection集合的体系特点
Collection单列集合,每个元素(数据)只包含一个值。
Map双列集合,每个元素包含两个值(键值对)。
注意:前期先掌握Collection集合体系的使用。
Collection集合特点
List系列集合:添加的元素是有序、可重复、有索引。
ArrayList、LinekdList:有序、可重复、有索引。
Set系列集合:添加的元素是无序、不重复、无索引。
HashSet:无序、不重复、无索引;LinkedHashSet:有序、不重复、无索引。
TreeSet;按照大小默认升序排序、不重复、无索引。
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
public class Test {
public static void main(String[] args) {
//有序 可重复 有索引
Collection list = new ArrayList();
list.add("Java");
list.add("Java");
list.add("Mybatis");
list.add(23);
list.add(23);
list.add(true);
list.add(true);
System.out.println(list);//[Java, Java, Mybatis, 23, 23, true, true]
//无序 不重复 有无引
Collection list1 = new HashSet();
list1.add("Java");
list1.add("Java");
list1.add("Mybatis");
list1.add(23);
list1.add(23);
list1.add(true);
list1.add(true);
System.out.println(list1);//[Java, 23, Mybatis, true]
}
}
集合对于泛型的支持
●集合都是支持泛型的,可以在编译阶段约束集合只能操作某种数据类型
Collection<String> lists = new ArrayList<String>();
Collection<String> lists = new ArrayList<>(); // JDK 1.7开始后面的泛型类型申明可以省略不写
注意:集合和泛型都只能支持引用数据类型,不支持基本数据类型,
所以集合中存储的元素都认为是对象。
Collection<int> lists = new ArrayList<>();//这种写法是错误的
如果集合中要存储基本类型的数据怎么办?
//存储基本类型使用包装类
Collection<Integer> lists = new ArrayList<>();
Collection<Double> lists = new ArrayList<>();
import java.util.ArrayList;
import java.util.Collection;
public class Test {
public static void main(String[] args) {
Collection<String> list = new ArrayList<>();
list.add("Java");
list.add("HTML");
Collection<Integer> list1 = new ArrayList<>();
list1.add(22);
list1.add(11);
Collection<Double> list2 = new ArrayList<>();
list2.add(22.5);
list2.add(11.0);
}
}
总结
1、集合的代表是?
● Collection接口。
2、Collection集合分了哪2大常用的集合体系?
● List系列集合:添加的元素是有序、可重复、有索引。
● Set系列集合:添加的元素是无序、不重复、无索引。
3、如何约定集合存储数据的类型,需要注意什么?
●集合支持泛型。
●集合和泛型不支持基本类型,只支持引用数据类型。
Collection集合常用API
Collection集合
● Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的。
Collection API如下:
方法名称 | 说明 |
public boolean add(E e) | 把给定的对象添加到当前集合中 |
public void clear() | 清空集合中所有的元素 |
public boolean remove(E e) | 把给定的对象在当前集合中删除 |
public boolean contains(Object obj) | 判断当前集合中是否包含给定的对象 |
public boolean isEmpty() | 判断当前集合是否为空 |
public int size () | 返回集合中元素的个数。 |
publicObject[] toArray() | 把集合中的元素,存储到数组中 |
import java.lang.reflect.Array;
import java.util.*;
public class Test {
public static void main(String[] args) {
//HashSet:添加元素满足无序,不重复,无索引
Collection<String> c = new ArrayList<>();
//1、添加元素,添加成功返回true
c.add("Java");
c.add("HTML");
System.out.println(c.add("HTML"));//true
c.add("MySQL");
System.out.println(c.add("python"));//true
System.out.println(c);//[Java, HTML, HTML, MySQL, python]
/*//2、清空集合的元素
c.clear();
System.out.println(c);//[]*/
//3、判断集合是否为空,是空返回true,反之
System.out.println(c.isEmpty());//false
//4、获取集合的大小
System.out.println(c.size());//5
//5、判断集合中是否包含某个元素
System.out.println(c.contains("Java"));//true
System.out.println(c.contains("java"));//false
System.out.println(c.contains("喜欢你"));//false
//6、删除某个元素:如果存在多个重复元素默认删除前面的第一个
System.out.println(c.remove("java"));//false
System.out.println(c);//[Java, HTML, HTML, MySQL, python]
System.out.println(c.remove("Java"));//true
System.out.println(c);//[HTML, HTML, MySQL, python]
//7、把集合转换成数组 [HTML, HTML, MySQL, python]
Object[] arrs = c.toArray();
System.out.println("数组:" + Arrays.toString(arrs));//数组:[HTML, HTML, MySQL, python]
System.out.println("--------------拓展---------------");
Collection<String> c1 = new ArrayList<>();
c1.add("java1");
c1.add("java2");
Collection<String> c2 = new ArrayList<>();
c2.add("java3");
c2.add("java4");
//addAll把c2集合的元素全部倒入到c1中
c1.addAll(c2);
System.out.println(c1);//[java1, java2, java3, java4]
System.out.println(c2);//[java3, java4]
}
}
Collection集合的遍历方式
方式一:迭代器
迭代器遍历概述
遍历就是一个一个的把容器中的元素访问一遍。
迭代器在Java中的代表是lterator,迭代器是集合的专用遍历方式。
Collection集合获取迭代器
方法名称 | 说明 |
Iterator<E> iterator() | 返回集合中的迭代器对象,该迭代器对象默认指向当前集合的0索引 |
Iterator中的常用方法
方法名称 | 说明 |
boolean hasNext() | 询问当前位置是否有元素存在,存在返回true,不存在返回false |
E next () | 获取当前位置的元素,并同时将迭代器对象移向下一个位置,注意防止取出越界。 |
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* 定义一个while循环,问一次取一次
* 通过it.hasNext()询问是否有下一个元素,有则进行循环
* it.next()取出元素,同时将迭代器对象移向下一个位置
*/
public class Test {
public static void main(String[] args) {
Collection<String> list = new ArrayList<>();
list.add("喜羊羊");
list.add("懒羊羊");
list.add("美羊羊");
list.add("沸羊羊");
System.out.println(list);
//1、得到当前集合的迭代器对象
Iterator<String> it = list.iterator();
/*String ele = it.next();
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next());
//System.out.println(it.next());//NoSuchElementException 出现无此元素异常的错误*/
//2、定义while循环遍历集合
while (it.hasNext()) {
String ele = it.next();
System.out.println(ele);
}
}
}
方式二:foreach/增强for循环
增强for循环
增强for循环:既可以遍历集合也可以遍历数组。
它是JDK5之后出现的,其内部原理是一个iterator迭代器,遍历集合相当于是迭代器的简化写法。
实现lterable接口的类才可以使用迭代器和增强for,Collection接口已经实现了Iterable接口。
格式:
for(元素数据类型 变量名:数组或者Collection集合){
//在此处使用变量即可,该变量就是元素
}
举例说明
Collection<String> list=new ArrayList<>();
...
for (String ele: list) {
System.out.printin(ele);
}
import java.util.ArrayList;
import java.util.Collection;
public class Test {
public static void main(String[] args) {
Collection<String> list = new ArrayList<>();
list.add("喜羊羊");
list.add("懒羊羊");
list.add("美羊羊");
list.add("沸羊羊");
System.out.println(list);
for (String s : list) {
System.out.println(s);
}
System.out.println("--------------------");
double[] scores = {100, 90.5, 59.5, 60};
for (double score : scores) {
System.out.println(score);
/*if (score==59.5){
score=95;//修改无意义,并不能改变数组的内容
}*/
}
}
}
1、增强for可以遍历哪些容器?
既可以遍历集合也可以遍历数组。
2、增强for的关键是记住它的遍历格式
for(元素数据类型 变量名:数组或者Collection集合){
//在此处使用变量即可,该变量就是元素
}
方式三:Lambda表达式
Lambda表达式遍历集合
● 得益于JDK8开始的新技术Lambda表达式,提供了一种更简单、更直接的遍历集合的方式。
Collection结合Lambda遍历的API
方法名称 | 说明 |
default void forEach (Consumer <? super T > action): | 结合lambda遍历集合 |
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;
public class Test {
public static void main(String[] args) {
Collection<String> list = new ArrayList<>();
list.add("喜羊羊");
list.add("懒羊羊");
list.add("美羊羊");
list.add("沸羊羊");
System.out.println(list);
/*list.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});*/
/*list.forEach((String s)-> {
System.out.println(s);
});*/
list.forEach(s -> System.out.println(s));
//list.forEach(System.out::println);
}
}
Collection集合存储自定义类型的对象
public class Movie {
private String name;
private double score;
private String actor;
public Movie(String name, double score, String actor) {
this.name = name;
this.score = score;
this.actor = actor;
}
public Movie() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
public String getActor() {
return actor;
}
public void setActor(String actor) {
this.actor = actor;
}
@Override
public String toString() {
return "Movie{" +
"name='" + name + '\'' +
", score=" + score +
", actor='" + actor + '\'' +
'}';
}
}
import java.util.ArrayList;
import java.util.Collection;
public class Test {
public static void main(String[] args) {
/*
1、定义一个电影类
2、定义一个集合对象存储3部电影对象
movies.add(new Movie("《你好,李焕英》”,9.5,“张小艾,贾玲,沈腾,陈赫”));
movies,add(new Movie("《唐人街探案》”,8.5,“王宝强,刘昊然”));
movies, add(new Movie(" 《刺杀小说家》",8.6 ,“雷佳音,杨寒”));
3、遍历集合容器中的每个电影对象
*/
Collection<Movie> movies = new ArrayList<>();
movies.add(new Movie("《你好,李焕英》", 9.5, "张小艾,贾玲,沈腾,陈赫"));
movies.add(new Movie("《唐人街探案》", 8.5, "王宝强,刘昊然"));
movies.add(new Movie("《刺杀小说家》", 8.6, "雷佳音,杨幂"));
System.out.println(movies);
for (Movie movie : movies) {
System.out.println("片名:" + movie.getName());
System.out.println("得分:" + movie.getScore());
System.out.println("主演:" + movie.getActor());
}
}
}
集合中存储的是元素的什么信息?
集合中存储的是元素对象的地址
List系列集合
List集合特点、特有API
List系列集合特点
ArrayList、LinekdList:有序,可重复,有索引。
有序:存储和取出的元素顺序一致
有索引:可以通过索引操作元素
可重复:存储的元素可以重复
List集合特有方法
● List集合因为支持索引,所以多了很多索引操作的独特api,其他Collection的功能List也都继承了。
方法名称 | 说明 |
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove (int index) | 删除指定索引处的元素,返回被删除的元素 |
E set (int index, E element) | 修改指定索引处的元素,返回被修改的元素 |
E get (int index) | 返回指定索引处的元素 |
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
//1.创建一个ArrayList集合对象:
//List:有序,可重复,有索引的。
List<String> list = new ArrayList<>();//一行经典代码就诞生了!
list.add("Java");
list.add("Java");
list.add("MySQL");
list.add("My SQL");
// 2.在某个索引位置插入元素。
list.add(2, " HTML");
System.out.println(list);//[Java, Java, HTML, MySQL, My SQL]
// 3.根据索引删除元素,返回被删除元素
System.out.println(list.remove(2));//HTML
System.out.println(list);//[Java, Java, MySQL, My SQL]
// 4.根据索引获取元素:public E get(int index):返回集合中指定位置的元素。
System.out.println(list.get(2));//MySQL
// 5. 修改索引位置处的元素: public E set(int index, E element)//返回修改前的数据
System.out.println(list.set(1, "高斯林"));//Java
System.out.println(list);//[Java, 高斯林, MySQL, My SQL]
list.clear();//清空集合
System.out.println(list);
}
}
总结
1、List系列集合特点
ArrayList、LinekdList:有序,可重复,有索引。
2、List的实现类的底层原理
ArrayList底层是基于数组实现的,根据查询元素快,增删相对慢。
LinkedList底层基于双链表实现的,查询元素慢,增删首尾元素是非常快的。
List集合的遍历方式小结
List集合的遍历方式有几种?
迭代器
增强for循环
Lambda表达式
for循环(因为List集合存在索引)
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<String> lists = new ArrayList<>();
lists.add("Java1");
lists.add("Java2");
lists.add("Java3");
lists.add("Java4");
//(1)for循环
System.out.println("----------------");
for (int i = 0; i < lists.size(); i++) {
String ele = lists.get(i);
System.out.println(ele);
}
//(2)迭代器
System.out.println("----------------");
Iterator<String> it = lists.iterator();
while (it.hasNext()) {
String ele = it.next();
System.out.println(ele);
}
//(3) foreach
System.out.println("----------------");
for (String ele : lists) {
System.out.println(ele);
}
//(4)JDK1.8开始之后的Lambda表达式
System.out.println("----------------");
lists.forEach(s -> System.out.println(s));
}
}
ArrayList集合的底层原理
ArrayList集合底层原理
ArrayList底层是基于数组实现的:根据索引定位元素快,增删需要做元素的移位操作。
第一次创建集合并添加第一个元素的时候,在底层创建一个默认长度为10的数组。
LinkList集合的底层原理
LinkedList的特点
底层数据结构是双链表,查询慢,首尾操作的速度是极快的,所以多了很多首尾操作的特有API。
LinkedList集合的特有功能
方法名称 | 说明 |
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
public E removeFirst() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |
import java.util.LinkedList;
public class Test {
public static void main(String[] args) {
// LinkedList可以完成队列结构,和栈结构 (双链表)
//压栈,入栈
LinkedList<String> stack = new LinkedList<>();
stack.push("第1颗子弹");//stack.addFirst("第1颗子弹");
stack.push("第2颗子弹");
stack.push("第3颗子弹");
stack.push("第4颗子弹");
System.out.println(stack);//[第4颗子弹, 第3颗子弹, 第2颗子弹, 第1颗子弹]
//出栈 弹栈
System.out.println(stack.pop());//System.out.println(stack.removeFirst());
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack);//[]
//队列
LinkedList<String> queue = new LinkedList<>();
//入队
queue.addLast("1号");
queue.addLast("2号");
queue.addLast("3号");
queue.addLast("4号");
System.out.println(queue);//[1号, 2号, 3号, 4号]
//出队
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue);//[]
}
}
补充知识:集合的并发修改异常问题
问题引出
当我们从集合中找出某个元素并删除的时候可能出现一种并发修改异常问题。
哪些遍历存在问题?
迭代器遍历集合且直接用集合删除元素的时候可能出现。
增强for循环遍历集合且直接用集合删除元素的时候可能出现。
哪种遍历且删除元素不出问题
迭代器遍历集合但是用迭代器自己的删除方法操作可以解决。
使用for循环遍历并删除元素不会存在这个问题。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class Test {
public static void main(String[] args) {
//1、准备数据
List<String> list = new ArrayList<>();
list.add("python");
list.add("Java");
list.add("Java");
list.add("孙悟空");
list.add("猪八戒");
list.add("沙和尚");
System.out.println(list);
//[python, Java, Java, 孙悟空, 猪八戒, 沙和尚]
//需求:删除全部Java信息
//1、迭代器删除
/*Iterator<String> it = list.iterator();
while (it.hasNext()){
String ele = it.next();
if ("Java".equals(ele)){
//list.remove("Java");//ConcurrentModificationException 修改异常报错
it.remove();//使用迭代器删除当前位置的元素,保证不后移,能够成功遍历全部元素
}
}
System.out.println(list);//[python, 孙悟空, 猪八戒, 沙和尚]*/
/*//2、foreach遍历删除(会出bug)
for (String s : list) {
if ("Java".equals(s)){
list.remove("Java");
}
}*/
/*//3、lambda表达式(会出bug)
list.forEach(s->{
if ("Java".equals(s)){
list.remove("Java");
}
});*/
/*//4、for循环 (不会出现异常报错,但是数据删除出现了问题:漏删元素)
for (int i = 0; i < list.size(); i++) {
String ele = list.get(i);
if ("Java".equals(ele)){
list.remove("Java");
}
}
System.out.println(list);//[python, Java, 孙悟空, 猪八戒, 沙和尚]*/
//解决方案
/* for (int i = list.size() - 1; i >= 0; i--) {
String ele = list.get(i);
if ("Java".equals(ele)) {
list.remove("Java");
}
}
System.out.println(list);//[python, 孙悟空, 猪八戒, 沙和尚]*/
//解决方案2
for (int i = 0; i < list.size(); i++) {
String ele = list.get(i);
if ("Java".equals(ele)) {
list.remove("Java");
i--;
}
}
System.out.println(list);//[python, 孙悟空, 猪八戒, 沙和尚]
}
}
补充知识:泛型的深入
泛型的概述和优势
泛型概述
泛型:是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。
泛型的格式:<数据类型>;注意:泛型只能支持引用数据类型。
集合体系的全部接口和实现类都是支持泛型的使用的。
泛型的好处:
统一数据类型。
把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为编译阶段类型就能确定下来。
泛型可以在很多地方进行定义:
类后面 | 泛类型 |
方法申明上 | 泛类型方法 |
节后后面 | 泛型接口 |
自定义泛型类
泛型类的概述
定义类时同时定义了泛型的类就是泛型类。
泛型类的格式:修饰符class类名<泛型变量>{}
范例:public class MyArrayList<T>{}
此处泛型变量T可以随便写为任意标识,常见的如E、T、K、V等。
作用:编译阶段可以指定数据类型,类似于集合的作用。
课程案例导学
模拟ArrayList集合自定义一个集合MyArrayList集合,完成添加和删除功能的泛型设计即可。
import java.util.ArrayList;
public class MyArrayList<E> {
private ArrayList lists = new ArrayList();
public void add(E e) {
lists.add(e);
}
public void remove(E e) {
lists.remove(e);
}
@Override
public String toString() {
return lists.toString();
}
}
public class Test {
public static void main(String[] args) {
//需求:模拟ArrayList集合自定义一个集合MyArrayList集合,完成添加和删除功能的泛型设计即可。
MyArrayList<String> list = new MyArrayList<>();
//添加元素
list.add("Java");
list.add("Java");
list.add("HTML");
list.add("Python");
//删除元素
list.remove("Java");
System.out.println(list);
MyArrayList<Integer> list1 = new MyArrayList<>();
list1.add(34);
list1.add(11);
list1.add(12);
System.out.println(list1);
}
}
1、泛型类的核心思想:
把出现泛型变量的地方全部替换成传输的真实数据类型。
2、泛型类的作用
编译阶段约定操作的数据的类型,类似于集合的作用。
自定义泛型方法
泛型方法的概述
定义方法时同时定义了泛型的方法就是泛型方法。
泛型方法的格式:修饰符<泛型变量>方法返回值 方法名称(形参列表)
范例:public<T>void show(T t){}
作用:方法中可以使用泛型接收一切实际类型的参数,方法更具备通用性。
课程案例导学
给任何一个类型的数组,都能返回它的内容。也就是实现Arrays.toString(数组)的功能!
public class Test {
public static void main(String[] args) {
//给任何一个类型的数组,都能返回它的内容。也就是实现Arrays.toString(数组)的功能!
String[] names = {"小王", "小张", "小强"};
printArray(names);//[小王, 小张, 小强]
Integer[] ages = {21, 20, 35, 12, 65};
printArray(ages);//[21, 20, 35, 12, 65]
}
//自定义泛型方法
public static <T> void printArray(T[] arr) {
if (arr != null) {
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < arr.length; i++) {
sb.append(arr[i]).append(i == arr.length - 1 ? "" : ", ");
}
sb.append("]");
System.out.println(sb);
} else {
System.out.println(arr);
}
}
}
自定义泛型接口
泛型接口的概述
使用了泛型定义的接口就是泛型接口。
泛型接口的格式:修饰符 interface 接口名称<泛型变量>①
范例:public interface Data<E>
作用:泛型接口可以让实现类选择当前功能需要操作的数据类型
实现类可以在实现接口的时候传入自己操作的数据类型,这样重写的方法都将是针对于该类型的操作。
课程案例导学
教务系统,提供一个接口可约束一定要完成数据(学生,老师)的增删改查操作泛型接口的原理:
泛型通配符、上下限
通配符:?
?可以在“使用泛型”的时候代表一切类型。
E T K V是在定义泛型的时候使用的。
注意:虽然BMW和BENZ都继承了Car但是ArrayList<BMW>和ArrayList<BENZ>与ArrayList<Car>没有关系的!!
泛型的上下限:
? extends Car:?必须是Car或者其子类 泛型上限
? super Car:?必须是Car或者其父类 泛型下限
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<BMW> bmws = new ArrayList<>();
bmws.add(new BMW());
bmws.add(new BMW());
bmws.add(new BMW());
go(bmws);
ArrayList<BENZ> benzs = new ArrayList<>();
benzs.add(new BENZ());
benzs.add(new BENZ());
benzs.add(new BENZ());
go(benzs);
/*ArrayList<Dog> dogs = new ArrayList<>();
dogs.add(new Dog());
dogs.add(new Dog());
dogs.add(new Dog());
go(dogs);*/
}
/**
* 所有车比赛
*/
public static void go(ArrayList<? extends Car> cars) {
}
}
class Dog {
}
class BENZ extends Car {
}
class BMW extends Car {
}
//父类
class Car {
}