java高级

我的java基础太差了

一、集合进阶

1.1 单列集合-List

image-20251125115100228

List系列集合添加的元素都是有序、可重复、有索引的

set系列集合添加的元素是无序,不重复,无索引的

1.1.1 Collection集合

collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的。

image-20251125115508008

Collection<String> coll = new ArrayList<>();
coll.add("XXX");
contains小知识点

当集合中插入的是对象使用对象来做比较哦是无法比对成功的,需要重写equals方法

1.1.1.1 Collection的遍历方式

迭代器遍历

迭代器在Java中的类是Iterator,迭代器是集合专用的遍历方式。

// 1. 创建集合并添加元素
Collection<String> coll = new ArrayList<>();
coll.add("aaa");
coll.add("bbb");
coll.add("ccc");
coll.add("ddd");

// 2. 获取迭代器对象
Iterator<String> it = coll.iterator();

// 3. 利用循环不断的去获取集合中的每一个元素
while(it.hasNext()) {
// 4. next方法的两件事情:获取元素并移动指针
String str = it.next();
System.out.println(str);
}

  1. 报错NoSuchElementException
  2. 迭代器遍历完毕,指针不会复位
  3. 循环中只能用一次next方法
  4. 迭代器遍历时,不能用集合的方法进行增加或者删除

增强for遍历

  • 增强for的底层就是迭代器,为了简化迭代器的代码书写的。
  • 它是JDK5之后出现的,其内部原理就是一个lterator迭代器
  • 所有的单列集合和数组才能用增强for进行遍历。

格式

for(元素的数据类型 变量名: 数组或者集合){

}
// 示例:

// 1. 创建集合并添加元素
Collection<String> coll = new ArrayList<>();
coll.add("zhangsan");
coll.add("lisi");
coll.add("wangwu");
// 2. 利用增强for进行遍历
// 注意点:
// s其实就是一个第三方变量,在循环的过程中依次表示集合中的每一个数据
for (String s : coll) {
System.out.println(s);
}

lambda表达式

// 1. 创建集合并添加元素
Collection<String> coll = new ArrayList<>();
coll.add("zhangsan");
coll.add("lisi");
coll.add("wangwu");

// 2. 利用方法引用优化遍历
coll.forEach(System.out::println);

1.1.2 List集合

  • collection的方法List都继承了
  • List集合因为有索引,所以多了很多索引操作的方法。
方法名称说明
void add(int index, E element)在此集合中的指定位置插入指定的元素
E remove(int index)删除指定索引处的元素,返回被删除的元素
E set(int index, E element)修改指定索引处的元素,返回被修改的元素
E get(int index)返回指定索引处的元素
// 1. 创建一个集合
List<String> list = new ArrayList<>();

// 2. 添加元素
list.add("aaa");
list.add("bbb");//1
list.add("ccc");

// void add(int index, E element) 在此集合中的指定位置插入指定的元素
// 细节: 原来索引上的元素会依次往后移
// list.add(1, "QQQ");

// E remove(int index) 删除指定索引处的元素,返回被删除的元素
// String remove = list.remove(0);
// System.out.println(remove);//aaa

// E set(int index, E element) 修改指定索引处的元素,返回被修改的元素
String result = list.set(0, "QQQ");
System.out.println(result);

// 3. 打印集合
System.out.println(list);

遍历

与上方Collection遍历一致

列表迭代器

// 5. 列表迭代器
// 获取一个列表迭代器的对象,里面的指针默认也是指向0索引的

// 额外添加了一个方法: 在遍历的过程中,可以添加元素
ListIterator<String> it = list.listIterator();
while(it.hasNext()){
String str = it.next();
if("bbb".equals(str)){
// qqq
it.add("qqq");
}
}

image-20251125174930136

1.1.3 ArrayList

扩容机制:

  • 利用空参创建的集合,在底层创建一个默认长度为0的数组
  • 添加第一个元素时,底层会创建一个新的长度为10的数组
  • 存满时,会扩容1.5倍
  • 如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准

这个1.5倍是相对与当前长度的1.5倍哦,比如现在长度是15了,再扩容就是15+(15*0.5)(向下取整)=22

image-20251126125047881

1.1.4 LinkedList集合·

image-20251126125309823

1.1.5 泛型

泛型方法

修饰符<类型>返回值类型方法名(类型变量名){
}

只能在本方法上使用

泛型接口:

修饰符 interface 接口名<类型>{
}
  1. 什么是泛型?
    • JDK5引入的特性,可以在编译阶段约束操作的数据类型,并进行检查
  2. 泛型的好处?
    • 统一数据类型
    • 把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为在编译阶段类型就能确定下来。
  3. 泛型的细节?

    • 泛型中不能写基本数据类型

    • 指定泛型的具体类型后,传递数据时,可以传入该类型和他的子类类型

    • 如果不写泛型,类型默认是Object

  4. 哪里定义泛型?

    • 泛型类:在类名后面定义泛型,创建该类对象的时候,确定类型

    • 泛型方法:在修饰符后面定义方法,调用该方法的时候,确定类型

    • 泛型接口:在接口名后面定义泛型,实现类确定类型,实现类延续泛型
  5. 泛型的继承和通配符
    • 泛型不具备继承性,但是数据具备继承性
    • 泛型的通配符:?
    • ? extend E
    • ? super E
  6. 使用场景
    • 定义类、方法、接口的时候,如果类型不确定,就可以定义泛型
    • 如果类型不确定,但是能知道是哪个继承体系中的,可以使用泛型的通配符

1.2 树

1.2.1 红黑树

  • 红黑树是一种自平衡的二叉查找树,是计算机科学中用到的一种数据结构。
  • 1972年出现,当时被称之为平衡二叉B树。后来,1978年被修改为如今的”红黑树”。
  • 它是一种特殊的二叉查找树,红黑树的每一个节点上都有存储位表示节点的颜色,
  • 每一个节点可以是红或者黑;红黑树不是高度平衡的,它的平衡是通过”红黑规则”进行实现的

红黑规则

  • 每一个节点或是红色的,或者是黑色的
  • 根节点必须是黑色
  • 如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的
  • 如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
  • 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点;

红黑树默认为红色

image-20251127114150945

1.3 单列集合-Set

无序、不重复、无索引

HashSet:无序、不重复、无索引

LinkedHashSet:有序、不重复、无索引

Treeset:可排序、不重复、无索引

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class SetExample {
public static void main(String[] args) {
// 1. 创建 Set 集合对象
Set<String> s = new HashSet<>();

// 2. 添加元素
s.add("张三");
s.add("张三"); // 重复元素添加失败,返回 false
s.add("李四");
s.add("王五");

// 3. 打印集合(无序)
System.out.println(s); // 例如输出 [李四, 张三, 王五]

// 4. 使用迭代器遍历
Iterator<String> it = s.iterator();
while (it.hasNext()) {
String str = it.next();
System.out.println(str);
}

// 5. 使用增强 for 遍历
System.out.println("增强for循环遍历:");
for (String str : s) {
System.out.println(str);
}

// 6. 使用 Lambda 表达式遍历
System.out.println("Lambda 表达式遍历:");
s.forEach(str -> System.out.println(str));

// 或者使用方法引用
System.out.println("方法引用遍历:");
s.forEach(System.out::println);
}
}

1.3.1 HashSet

HashSet底层原理

  • Hashset集合底层采取哈希表存储数据
  • 哈希表是一种对于增删改查数据性能都较好的结构

哈希表的组成

JDK8开始:数组+链表+红黑树

哈希值

  • 根据hashcode方法算出来的int类型的整数
  • 该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算
  • 一般情况下,会重写hashcode方法,利用对象内部的属性值计算哈希值

对象的哈希值特点:

  • 如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
  • 如果已经重写hashcode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
  • 在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样。(哈希碰撞)

示例

@Data
public class Student{
private String name;
private int age;
}
// 1. 创建对象
Student s1 = new Student("zhangsan", 23);
Student s2 = new Student("zhangsan", 23);

// 2. 打印 hashCode(假设已重写 hashCode 方法)
System.out.println(s1.hashCode()); // 例如输出:-1461067292
System.out.println(s2.hashCode()); // 例如输出:-1461067292

底层原理

  • 创建一个默认长度16,默认加载因为0.75的数组,数组名table
  • 根据元素的哈希值跟数组的长度计算出应存入的位置
  • 判断当前位置是否为null,如果是null直接存入
  • 如果位置不为null,表示有元素,则调用equals方法比较属性值
  • 一样:不存不一样:存入数组,形成链表
    JDK8以前:新元素存入数组,老元素挂在新元素下面JDK8以后:新元素直接挂在老元素下面

image-20251127145357809

JDK8以后,当链表长度超过8,而且数组长度大于等于64时,自动转换为红黑树

如果集合中存储的是自定义对象,必须要重写hashCode和equals方法

1.3.2 LinkedHashSet

  • 有序、不重复、无索引。
  • 这里的有序指的是保证存储和取出的元素顺序一致
  • 原理∶底层数据结构是依然哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序。

image-20251128093136403

效率较低

1.3.3 TreeSet

  • 有序、不重复、无索引。
  • 默认从小到大排序
  • 底层基于红黑树实现排序,增删改查性能较好

TreeSet集合自定义规则有几种方式

  • 方式一: Javabean类实现Comparable接口,指定比较规则
  • 方式二:创建集合时,自定义Comparator比较器对象,指定比较规则

方法的返回值的特点

  • 负数:表示当前要添加的元素是小的,存左边
  • 正数:表示当前要添加的元素是大的,存右边
  • 0:表示当前要添加的元素已经存在,舍弃
import java.util.Iterator;
import java.util.TreeSet;

public class TreeSetDemo {
public static void main(String[] args) {
// 1. 创建TreeSet集合对象
TreeSet<Integer> ts = new TreeSet<>();

// 2. 添加元素
ts.add(4);
ts.add(5);
ts.add(1);
ts.add(3);
ts.add(2);

// 3. 打印集合
// System.out.println(ts);

// 4. 遍历集合(三种遍历)

// (1)迭代器遍历
Iterator<Integer> it = ts.iterator();
while (it.hasNext()) {
int i = it.next();
System.out.println(i);
}

// (2)增强for循环遍历
for (Integer num : ts) {
System.out.println(num);
}

// (3)Lambda表达式遍历
ts.forEach(num -> System.out.println(num));
}
}

方式1:Comparable

实现类中去实现comparable接口,重写里面的抽象方法,再制定规则

public class Student implements Comparable<Student> {
private String name;
private int age;

// 无参构造器
public Student() {
}

// 带参构造器
public Student(String name, int age) {
this.name = name;
this.age = age;
}

// Getter 和 Setter 方法
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;
}

// 重写 compareTo 方法,按年龄升序比较
@Override
public int compareTo(Student other) {
return Integer.compare(this.age, other.age);
// 如果需要先比较年龄,再比较姓名,可以这样写:
// int ageCompare = Integer.compare(this.age, other.age);
// if (ageCompare != 0) {
// return ageCompare;
// }
// return this.name.compareTo(other.name);
}

// 重写 toString 方法,方便打印对象信息
@Override
public String toString() {
return "Student{name='" + name + '\'' + ", age=" + age + '}';
}
}

底层使用的是红黑树

方式二:比较器排序

方式一不满足我们的需求时才需要使用比较器

import java.util.Comparator;
import java.util.TreeSet;

public class TreeSetComparatorDemo {
public static void main(String[] args) {
// 1. 创建集合,并传入自定义比较器
// o1: 表示当前要添加的元素
// o2: 表示已经在红黑树存在的元素
// 返回值规则跟之前一样:负数表示o1 < o2,0表示相等,正数表示o1 > o2
TreeSet<String> ts = new TreeSet<>((o1, o2) -> {
int i = o1.length() - o2.length();
return i == 0 ? o1.compareTo(o2) : i;
});

// 添加元素测试
ts.add("apple");
ts.add("banana");
ts.add("pear");
ts.add("grape");
ts.add("kiwi");
ts.add("orange");

// 输出排序后的TreeSet
System.out.println(ts);
}
}

1.4 单列集合的使用场景

  1. 如果想要集合中的元素可重复
  • 用ArrayList集合,基于数组的。(用的最多)
  1. 如果想要集合中的元素可重复,而且当前的增删操作明显多于查询
  • 用LinkedList集合,基于链表的。
  1. 如果想对集合中的元素去重
  • 用HashSet集合,基于哈希表的。(用的最多)
  1. 如果想对集合中的元素去重,而且保证存取顺序
  • 用LinkedHashSet集合,基于哈希表和双链表,效率低于HashSet。
  1. 如果想对集合中的元素进行排序
  • 用TreeSet集合,基于红黑树。后续也可以用List集合实现排序。

1.5 双列集合-Map

  • 双列集合一次需要存一对数据,分别为键和值
  • 键不能重复,值可以重复
  • 键和值是一一对应的,每一个键只能找到自己对应的值
  • 键+值这个整体我们称之为“键值对”或者“键值对对象”,在Java中叫做“Entry对象”

image-20251128110118704

1.5.1 Map的常见API

Map是双列集合的顶层接口,它的功能是全部双列集合都可以继承使用的

方法名称说明
V put(K key, V value)添加元素
V remove(Object key)根据键删除键值对元素
void clear()移除所有的键值对元素
boolean containsKey(Object key)判断集合是否包含指定的键
boolean containsValue(Object value)判断集合是否包含指定的值
boolean isEmpty()判断集合是否为空
int size()集合的长度,也就是集合中键值对的个数

使用方式

// 1. 创建Map集合的对象
Map<String, String> m = new HashMap<>();

// 2. 添加元素
// put方法的细节:
// 添加/覆盖
// 在添加数据的时候,如果键不存在,那么直接把键值对对象添加到map集合当中
// 在添加数据的时候,如果键是存在的,那么会把原有的键值对对象覆盖,会把被覆盖的值进行返回。

m.put("郭靖", "黄蓉");
m.put("韦小宝", "沐剑屏");
m.put("尹志平", "小龙女");

String value = m.put("韦小宝", "双儿");
System.out.println(value);


// 删除-remove
String result = m.remove( key:"郭靖");

// 清空-clear
m.clear();

// 判断是否包含-成功true失败false
boolean keyResult = m.containsKey("郭靖");
System.out.println(keyResult); // true

boolean valueResult = m.containsValue("小龙女2");
System.out.println(valueResult);

// 判断集合是否为空
boolean result = m.isEmpty();
System.out.println(result);

// 集合的长度,也就是集合中键值对的个数
int size = m.size();
System.out.println(size);


// 3. 打印集合
System.out.println(m);

遍历方式

方式一:键找值

public class A02_MapDemo2 {
public static void main(String[] args) {
// Map集合的第一种遍历方式

// 1.创建Map集合的对象
Map<String, String> map = new HashMap<>();

// 2.添加元素
map.put("尹志平", "小龙女");
map.put("郭靖", "穆念慈");
map.put("欧阳克", "黄蓉");

// 3.通过键找值

// 3.1获取所有的键,把这些键放到一个单列集合当中
Set<String> keys = map.keySet();

// 3.2遍历单列集合,得到每一个键
for (String key : keys) {
// System.out.println(key);
// 3.3 利用map集合中的键获取对应的值 get
String value = map.get(key);
System.out.println(key + " = " + value);
}

// 使用迭代器的方式
Iterator<String> iterator = keys.iterator();
while (iterator.hasNext()) {
String key = iterator.next();
String value = map.get(key);
System.out.println(key + " = " + value);
}
// 使用Lambda表达式的方式遍历
keys.forEach(key -> {
String value = map.get(key);
System.out.println(key + " = " + value);
});
}
}

方式二:键值对遍历

public static void main(String[] args) {
// Map集合的第二种遍历方式

// 1.创建Map集合的对象
Map<String, String> map = new HashMap<>();

// 2.添加元素
// 键:人物的外号
// 值:人物的名字
map.put("标枪连手", "马超");
map.put("人物挂件", "明世隐");
map.put("御龙骑士", "尹志平");

// 3. Map集合的第二种遍历方式
// 通过键值对对象进行遍历
// 3.1 通过一个方法获取所有的键值对对象,返回一个Set集合
Set<Map.Entry<String, String>> entries = map.entrySet();

// 1. 增强for循环遍历键值对对象的单列集合
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + " = " + value);
}
// 2. 迭代器形式遍历键值对对象的单列集合
Iterator<Map.Entry<String,String>> iterator = entries.iterator();
while(iterator.hasNext()) {
Map.Entry<String,String> entry = iterator.next();
System.out.println(entry.getKey() + " = " + entry.getValue());
}
// 3. lambda表达式遍历
entries.forEach(entry -> {
System.out.println(entry.getKey() + " = " + entry.getValue());
});
}

方法三:Lambda表达式遍历

public static void main(String[] args) {
// Map集合的第三种遍历方式

// 1. 创建Map集合的对象
Map<String, String> map = new HashMap<>();

// 2. 添加元素
// 键:人物的名字
// 值:名人名言
map.put("鲁迅", "这句话是我说的");
map.put("曹操", "不可能绝对不可能");
map.put("刘备", "接着奏乐接着舞");
map.put("柯镇恶", "看我眼色行事");

// 3. 使用Lambda表达式遍历map
map.forEach((key, value) -> System.out.println(key + " = " + value));
}

1.5.2 HashMap

  1. HashMap底层是哈希表结构的
  2. 依赖hashCode方法和equals方法保证键的唯一
  3. 如果键存储的是自定义对象,需要重写hashcode和equals方法如果值存储自定义对象,不需要重写hashCode和equals方法

案例1:

需求:创建一个HashMap集合,是公生对象(Student),值是籍贯(String)。

存储三个键值对元素,并遍历

要求:同姓名,同年龄认为是同一个学生

@Data
public class Student{
private String name;
private int age;
}
public class hashDemo{
public static void main(String[] args){
// 1. 创建集合
HashMap<Student,String> hm new HashMap<>();

// 2. 创建三个学生对象
Student s1 = new Student("zhangsan", 23);
Student s2 = new Student("lisi", 24);
Student s3 = new Student("wangwu", 25);

// 3. 添加元素到Map集合
Map<Student, String> hm = new HashMap<>();
hm.put(s1, "江苏");
hm.put(s2, "浙江");
hm.put(s3, "福建");

// 遍历集合
// 1. 通过keySet遍历
Set<Student> keys = hm.keySet();
for (Student key : keys) {
String value = hm.get(key);
System.out.println(key + "=" + value);
}

System.out.println("----------------------------");

// 2. 通过entrySet遍历
Set<Map.Entry<Student, String>> entries = hm.entrySet();
for (Map.Entry<Student, String> entry : entries) {
Student key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "=" + value);
}

// 3. Lambda表达式
hm.forEach((Student,s)->System.out.println(student,s))

}
}

1.5.3 LinkedhashMap

  • 由键决定:有序、不重复、无索引。
  • 这里的有序指的是保证存储和取出的元素顺序一致
  • 原理:底层数据结构是依然哈希表,只是每个键值对元素又额外的多了一个双链表的机制记录存储的顺序。
// 1. 创建集合
LinkedHashMap<String, Integer> lhm = new LinkedHashMap<>();

// 2. 添加元素
lhm.put("c", 789);
lhm.put("b", 456);
lhm.put("a", 123);
lhm.put("a", 111); // "a" 这个键对应的值会被更新为111

// 3. 打印集合
System.out.println(lhm);

1.5.4 TreeMap

  • TreeMap跟TreeSet底层原理一样,都是红黑树结构的。
  • 由键决定特性:不重复、无索引、可排序
  • 可排序:对键进行排序。
  • 注意:默认按照键的从小到大进行排序,也可以自己规定键的排序规则

1.6 Collections

  • java.util.Collections:是集合工具类
  • 作用:Collections不是集合,而是集合的工具类。
方法名称说明
public staticboolean addAll(Collectionc, T… elements)批量添加元素
public static void shuffle(List<?> list)打乱List集合元素的顺序
public staticvoid sort(Listlist)排序
public staticvoid sort(Listlist, Comparatorc)根据指定的规则进行排序
public staticint binarySearch(Listlist, T key)以二分查找法查找元素
public staticvoid copy(Listdest, Listsrc)拷贝集合中的元素
public staticint fill(Listlist, T obj)使用指定的元素填充集合
public staticvoid max/min(Collectioncoll)根据默认的自然排序获取最大/小值
public staticvoid swap(List<?> list, int i, int j)交换集合中指定位置的元素
import java.util.*;

public class CollectionsExample {
public static void main(String[] args) {
// 创建一个List
List<Integer> list = new ArrayList<>(Arrays.asList(10, 1, 2, 4, 8, 5, 9, 6, 7, 3));

// shuffle 打乱List集合元素的顺序
Collections.shuffle(list);
System.out.println("shuffle后: " + list);

// sort 排序(自然顺序)
Collections.sort(list);
System.out.println("sort后: " + list);

// sort 指定Comparator规则排序,降序排序示例
Collections.sort(list, (a, b) -> b - a);
System.out.println("sort(降序)后: " + list);

// binarySearch 二分查找,查找元素5,返回索引,如果没找到返回负数
Collections.sort(list); // binarySearch作用于已排序的list,先排序
int index = Collections.binarySearch(list, 5);
System.out.println("元素5的位置: " + index);

// copy 拷贝集合中的元素,目标集合容量需大于等于源集合
List<Integer> dest = new ArrayList<>(Collections.nCopies(list.size(), 0)); // 初始化目标集合容量
Collections.copy(dest, list);
System.out.println("copy后dest: " + dest);

// fill 使用指定的元素填充集合
Collections.fill(dest, 100);
System.out.println("fill后dest: " + dest);

// max/min 根据默认自然排序获取最大/小值
int max = Collections.max(list);
int min = Collections.min(list);
System.out.println("最大值: " + max + ", 最小值: " + min);

// swap 交换集合中指定位置元素
Collections.swap(list, 0, list.size() - 1);
System.out.println("swap后list: " + list);
}
}

1.7 不可变集合

不可以被修改的集合,长度和内容都不可被修改

1.7.1 代码格式

在List、Set、Map接口中,都存在静态的of方法,可以获取一个不可变的集合。

方法名称说明
staticListof(E…elements)创建一个具有指定元素的List集合对象
staticSetof(E…elements)创建一个具有指定元素的Set集合对象
staticMapof(E…elements)创建一个具有指定元素的Map集合对象

注意: 这个集合不能添加,不能删除,不能修改

List<String> list = List.of("张三", "李四", "王五", "赵六");

System.out.println(list.get(0));
System.out.println(list.get(1));
System.out.println(list.get(2));
System.out.println(list.get(3));

for (String s : list) {
System.out.println(s);
}

System.out.println("--------------------------");

Iterator<String> it = list.iterator();
while(it.hasNext()){
String s = it.next();
System.out.println(s);
}
System.out.println("--------------------------");


可变Map集合

import java.util.Map;
import java.util.HashMap;

public class ImmutableDemo {

public static void main(String[] args) {
// 1. 创建一个可变的HashMap,并放入数据
HashMap<String, String> hm = new HashMap<>();
hm.put("郑十", "苏州");
hm.put("刘一", "无锡");
hm.put("陈二", "嘉兴");
hm.put("aaa", "111");

// 2. 利用上面的数据来获取一个不可变的集合
// 通过entrySet转换为数组后,调用Map.ofEntries创建不可变Map
Map<Object, Object> map = Map.ofEntries(hm.entrySet().toArray(new Map.Entry[0]));

// 尝试修改会抛出异常(这里注释,以免程序异常终止)
// map.put("bbb", "222"); // UnsupportedOperationException

// 打印不可变Map内容
map.forEach((key, value) -> System.out.println(key + " -> " + value));
}
}

总结

  1. 不可变集合的特点?
    ● 定义完成后不可以修改,或者添加、删除
  2. 如何创建不可变集合?
    ● List、Set、Map接口中,都存在of方法可以创建不可变集合
  3. 三种方式的细节
    ● List:直接用
    ● Set:元素不能重复
    ● Map:元素不能重复、键值对数量最多是10个。
    超过10个用ofEntries方法

1.8 Stream流

1.8.1 流的开始

先得到一条Stream流(流水线),并把数据放上去

获取方式方法名说明
单列集合default Streamstream()Collection中的默认方法
双列集合无法直接使用stream流
数组public staticStreamstream(T[] array)Arrays工具类中的静态方法
一堆零散数据public staticStreamof(T… values)Stream接口中的静态方法

双列集合获取Stream流的方式

import java.util.HashMap;

public class StreamDemo3 {
public static void main(String[] args) {
// 1. 创建双列集合
HashMap<String, Integer> hm = new HashMap<>();

// 2. 添加数据
hm.put("aaa", 111);
hm.put("bbb", 222);
hm.put("ccc", 333);
hm.put("ddd", 444);

// 3. 第一种获取stream流
// hm.keySet().stream().forEach(s -> System.out.println(s));

// 4. 第二种获取stream流
hm.entrySet().stream().forEach(s -> System.out.println(s));
}
}

数组方式获取Stream流集合

import java.util.Arrays;

public class StreamDemo4 {
public static void main(String[] args) {
// 1. 创建数组
int[] arr1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
String[] arr2 = {"a", "b", "c"};

// 2. 获取stream流
Arrays.stream(arr1).forEach(s -> System.out.println(s));

System.out.println("==========================");

Arrays.stream(arr2).forEach(s -> System.out.println(s));
}
}

1.8.2 流的中间操作

名称说明
Streamfilter(Predicate<? super T> predicate)过滤
Streamlimit(long maxSize)获取前几个元素
Streamskip(long n)跳过前几个元素
Streamdistinct()元素去重,依赖(hashCode和equals方法)
staticStreamconcat(Stream a, Stream b)合并a和b两个流为一个流
Streammap(Functionmapper)转换流中的数据类型

1.8.3 终结方法

名称说明
void forEach(Consumer action)遍历
long count()统计
toArray()收集流中的数据,放到数组中
collect(Collector collector)收集流中的数据,放到集合中

1.8.4 Collections收集方法

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class StreamDemo {
public static void main(String[] args) {
// 创建并初始化一个ArrayList
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,
"张无忌-男-15", "张无忌-男-15", "周芷若-女-14", "赵敏-女-13", "张强-男-20",
"张三丰-男-100", "张翠山-男-40", "张良-男-35", "王二麻子-男-37", "谢广坤-男-41");

// ------------------- 任务一:收集到List集合当中 -------------------
// 需求: 我要把所有的男性收集起来
List<String> newList1 = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toList());

System.out.println("收集到List中的男性(允许重复): " + newList1);
System.out.println("=========================================");


// ------------------- 任务二:收集到Set集合当中 -------------------
// 需求: 我要把所有的男性收集起来
Set<String> newList2 = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toSet());

System.out.println("收集到Set中的男性(自动去重): " + newList2);
}

// 以姓名为key,年龄为value,收集男性。这里的key重复了会报错,因此用toMap第三个参数合并函数解决:
Map<String, String> nameToAgeMap = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toMap(
s -> s.split("-")[0], // key = 姓名
s -> s.split("-")[2], // value = 年龄
(oldVal, newVal) -> oldVal // key重复时,保留第一个
));
System.out.println("姓名 -> 年龄 (男性): " + nameToAgeMap);
/**
list.stream()
把list(一个字符串集合)转换成一个流,方便链式操作。
.filter(s -> "男".equals(s.split("-")[1]))

过滤操作,即只保留性别是“男”的字符串。
s.split("-")是将字符串用“-”拆分成数组,形如[姓名, 性别, 年龄]。
通过取[1]索引获得性别字段,判断是否等于"男"。
只有满足此条件的字符串才会被保留进入下一步。
.collect(Collectors.toMap(...))

将流中剩余的元素收集为一个Map(键值对集合)。
Collectors.toMap需要你指定:
Key映射函数:给Map的key赋值;
Value映射函数:给Map的value赋值;
合并函数:当key重复时,如何处理
*/
}

1.8.5 方法引用

  1. 引用处需要是函数式接口.
  2. 被引用的方法需要已经存在
  3. .被引用方法的形参和返回值需要跟抽象方法的形参和返回值保持一致.
  4. 被引用方法的功能需要满足当前的要求

引用静态方法

格式:类名::静态方法

范例:Integer : :parseInt

ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "1", "2", "3", "4", "5");

list.stream()
.map(Integer::parseInt)
.forEach(s -> System.out.println(s));

引用其他类中的成员变量

格式: 其他类对象::方法名

ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰");

list.stream()
.filter(new FunctionDemo3()::stringJudge)
.forEach(s -> System.out.println(s));

public boolean stringJudge(String s){
return s.startsWith("张") && s.length() == 3;
}

引用本类或父类成员方法

本类:this::方法名

父类:super::方法名

注意:引用处不能是静态方法

引用构造方法

格式:类名::new

范例:student : :new

二、异常

Exception:叫做异常,代表程序可能出现的问题。
我们通常会用Exception以及他的子类来封装程序出现的问题。

运行时异常:RuntimeException及其子类,编译阶段不会出现异常提醒。
运行时出现的异常(如:数组索引越界异常)

编译时异常:编译阶段就会出现异常提醒的。(如:日期解析异常)

方法名称说明
public String getMessage()返回此 throwable 的详细消息字符串
public String toString()返回此可抛出的简短描述
public void printStackTrace()把异常的错误信息输出在控制台

三、File

方法名称说明
public File(String pathname)根据文件路径创建文件对象
public File(String parent, String child)根据父路径名称字符串和子路径名称字符串创建文件对象
public File(File parent, String child)根据父路径对应文件对象和子路径名称字符串创建文件对象

3.1 常见方法(判断、获取)

方法名称说明
public boolean isDirectory()判断此路径名表示的File是否为文件夹
public boolean isFile()判断此路径名表示的File是否为文件
public boolean exists()判断此路径名表示的File是否存在
public long length()返回文件的大小(字节数量)
public String getAbsolutePath()返回文件的绝对路径
public String getPath()返回定义文件时使用的路径
public String getName()返回文件的名称,带后缀
public long lastModified()返回文件的最后修改时间(时间毫秒值)

3.2 常见方法(创建、删除)

方法名称说明
public boolean createNewFile()创建一个新的空的文件
public boolean mkdir()创建单级文件夹
public boolean mkdirs()创建多级文件夹
public boolean delete()删除文件、空文件夹
File f1 = new File("D:\\aaa\\ddd\\a.txt");
boolean b = f1.createNewFile();
System.out.println(b);

3.3 File的常见成员方法(获取并且遍历)

方法名称说明
public File[] listFiles()获取当前该路径下所有内容

范例

import java.io.File;

public class Test2 {
public static void main(String[] args) {
/*
* 需求:
* 定义一个方法找某一个文件夹中,是否有以avi结尾的电影。
* (暂时不需要考虑子文件夹)
*/
File file = new File("D:\\aaa");
boolean b = haveAVI(file);
System.out.println(b);
}

/*
* 作用: 用来找某一个文件夹中,是否有以avi结尾的电影
* 形参: 要查找的文件夹
* 返回值: 查找的结果 存在true 不存在false
*/
public static boolean haveAVI(File file) { // D:\\aaa
// 1.进入aaa文件夹,而且要获取里面所有的内容
File[] files = file.listFiles();

// 2.遍历数组获取里面的每一个元素
for (File f : files) {
// 3.判断是否是以avi结尾
if (f.getName().endsWith(".avi")) {
return true;
}
}
return false;
}
}

四、IO流

4.1 字节写数据

一次写一个

/*
字节输出流的细节:
1. 创建字节输出流对象
细节1: 参数是字符串表示的路径或者是File对象都是可以的
细节2: 如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的。
细节3: 如果文件已经存在,则会清空文件
2. 写数据
细节: write方法的参数是整数,但是实际写到本地文件中的是整数在ASCII上对应的字符
‘9’
‘7’
3. 释放资源
每次使用完流之后都要释放资源
*/
// 1.创建对象
FileOutputStream fos = new FileOutputStream("myio\\a.txt");
// 2.写出数据
fos.write(57);
fos.write(55);
// 3.释放资源
fos.close();

一次写多个

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;

public class WriteExamples {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("myio\\example.txt");

// 1. void write(int b) 一次写一个字节数据
fos.write(65); // 写入字节'A' (ASCII 65)

// 2. void write(byte[] b) 一次写一个字节数组数据
byte[] bytes = {66, 67, 68}; // 对应 'B', 'C', 'D'
fos.write(bytes);

// 3. void write(byte[] b, int off, int len) 一次写一个字节数组的部分数据
byte[] moreBytes = {69, 70, 71, 72, 73}; // 对应 'E','F','G','H','I'
// 从索引1开始写3个字节,也就是写 'F','G','H'
fos.write(moreBytes, 1, 3);

fos.close();
}
}

五、多线程

5.1 什么是多线程

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。

并发:在同一时刻,有多个指令在单个CPU上交替执行
并行:在同一时刻,有多个指令在多个CPU上同时执行

5.2 多线程的实现方式

5.2.1 继承Thread类的方式进行实现

package com.itheima.a01threadcase1;

/**
* 多线程的第一种启动方式:
* 1. 自定义一个类继承Thread
* 2. 重写run方法
* 3. 创建子类的对象, 并启动线程
*/
public class ThreadDemo {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}

package com.itheima.a01threadcase1;

public class MyThread extends Thread {
@Override
public void run() {
//写线程要执行代码
for (int i = 0; i < 100; i++) {
System.out.println(getName() + "HelloWorld");
}
}
}

5.2.2 实现Runnable接口的方式进行实现

package com.itheima.a02threadcase2;

public class ThreadDemo {
public static void main(String[] args) {
//创建MyRun的对象
MyRun mr = new MyRun();
//创建线程对象
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
//给线程设置名字
t1.setName("线程1");
t2.setName("线程2");
//开启线程
t1.start();
t2.start();
}
}

package com.itheima.a02threadcase2;

public class MyRun implements Runnable {
@Override
public void run() {
//写线程要执行的代码
for (int i = 0; i < 100; i++) {
//输出当前线程的名字和"HelloWorld!"
System.out.println(Thread.currentThread().getName() + "HelloWorld!");
}
}
}

5.2.3

六、反射

反射允许对成员变量,成员方法和构造方法的信息进行编程访问