目录
一、集合概述
集合和数组都是容器。
数组的特点:1.数组定于完成并启动后,类型确定、长度固定;2.在进行增删数据操作的时候,数组是不太合适的,增删数据都要放弃原数组或者移位
数组适合的场景:当业务数据的个数是固定的,且是同一批数据类型的时候,可以采取定义数组存储
集合的特点:1.集合的大小不固定,启动后可以动态变化,类型也可以选择不固定,集合更像气球;2.集合非常适合做元素的增删操作
集合适用的场景:数据的个数不确定,需要进行增删元素的时候
二、Collection集合的体系特点
Collection单列集合,每个元素(数据)只包含一个值。
Map双列集合,每个元素包含两个值(键值对)。
Collection集合特点:
List系列集合:添加的元素是有序、可重复、有索引;
ArrayList、LinkedList:有序、可重复、有索引。
Set系列集合:添加的元素是无序、不重复、无索引;
HashSet:无序、不重复、无索引;LinkedHashSet:有序、不重复、无索引;TreeSet:按照大小默认升序排序、不重复、无索引。
集合对泛型的支持:集合都是支持泛型的,可以在编译阶段约束集合只能操作某种数据类型
三、Collection集合常用API
Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的。
package com.pikaqiu.d2_collection_api;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
public class CollectionDemo {
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();
//1.添加元素,添加成功返回true
c.add("Java");
c.add("HTML");
System.out.println(c.add("HTML")); //true
c.add("Mysql");
c.add("Java");
System.out.println(c);
// //2.清空集合的元素
// c.clear();
// System.out.println(c);
//3.判断集合是否为空,是空就返回true,反之false
System.out.println(c.isEmpty()); //false
//4.获取集合大小
System.out.println(c.size());
//5.判断集合中是否包含某个元素
System.out.println(c.contains("Java")); //true
System.out.println(c.contains("java")); //false
//6.删除某个元素:如果有多个重复的元素默认删除前面的第一个
System.out.println(c.remove("Java")); //true
System.out.println(c); //[HTML, HTML, Mysql, Java]
//7.把集合转换成数组
Object[] arrs = c.toArray();
System.out.println(Arrays.toString(arrs));
System.out.println("-------------------------------");
//拓展:合并两个集合
Collection<String> c1 = new ArrayList<>();
c1.add("java1");
c1.add("java2");
Collection<String> c2 = new ArrayList<>();
c2.add("赵敏");
c2.add("殷素素");
c1.addAll(c2);
System.out.println(c1); //[java1, java2, 赵敏, 殷素素]
System.out.println(c2); //[赵敏, 殷素素]
}
}
四、Collection集合的遍历方式
1.方式一:迭代器
迭代器遍历概述:遍历就是一个一个把容器中的元素访问一遍;迭代器在Java中的代表就是Iterator,迭代器是集合的专用遍历方式
package com.pikaqiu.d3_collection_traversal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionDemo1 {
public static void main(String[] args) {
Collection<String> lists = new ArrayList<>();
lists.add("赵敏");
lists.add("小昭");
lists.add("素素");
lists.add("灭绝");
System.out.println(lists);
//1.得到当前集合的迭代器对象
Iterator<String> it = lists.iterator();
// for (int i = 0 ; i < lists.size() ; i++) {
// String ele = it.next();
// System.out.print(ele + " ");
// }
//定义while循环
while (it.hasNext()){
String ele = it.next();
System.out.print(ele + " ");
}
}
}
2.方式二:foreach/增强for循环
增强for循环:既可以遍历集合也可以遍历数组;JDK5之后出现的,其内部原理是一个Iterator迭代器,遍历集合相当于迭代器的简化写法;实现Iterable接口类才可以使用迭代器和增强for,Collection接口已经实现了Iterable接口
格式:for(元素数据类型 变量名 : 数组或者Collection集合){//在此处使用变量即可,该变量就是元素}
package com.pikaqiu.d3_collection_traversal;
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo2 {
public static void main(String[] args) {
Collection<String> lists = new ArrayList<>();
lists.add("赵敏");
lists.add("小昭");
lists.add("素素");
lists.add("灭绝");
System.out.println(lists);
for(String ele : lists){
System.out.print(ele + " ");
}
}
}
3.方式三:lambda表达式
得益于JDK8开始的新技术Lambda表达式,提供了一种更简单、更直接的遍历集合的方式
package com.pikaqiu.d3_collection_traversal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;
public class CollectionDemo3 {
public static void main(String[] args) {
Collection<String> lists = new ArrayList<>();
lists.add("赵敏");
lists.add("小昭");
lists.add("素素");
lists.add("灭绝");
System.out.println(lists);
lists.forEach(s -> System.out.print(s + " "));
System.out.println();
lists.forEach(System.out::println);
}
}
五、Collection集合存储自定义类型的对象
package com.pikaqiu.d4_collection_object;
import java.util.ArrayList;
import java.util.Collection;
public class TestDemo {
public static void main(String[] args) {
//定义电影类,这里省略
//2.定义一个集合对象存储三部电影的信息
Collection<Movie> movies = new ArrayList<>();
movies.add(new Movie("《你好,李焕英》",9.5,"贾玲、张小斐"));
movies.add(new Movie("《唐人街探案》",8.8,"王宝强、刘昊然"));
movies.add(new Movie("《刺杀小说家》",8.5,"杨幂、雷佳音"));
//3.遍历
for(Movie movie : movies){
System.out.println("电影:" + movie.getName());
System.out.println("评分:" + movie.getScore());
System.out.println("主演:" + movie.getActor());
}
}
}
六、常见数据结构
1.数据结构概述、栈、队列
数据结构概述:数据结构是计算机底层存储、组织数据的方式。是指数据相互之间是什么方式排列在一起的;一般情况下,精心选择的数据结构可以带来更高的运行或者存储效率。
栈:先进后出
数据进栈:压栈(进栈)
数据出栈:弹栈(出栈)
队列:先进先出
数据从后端进入队列:入队
数据从前端出队列:出队
2.数组
查询速度快:查询数据通过地址值和索引定位,查询任意数据耗时相同(元素在内存中是连续存储的)
删除效率低:要将原数据删除,同时后面每个数据前移
添加效率低:添加位置后的每个数据后移,再添加元素
3.链表
链表的特点:链表的元素再内存中是不连续存储的,每个元素节点都包含数据值和下一个元素的地址
链表查询慢:无论哪个数据都要从头开始找
链表的增删相对较快,不用移动其他元素的位置
4.二叉树、二叉查找树
二叉树的特点:1.只能有一个根节点,每个节点最多支持2个直接节点;节点的度:节点拥有的子树的个数,二叉树的度不大于2,叶子节点度为0的节点,也称之为终端结点;3.高度:叶子结点的高度为1,叶子结点的父亲结点的高度为2,依次类推,根节点的高度最高;4.层:跟结点在第一层,以此类推;5.兄弟节点:拥有共同父亲节点的节点互称为兄弟节点
二叉查找树又称二叉排序树或者二叉搜索树。
二叉查找树的特点:1.每个节点最多两个子节点;2.左子树所有节点的值都小于根节点的值;3.右子树所有节点的值都大于根节点的值
5.平衡二叉树
二叉树查找的问题:出现瘸子现象。导致查询的性能与单链表一样,查询速度变慢
平衡二叉树:在满足查找二叉树的前提下,让树尽可能矮小,以此提高查数据的性能
平衡二叉树的要求:任意节点的左右两个子树的高度差不超过1,任意节点的左右子树都是一棵平衡二叉树
6.红黑树
红黑树概述:1.红黑树是一种平衡的二叉查找树,是计算机中用到的一种数据结构;2.1972年出现,当时被称为平衡二叉B树,1978年被修改为如今的"红黑树";3.每一个节点可以是红或黑,红黑树不是通过高度平衡的,它的平衡是通过"红黑规则"进行实现的
红黑规则:1.每个节点或是红色,或者是黑色的,根节点必须是黑色的;2.如果一个节点没有子节点或者父节点,则该节点相应的指针属性为Nil,这些Nil视为叶节点,叶节点是黑色的;3.如果一个节点是红色,那么它的子节点必须是黑色(不能出现两个红节点相连的情况);4.对每一个节点,从该节点到其所有后代节点的简单路径上,均包含相同的数目的黑色节点
七、List系列集合
1.List集合特点、特有API
ArrayList、LinkedList:有序、可重复、有索引
有序:存储和取出元素顺序一致;有索引:可以通过索引操作元素;可重复:存储的元素可以重复
2.List集合的遍历方式小结
1.for循环;2.迭代器;3.增强for;4.lambda表达式
3.ArrayList集合的底层原理
ArrayList集合的底层原理:1.ArrayList底层是基于数组实现的:根据索引定位元素块,增删需要做元素的移位操作;2.第一次创建集合并添加第一个元素的时候,在底层创建一个默认长度为10的数组
4.LinkedList集合的底层原理
LinkedList的特点:底层数据结构是双链表,查询慢,首尾操作的速度是极快的,所以多了很多首尾操作的特有API
八、补充知识:集合的并发修改异常问题
package com.pikaqiu.d6_collection_update_delete;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("黑马");
list.add("Java");
list.add("Java");
list.add("赵敏");
list.add("赵敏");
list.add("素素");
System.out.println(list); //[黑马, Java, Java, 赵敏, 赵敏, 素素]
//1.迭代器遍历删除
// Iterator<String> it = list.iterator();
// while(it.hasNext()){
// String ele = it.next();
// if("Java".equals(ele)){
// //list.remove("Java");
// it.remove(); //删除当前元素,并且不会后移
// }
// }
// System.out.println(list);
//foreach遍历删除元素
//解决不了这个问题
// System.out.println("---------------------------------");
// for (String s : list) {
// if("Java".equals(s)){
// list.remove("Java");
// }
// }
// System.out.println(list);
//lambda表达式遍历删除
//同foreach一样
// list.forEach(s -> {
// if("Java".equals(s)){
// list.remove("Java");
// }
// });
//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);
//后删操作即可
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);
}
}
九、泛型深入
1.泛型的概述和优势
泛型概述:JDK5中开始引入的特性,可以在编译阶段约束操作的数据类型,并进行检查
泛型格式:<数据类型>;
注意:泛型只支持引用类型
集合体系的全部接口和实现类都是支持泛型的使用的
泛型的好处:1.统一数据类型;2.把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为编译阶段类型就能确定下来
2.自定义泛型类
泛型类的概述:1.定义类时同时定义了泛型的类就是泛型类;2.泛型类的格式:修饰符 class 类名<泛型变量>{ }
作用:编译阶段可以指定数据类型,类似于集合的作用
泛型类的原理:把出现的泛型变量的地方全部替换成传输的真实数据类型
package com.pikaqiu.d8_genericity_class;
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();
}
}
package com.pikaqiu.d8_genericity_class;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
MyArrayList<String> list = new MyArrayList<>();
list.add("Java");
list.add("Java");
//list.add(22);
list.add("Mysql");
list.add("Mysql");
System.out.println(list);
list.remove("Java");
System.out.println(list);
}
}
3.自定义泛型方法
定义方法的同时定义了泛型的方法就是泛型方法
泛型方法的格式:修饰符 <泛型变量> 方法返回值 方法名称(形参列表){ }
泛型方法的原理:把出现泛型变量的地方全部替换成传输的真实数据类型
package com.pikaqiu.d9_genericity_method;
public class Test {
public static void main(String[] args) {
String[] names = {"嘿嘿","哈哈","嗯哼"};
printArray(names);
Integer[] ages = {18,19,20};
printArray(ages);
}
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);
}
}
}
4.自定义泛型接口
概述:使用了泛型定义的接口就是泛型接口;
泛型接口的格式:修饰符 interface Data<E>{ }
作用:泛型接口可以让实现类选择当前功能需求操作的数据类型
泛型接口的原理:实现类可以实现接口的时候传入自己操作的数据类型,这样重写的方法都将是针对于该类型的操作
5.泛型通配符、上下限
通配符: ?
?可以在"使用泛型"的时候代表一切类型
泛型的上下限:1.?extends Car: ?必须是Car或者其子类 泛型上限;2. ? super Car:?必须是Car或者其父类 泛型下限