Lambda 算法基础 集合概述

第一天

一、Lambda表达式

1. 核心定义

Lambda表达式是Java 8引入的新特性,本质是匿名函数(没有方法名、修饰符和返回值类型),用于简化函数式接口的实现,让代码更简洁、紧凑,专注于"做什么"而非"怎么实现"。

核心前提:Lambda表达式只能用于实现函数式接口(只有一个抽象方法的接口,可通过@FunctionalInterface注解校验)。

2. 语法格式

基础格式:(参数列表) -> { 方法体 }

说明:

  • 参数列表:参数类型可省略(类型推断),多个参数用逗号分隔,无参数则写();

  • ->:箭头符号,用于分隔参数列表和方法体;

  • 方法体:单个语句可省略{}和return(若有返回值),多个语句必须用{}包裹,且需显式写return。

3. 常见简化写法(示例)

以函数式接口Runnable(无参无返回)、Comparator(有参有返回)为例:

  • 原始匿名内部类写法: // Runnable接口实现 ``Runnable runnable = new Runnable() { `` @Override `` public void run() { `` System.out.println("匿名内部类执行"); `` } ``};

  • Lambda简化(无参无返回,方法体单语句): Runnable runnable = () -> System.out.println("Lambda执行");

  • 有参有返回(Comparator排序,省略参数类型+方法体单语句省略return): // 原始写法 ``Comparator<Integer> comparator = new Comparator<Integer>() { `` @Override `` public int compare(Integer a, Integer b) { `` return a - b; `` } ``}; ``// Lambda简化 ``Comparator<Integer> comparator = (a, b) -> a - b;

4. 核心注意事项

  • Lambda表达式没有自己的this和super,其this指向外层包围类的对象;

  • Lambda表达式中引用的外部变量,必须是final或事实上的final(即变量声明后未被修改);

  • Lambda表达式不能单独存在,必须赋值给函数式接口变量,或作为方法参数传递。

二、方法引用

1. 核心定义

方法引用是Lambda表达式的"语法糖",当Lambda表达式的方法体,只是调用一个已存在的方法(无需额外逻辑)时,可通过方法引用简化写法,让代码更简洁、可读性更强。

核心逻辑:方法引用本质是"引用一个方法,代替Lambda的方法体",其签名(参数列表、返回值)必须与函数式接口的抽象方法签名一致。

2. 语法格式

方法引用的格式分为4种,核心是类名/对象名 :: 方法名(::为方法引用运算符),具体如下:

(1)对象::实例方法

场景:调用某个对象的实例方法,Lambda的参数列表与该实例方法的参数列表一致。

复制代码
// 示例:调用String对象的length()方法(无参有返回)
// Lambda写法
Function<String, Integer> function = s -> s.length();
// 方法引用写法(String对象s的实例方法length)
Function<String, Integer> function = String::length;

(2)类::静态方法

场景:调用某个类的静态方法,Lambda的参数列表与该静态方法的参数列表一致。

复制代码
// 示例:调用Integer类的parseInt()方法(有参有返回)
// Lambda写法
Function<String, Integer> function = s -> Integer.parseInt(s);
// 方法引用写法(Integer类的静态方法parseInt)
Function<String, Integer> function = Integer::parseInt;

(3)类::实例方法

场景:调用某个类的实例方法,但Lambda的第一个参数是该类的对象(即方法的调用者),后续参数是该实例方法的参数。

复制代码
// 示例:调用String类的equals()方法(有参有返回,第一个参数是调用者)
// Lambda写法
BiFunction<String, String, Boolean> biFunction = (s1, s2) -> s1.equals(s2);
// 方法引用写法(String类的实例方法equals,s1是调用者)
BiFunction<String, String, Boolean> biFunction = String::equals;

(4)构造器引用:类::new

场景:Lambda的方法体是创建一个对象(调用构造器),Lambda的参数列表与该构造器的参数列表一致。

复制代码
// 示例:调用String的构造器(String(char[] value))
// Lambda写法
Function<char[], String> function = chars -> new String(chars);
// 构造器引用写法
Function<char[], String> function = String::new;

3. 核心注意事项

  • 方法引用不能单独使用,必须配合函数式接口,且方法签名(参数、返回值)需与接口抽象方法一致;

  • 方法引用是Lambda的简化,只有当Lambda方法体仅调用一个现有方法时,才能使用;

  • 构造器引用本质是"引用构造器",等价于Lambda中new对象的逻辑,支持重载(根据函数式接口的抽象方法签名匹配对应构造器)。

三、Lambda与方法引用的关联

  1. 方法引用是Lambda的简化形式,所有方法引用都能转化为对应的Lambda表达式,但反之不一定(Lambda方法体有复杂逻辑时,无法用方法引用);

  2. 两者的核心目的一致:简化函数式接口的实现,减少冗余代码,提升代码可读性和简洁性;

  3. 使用优先级:能使用方法引用时,优先使用(更简洁);Lambda适合方法体有自定义逻辑的场景。

四、核心总结

  • Lambda:匿名函数,简化函数式接口实现,格式为 (参数) -> 方法体;

  • 方法引用:Lambda的语法糖,格式为 类名/对象名::方法名,分4种场景;

  • 核心前提:两者都依赖函数式接口(仅一个抽象方法)。

第二天

一、算法基础

1. 核心定义

算法是解决特定问题的步骤集合,是对问题求解过程的精准描述,具有明确的输入、输出和有限性(步骤有限且可终止)、确定性(每一步操作唯一)、可行性(每一步可通过有限操作实现)的特点。

简单来说,算法就是"解决问题的思路和步骤",比如计算两数之和、排序数组,都有对应的算法。

2. 算法的核心特性(必记)

  • 输入:算法有0个或多个输入(如排序算法需要输入待排序数组);

  • 输出:算法至少有1个输出(求解结果),输出与输入对应;

  • 有限性:算法的步骤是有限的,不会无限循环,最终会终止;

  • 确定性:每一步操作都有明确的含义,不会出现歧义;

  • 可行性:每一步操作都可以通过计算机或人工有限次执行完成。

3. 常见基础算法(入门必备)

(1)排序算法(最常用)

核心目的:将一组数据按指定顺序(升序/降序)排列,重点掌握3种基础排序:

  • 冒泡排序 :通过相邻元素两两比较、交换,将最大/最小元素逐步"冒泡"到数组末尾,简单易理解,效率较低(适合少量数据)。 // 冒泡排序示例(升序) ``public static void bubbleSort(int[] arr) { `` int n = arr.length; `` for (int i = 0; i < n - 1; i++) { `` for (int j = 0; j < n - 1 - i; j++) { `` if (arr[j] > arr[j + 1]) { `` // 交换相邻元素 `` int temp = arr[j]; `` arr[j] = arr[j + 1]; `` arr[j + 1] = temp; `` } `` } `` } ``}

  • 选择排序:每次从待排序区间选择最小/最大元素,放到已排序区间的末尾,效率略高于冒泡排序。

  • 插入排序:将待排序元素逐个插入到已排序区间的合适位置,类似整理扑克牌,适合数据基本有序的场景。

(2)查找算法

  • 顺序查找:从数组开头依次遍历,查找目标元素,简单但效率低(适合无序数组);

  • 二分查找 :仅适用于有序数组,通过不断将查找区间减半,快速定位目标元素,效率高(时间复杂度O(log₂n))。

4. 算法的时间复杂度与空间复杂度

用于衡量算法的效率,是算法设计的核心评估指标:

  • 时间复杂度 :描述算法执行所需的时间与输入规模的关系,常用大O表示(忽略常数、低次项和系数),比如O(1)(常数时间)、O(n)(线性时间)、O(n²)(平方时间)。

  • 空间复杂度 :描述算法执行所需的额外空间与输入规模的关系,同样用大O表示,比如冒泡排序空间复杂度为O(1)(仅用临时变量),递归算法通常空间复杂度较高。

二、异常(Java)

1. 核心定义

异常是程序运行过程中出现的意外情况(如除数为0、数组下标越界、文件找不到),会导致程序正常执行流程中断。Java中通过异常机制,捕获并处理这些意外,避免程序崩溃。

异常本质是Java中的对象,继承自Throwable类,分为两大类:Error(错误)和Exception(异常)。

2. 异常的分类(核心区分)

(1)Error(错误)

由JVM生成,程序无法处理,通常是严重的系统级错误(如内存溢出、虚拟机错误),一般不需要编写代码捕获,比如OutOfMemoryError(内存溢出)、StackOverflowError(栈溢出)。

(2)Exception(异常)

程序可以处理的异常,也是我们日常开发中重点关注的类型,分为两大类:

  • 编译时异常(受检异常):编译阶段就会报错,必须手动处理(捕获或抛出),否则无法编译,比如IOException(文件操作异常)、SQLException(数据库操作异常)。

  • 运行时异常(非受检异常):编译阶段不报错,运行时才会出现,可选择处理或不处理,比如NullPointerException(空指针异常)、ArrayIndexOutOfBoundsException(数组下标越界)、ArithmeticException(除数为0)。

3. 异常的处理方式(3种)

(1)try-catch捕获异常

核心:尝试执行可能出现异常的代码,若出现异常,捕获并处理,避免程序崩溃,格式如下:

复制代码
try {
    // 可能出现异常的代码(如除数为0)
    int a = 10 / 0;
} catch (ArithmeticException e) {
    // 捕获指定类型的异常,进行处理
    System.out.println("异常原因:" + e.getMessage()); // 输出异常信息
} finally {
    // 可选:无论是否出现异常,都会执行(常用于释放资源,如关闭文件、连接)
    System.out.println("无论是否异常,都会执行");
}

说明:可多个catch捕获不同类型异常(从子类到父类),finally可选,但若有return,finally会在return之前执行。

(2)throws抛出异常

核心:不处理异常,将异常抛给调用者,由调用者处理,格式:在方法声明后加throws 异常类型。

复制代码
// 抛出编译时异常,由调用该方法的代码处理
public static void readFile() throws IOException {
    // 可能出现IOException的代码
    FileReader fr = new FileReader("test.txt");
}

(3)throw手动抛出异常

核心:手动创建异常对象,主动抛出,用于自定义异常场景(如参数校验)。

复制代码
public static void checkAge(int age) {
    if (age < 0 || age > 120) {
        // 手动抛出运行时异常
        throw new IllegalArgumentException("年龄不合法:" + age);
    }
}

4. 自定义异常

当Java自带的异常无法满足需求时,可自定义异常,步骤:

  1. 继承Exception(编译时异常)或RuntimeException(运行时异常);

  2. 编写构造方法(默认无参、带异常信息的构造);

  3. 在需要的地方,用throw抛出自定义异常。

复制代码
// 自定义编译时异常
public class MyException extends Exception {
    // 无参构造
    public MyException() {}
    // 带异常信息的构造
    public MyException(String message) {
        super(message);
    }
}
​
// 使用自定义异常
public static void checkScore(int score) throws MyException {
    if (score < 0 || score > 100) {
        throw new MyException("分数不合法:" + score);
    }
}

5. 异常处理的注意事项

  • 捕获异常时,要避免使用catch (Exception e) 捕获所有异常(无法精准定位问题);

  • finally中避免使用return,否则会覆盖try或catch中的return值;

  • 运行时异常可不用强制处理,但编译时异常必须处理(捕获或抛出);

  • 异常处理的核心是"精准捕获、合理处理",避免程序崩溃,同时便于排查问题。

三、核心总结

  1. 算法:解决问题的步骤集合,有5大特性,重点掌握基础排序和查找算法,关注时间/空间复杂度;

  2. 异常:程序运行中的意外情况,分为Error(不可处理)和Exception(可处理),3种处理方式(try-catch、throws、throw),可自定义异常;

  3. 核心原则:算法追求高效(低时间/空间复杂度),异常追求精准处理(避免程序崩溃、便于排查)

第三天

一、集合概述(前置基础)

集合是Java中用于存储多个数据的容器,区别于数组(固定长度、只能存储同一种基本类型/引用类型),集合长度可变,可存储不同类型的对象(本质存储对象引用),核心接口是Collection,List和Set是Collection的两大核心子接口。

核心特点:集合只存储对象,不存储基本数据类型(需使用包装类,如int→Integer);长度可动态增减;提供了丰富的方法(添加、删除、遍历、查找等),简化数据操作。

Collection核心通用方法(List和Set均适用):

  • add(E e):添加单个元素,返回boolean(添加成功为true);

  • remove(Object o):删除指定元素,返回boolean;

  • size():返回集合中元素的个数;

  • isEmpty():判断集合是否为空;

  • clear():清空集合中所有元素;

  • contains(Object o):判断集合中是否包含指定元素。

二、List集合

1. 核心定义与特点

List是Collection的子接口,代表有序、可重复的集合,元素有明确的索引(类似数组的下标),可通过索引快速访问、插入、删除元素,适合需要"有序存储、可重复、按索引操作"的场景。

核心特点:有序(元素存入顺序与取出顺序一致)、可重复(允许存储多个相同的元素)、有索引(从0开始,依次递增)。

2. List接口的常用实现类(3个核心)

(1)ArrayList(最常用)

底层基于数组实现,查询效率高(通过索引直接访问,时间复杂度O(1)),增删效率低(需移动数组元素,时间复杂度O(n)),线程不安全,效率高,适合"查询频繁、增删较少"的场景(如展示列表数据)。

复制代码
// ArrayList示例
List<String> list = new ArrayList<>();
// 添加元素
list.add("Java");
list.add("Python");
list.add("Java"); // 允许重复
// 按索引插入元素(指定位置插入)
list.add(1, "C++"); // 插入后:[Java, C++, Python, Java]
// 按索引访问元素
String element = list.get(0); // 结果:Java
// 按索引修改元素
list.set(2, "JavaScript"); // 修改后:[Java, C++, JavaScript, Java]
// 按索引删除元素
list.remove(3); // 删除索引3的元素,返回删除的元素
// 遍历集合(三种方式)
// 1. 普通for循环(利用索引,最常用)
for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}
// 2. 增强for循环(foreach,无需索引)
for (String str : list) {
    System.out.println(str);
}
// 3. 迭代器遍历(安全遍历,可在遍历中删除元素)
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String str = iterator.next();
    if ("C++".equals(str)) {
        iterator.remove(); // 迭代器删除,避免并发修改异常
    }
}

(2)LinkedList

底层基于双向链表实现,查询效率低(需从头/尾遍历查找,时间复杂度O(n)),增删效率高(只需修改链表节点的指针,时间复杂度O(1)),线程不安全,适合"增删频繁、查询较少"的场景(如队列、栈的实现)。

额外特性:LinkedList还实现了Deque接口,可作为队列(先进先出)、栈(先进后出)使用,提供了poll()、push()、pop()等方法。

(3)Vector

底层基于数组实现,与ArrayList功能基本一致,但线程安全(方法加了synchronized锁),效率低,目前已基本被ArrayList替代,仅在多线程场景下偶尔使用。

3. List集合的特有方法(区别于Set)

因List有索引,所以拥有Set没有的、基于索引的操作方法:

  • get(int index):获取指定索引的元素;

  • set(int index, E e):修改指定索引的元素,返回被修改的旧元素;

  • add(int index, E e):在指定索引插入元素,后续元素后移;

  • remove(int index):删除指定索引的元素,返回被删除的元素;

  • indexOf(Object o):返回指定元素在集合中第一次出现的索引,没有则返回-1;

  • lastIndexOf(Object o):返回指定元素在集合中最后一次出现的索引,没有则返回-1。

4. List集合的注意事项

  • ArrayList和LinkedList均线程不安全,多线程环境下需手动加锁,或使用Collections.synchronizedList()包装;

  • 遍历ArrayList时,优先使用普通for循环(效率高);遍历LinkedList时,优先使用增强for或迭代器(避免频繁通过索引查找);

  • 避免在增强for循环中修改集合(添加/删除元素),会抛出ConcurrentModificationException(并发修改异常),需使用迭代器删除。

三、Set集合

1. 核心定义与特点

Set是Collection的子接口,代表无序、不可重复的集合,元素没有索引,无法通过索引访问元素,适合需要"去重存储、无需按顺序访问"的场景(如存储唯一标识、去重数据)。

核心特点:无序(元素存入顺序与取出顺序不一定一致,底层存储无序)、不可重复(不允许存储两个相等的元素,equals()方法判断相等)、无索引(无法通过下标访问)。

补充:Set判断元素是否重复的规则:先通过hashCode()方法判断哈希值,若哈希值不同,则元素不同;若哈希值相同,再通过equals()方法判断,若equals()返回true,则元素重复,不添加;若返回false,则添加。

2. Set接口的常用实现类(3个核心)

(1)HashSet(最常用)

底层基于**哈希表(HashMap)**实现,无序、不可重复,查询和增删效率都很高(时间复杂度O(1)),线程不安全,适合"去重、高效操作"的常规场景。

复制代码
// HashSet示例
Set<String> set = new HashSet<>();
// 添加元素(不可重复,重复元素添加失败)
set.add("Java");
set.add("Python");
set.add("Java"); // 重复元素,添加失败,集合中仍只有1个Java
// 删除元素
set.remove("Python"); // 删除成功返回true,不存在返回false
// 遍历集合(两种方式,无索引,无法用普通for循环)
// 1. 增强for循环
for (String str : set) {
    System.out.println(str); // 输出顺序可能与添加顺序不一致
}
// 2. 迭代器遍历
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}
// 判断元素是否存在
boolean hasJava = set.contains("Java"); // 结果:true

(2)LinkedHashSet

底层基于哈希表+双向链表 实现,是HashSet的子类,特点:有序、不可重复(有序指"存入顺序与取出顺序一致"),查询和增删效率略低于HashSet,线程不安全,适合"去重且需要保持插入顺序"的场景。

(3)TreeSet

底层基于红黑树 实现,无序(不保证插入顺序)、不可重复,但会对元素进行自然排序(默认升序),也可自定义排序规则,查询和增删效率中等(时间复杂度O(log n)),线程不安全,适合"去重且需要排序"的场景。

复制代码
// TreeSet示例(自然排序,String类型默认按字典序升序)
Set<String> treeSet = new TreeSet<>();
treeSet.add("Banana");
treeSet.add("Apple");
treeSet.add("Cherry");
// 遍历输出:Apple、Banana、Cherry(自然排序后)
for (String str : treeSet) {
    System.out.println(str);
}
​
// 自定义排序(如整数降序)
Set<Integer> numSet = new TreeSet<>(new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2 - o1; // 降序排序
    }
});
numSet.add(3);
numSet.add(1);
numSet.add(2);
// 遍历输出:3、2、1

3. Set集合的注意事项

  • HashSet、LinkedHashSet、TreeSet均线程不安全,多线程环境下需使用Collections.synchronizedSet()包装;

  • HashSet存储自定义对象时,需重写hashCode()和equals()方法,否则无法实现去重(默认使用Object类的方法,判断地址是否相同);

  • TreeSet存储自定义对象时,需让对象实现Comparable接口(重写compareTo()方法),或创建TreeSet时传入Comparator,否则会抛出ClassCastException(类型转换异常);

  • Set无索引,无法使用普通for循环遍历,只能用增强for、迭代器遍历。

四、List集合与Set集合的核心区别(重点)

对比维度 List集合 Set集合
有序性 有序(存入与取出顺序一致) 无序(HashSet、TreeSet);LinkedHashSet有序
可重复性 可重复 不可重复
索引 有索引,可通过索引操作 无索引,无法通过索引操作
底层实现 ArrayList(数组)、LinkedList(链表) HashSet(哈希表)、TreeSet(红黑树)
适用场景 需有序、可重复、按索引操作(如列表展示) 需去重、无需索引(如唯一标识存储)

五、核心总结

  1. List和Set均继承自Collection,拥有Collection的所有通用方法;

  2. List核心:有序、可重复、有索引,重点掌握ArrayList和LinkedList的区别;

  3. Set核心:不可重复、无索引,重点掌握HashSet(常规去重)、TreeSet(排序去重)的使用;

  4. 选型原则:需有序/按索引操作→用List;需去重→用Set;需排序去重→TreeSet;需保持插入顺序去重→LinkedHashSet。

相关推荐
SmallBambooCode1 小时前
【人工智能】【Python】离线环境下huggingface预训练权重导入流程
开发语言·人工智能·python
Yeh2020581 小时前
Mybatis笔记一
java·笔记·mybatis
likerhood1 小时前
Java 动态代理深度解析:从“为什么“到“底层原理“
java
_阿伟_1 小时前
信息检索简单介绍
java
下次再写1 小时前
深入浅出微服务架构:从理论到Spring Boot实战
java·微服务·springboot·springcloud·架构设计·后端开发·分布式系统
夕除2 小时前
spring boot--08
开发语言·windows·python
进阶的猿猴2 小时前
Rsa简单实现接口到期限制(springBoot)
java·spring boot·后端
狐狐生风2 小时前
Python UV 完整安装教程
开发语言·python·uv