Java 集合与函数式编程知识整理
Java 集合与函数式编程知识整理
1. 函数式接口
1.1 概念
有且仅有一个抽象方法的接口,称为函数式接口。
可使用 @FunctionalInterface 注解进行检测。
1.2 常用函数式接口
| 接口 | 抽象方法 | 作用 |
|---|---|---|
Supplier<T> | T get() | 供给型,生产数据 |
Consumer<T> | void accept(T t) | 消费型,操作数据 |
Function<T, R> | R apply(T t) | 转换型,类型转换 |
Predicate<T> | boolean test(T t) | 断言型,判断 |
Supplier 示例
public static void main(String[] args) {
method(() -> {
int[] arr = {5, 34, 4, 5, 76, 7};
Arrays.sort(arr);
return arr[arr.length - 1];
});
}
public static void method(Supplier<Integer> supplier) {
System.out.println(supplier.get());
}
Consumer 示例
public static void main(String[] args) {
method(s -> System.out.println(s.length()), "abcdefg");
}
public static void method(Consumer<String> consumer, String s) {
consumer.accept(s);
}
Function 示例
public static void main(String[] args) {
method(integer -> integer + "", 1000);
}
public static void method(Function<Integer, String> function, int a) {
String s = function.apply(a);
System.out.println(s + 1); // 10001
}
Predicate 示例
public static void main(String[] args) {
method(s -> s.length() == 7, "abcdefg");
}
public static void method(Predicate<String> predicate, String s) {
boolean test = predicate.test(s);
System.out.println(test);
}
- Stream 流
Stream 是 Java 8 引入的"流水线"式处理数据的工具,与 IO 流无关,主要用于对集合或数组进行过滤、映射、归约等操作。
2.1 获取 Stream
数组:Stream.of(T... values)
单列集合:调用 stream() 方法(Collection 接口提供)
// 数组
Stream<String> stream1 = Stream.of("a", "b", "c");
// 集合
ArrayList<String> list = new ArrayList<>();
Stream<String> stream2 = list.stream();
2.2 常用方法
方法 描述 类型
forEach(Consumer) 逐一处理(遍历) 终结方法
count() 统计元素个数 终结方法
filter(Predicate) 过滤,返回新流 中间方法
limit(long n) 取前 n 个元素 中间方法
skip(long n) 跳过前 n 个元素 中间方法
concat(Stream a, Stream b) 合并两个流(静态) 中间方法
map(Function) 类型转换,返回新流 中间方法
distinct() 去重(依赖 hashCode/equals) 中间方法
collect(Collectors.toList()) 将流转为 List 集合 终结方法
Stream<String> stream = Stream.of("熊出没", "数码宝贝", "神厨小福贵");
List<String> result = stream
.filter(s -> s.length() > 3)
.collect(Collectors.toList());
System.out.println(result); // [数码宝贝, 神厨小福贵]
示例:map 类型转换
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
stream.map(integer -> integer + "")
.forEach(s -> System.out.println(s + 1));
// 输出:11 21 31 41 51
2.3 综合练习
两个队伍名单处理:
-
队伍一保留名字为 3 个字的成员,取前 3 人;
-
队伍二保留姓张的成员,跳过前 2 人;
-
合并两队并打印。
ArrayList<String> one = new ArrayList<>(Arrays.asList("迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子", "洪七公"));
ArrayList<String> two = new ArrayList<>(Arrays.asList("古力娜扎", "张无忌", "张三丰", "赵丽颖", "张二狗", "张天爱", "张三"));
Stream<String> streamOne = one.stream().filter(s -> s.length() == 3).limit(3);
Stream<String> streamTwo = two.stream().filter(s -> s.startsWith("张")).skip(2);
Stream.concat(streamOne, streamTwo).forEach(System.out::println);
- 方法引用
方法引用是 Lambda 表达式的进一步简化,要求被引用方法的参数和返回值与重写方法一致。
3.1 引用规则
-
对象名引用成员方法:对象::成员方法
-
类名引用静态方法:类名::静态方法
-
构造方法引用:类名::new
-
数组引用:类型[]::new
示例:对象名引用成员方法
// Lambda 写法
method(() -> " abcdefg ".trim());
// 方法引用
method(" abcdefg "::trim);
public static void method(Supplier<String> supplier) {
System.out.println(supplier.get());
}
示例:类名引用静态方法
method(Math::random); // Supplier<Double> 引用 Math.random()
示例:构造方法引用
// Function<String, Person>
method(Person::new, "张三");
public static void method(Function<String, Person> function, String name) {
Person person = function.apply(name);
System.out.println(person);
}
示例:数组引用
// Function<Integer, int[]>
method(int[]::new, 10);
public static void method(Function<Integer, int[]> function, int len) {
int[] arr = function.apply(len);
System.out.println(arr.length);
}
-
集合框架概述
Java 集合框架提供了一组接口和类,用于存储和操作对象。 -
单列集合:每个位置只存储一个元素(Collection)
-
双列集合:每个元素由键值对组成(Map)
-
集合与数组的区别:
集合长度可变,数组定长
集合只能存储引用类型(基本类型会自动装箱)
集合提供丰富的方法(add、remove、contains 等)
Collection 接口
Collection 是单列集合的根接口,其子接口包括 List 和 Set。
5.1 常用方法
方法 描述
boolean add(E e) 添加元素
boolean addAll(Collection c) 添加另一个集合的所有元素
void clear()清空集合
boolean contains(Object o) 是否包含指定元素
boolean isEmpty() 集合是否为空
boolean remove(Object o) 移除指定元素
int size() 返回元素个数
Object[] toArray() 转换为 Object 数组
5.2 使用示例
Collection<String> collection = new ArrayList<>();
collection.add("张三");
collection.add("李四");
collection.addAll(Arrays.asList("王五", "赵六"));
System.out.println(collection.size()); // 4
System.out.println(collection.contains("张三")); // true
collection.remove("王五");
for (String s : collection) {
System.out.println(s);
}
-
迭代器 Iterator
6.1 基本使用
通过 iterator() 方法获取迭代器,用于遍历集合。`hasNext()`:判断是否存在下一个元素 `next()`:返回下一个元素
ArrayList<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
注意:迭代次数不能超过元素个数,否则会抛出 NoSuchElementException。
6.2 并发修改异常
使用迭代器遍历集合时,若直接通过集合对象的 add/remove 方法修改集合,会引发 ConcurrentModificationException。
错误示例:
ArrayList<String> list = new ArrayList<>(Arrays.asList("唐僧", "孙悟空", "猪八戒", "沙僧"));
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if ("猪八戒".equals(it.next())) {
list.add("白龙马"); // 错误:使用集合自身的 add
}
}
正确做法:使用 ListIterator (List 专有)提供的 add/remove 方法。
ListIterator<String> lit = list.listIterator();
while (lit.hasNext()) {
if ("猪八戒".equals(lit.next())) {
lit.add("白龙马"); // 通过迭代器自身操作
}
}
6.3 实现原理
不同集合的 iterator() 返回不同的内部类实现。例如 ArrayList 返回一个名为 Itr 的内部类,其通过 modCount 与 expectedModCount 的比较来检测并发修改。
-
数据结构基础
7.1 常见数据结构栈:先进后出(LIFO),像手枪弹夹。队列:先进先出(FIFO),像排队。数组:查询快(通过索引),增删慢(需要移动元素)。链表:查询慢(需要遍历),增删快(只需修改指针)。单向链表:节点包含数据域和指向下一节点的指针。双向链表:节点包含数据域、指向前一节点和后一节点的指针。 -
List 接口及其实现类
8.1 List 接口
特点:元素有序、有索引、可重复。
常用实现类:ArrayList、LinkedList、Vector
通用方法:
方法 描述
void add(int index, E element) 指定位置插入
E remove(int index) 删除指定索引元素并返回
E set(int index, E element) 修改指定索引元素
E get(int index) 获取指定索引元素
int size() 元素个数
遍历方式
// 1. 普通 for
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
// 2. 增强 for
for (String s : list) {
System.out.println(s);
}
// 3. 迭代器
Iterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
8.2 ArrayList
数据结构:数组
特点:查询快,增删慢;线程不安全。
扩容机制:
-
无参构造:首次添加元素时,数组初始化为长度 10;后续扩容为原容量的 1.5 倍。 -
有参构造:可指定初始容量。
ArrayList<Integer> list = new ArrayList<>();
// 删除时注意:直接传 int 会被当作索引,需装箱
list.remove(Integer.valueOf(2));
8.3 LinkedList
数据结构:双向链表
特点:查询慢,增删快;无索引;线程不安全。
特有方法(主要操作首尾):
方法 描述
addFirst(E e) 头部添加
addLast(E e) 尾部添加
getFirst() 获取第一个元素
getLast()获取最后一个元素
removeFirst() 移除并返回第一个元素
removeLast() 移除并返回最后一个元素
push(E e) 压栈(头部添加)
pop() 弹栈(头部移除并返回)
LinkedList<String> list = new LinkedList<>();
list.addLast("C");
list.addFirst("A");
System.out.println(list); // [A, C]
list.push("First");
System.out.println(list.pop()); // First
8.4 Vector(了解)
数据结构:数组
特点:线程安全,其余与 ArrayList 类似。
扩容:无参构造默认容量 10,扩容为 2 倍;有参构造可按指定增量扩容。
8.5 增强 for 循环
格式:for (元素类型 变量名 : 数组或集合)
快捷键:集合名.for
底层原理:
数组:编译后为普通 for 循环
集合:编译后为迭代器,因此遍历时不可通过集合对象修改长度。
- Collections 工具类
说明:操作集合的工具类,构造方法私有,所有方法为静态。
常用方法:
方法 描述
static <T> boolean addAll(Collection<? super T> c, T... elements) 批量添加元素
static void shuffle(List<?> list) 随机打乱顺序
static <T> void sort(List<T> list) 按自然顺序排序(元素实现 Comparable)
static <T> void sort(List<T> list, Comparator<? super T> c) 按指定比较器排序
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "小猫", "小狗", "小羊");
Collections.shuffle(list);
Collections.sort(list);
// 自定义对象排序
Collections.sort(list, (o1, o2) -> o1.getAge() - o2.getAge());
9.1 Comparable 与 Comparator
Comparable:定义类的默认排序规则,实现 compareTo() 方法;
Comparator:外部比较器,可在不修改类的情况下自定义多种排序规则。
// 实现 Comparable 接口
public class Student implements Comparable<Student> {
private int score;
public int compareTo(Student o) {
return this.score - o.score;
}
}
// 外部 Comparator
Collections.sort(list, Comparator.comparingInt(Student::getScore));
Arrays.asList(T... a) 返回固定大小的 List,不可修改长度。
-
Set 接口及其实现类
特点:元素不可重复,无索引。所有 Set 底层均依赖 Map 实现。
10.1 HashSet
特点:无序、无索引、元素唯一、线程不安全。
数据结构:哈希表(数组 + 链表 + 红黑树)
HashSet<String> set = new HashSet<>();
set.add("apple");
set.add("banana");
set.add("apple");
System.out.println(set); // [banana, apple]
10.2 LinkedHashSet
特点:有序(保持插入顺序)、无索引、元素唯一、线程不安全。
数据结构:哈希表 + 双向链表
LinkedHashSet<String> set = new LinkedHashSet<>();
set.add("C");
set.add("A");
set.add("B");
System.out.println(set); // [C, A, B]
10.3 TreeSet
特点:可排序、无索引、元素唯一、线程不安全。
数据结构:红黑树
自然排序:TreeSet() 按元素的自然顺序
定制排序:TreeSet(Comparator)
TreeSet<Person> set = new TreeSet<>((o1, o2) -> o1.getAge() - o2.getAge());
set.add(new Person("张三", 18));
set.add(new Person("李四", 15));
10.4 元素唯一性原理
先比较 hashCode();
哈希不同 → 直接存储;
哈希相同 → 比较 equals();
equals 返回 true → 视为重复,不存储;
equals 返回 false → 通过链表/红黑树存储。
结论:自定义对象必须同时重写 hashCode() 和 equals() 方法。
- 泛型
11.1 为什么使用泛型
将运行时 ClassCastException 提前到编译期;
使代码更通用、更安全。
11.2 含有泛型的类
public class MyArrayList<E> {
private Object[] arr = new Object[10];
public void add(E e) { ... }
public E get(int index) { ... }
}
// 使用
MyArrayList<String> list = new MyArrayList<>();
11.3 含有泛型的方法
public static <E> void print(E e) {
System.out.println(e);
}
11.4 含有泛型的接口
public interface MyList<E> {
void add(E e);
}
// 方式1:实现时确认类型
public class MyStringList implements MyList<String> { }
// 方式2:继续泛型
public class MyArrayList<E> implements MyList<E> { }
11.5 泛型通配符 ?
public void printList(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}
11.6 上下限
? extends 类:接收该类及其子类(上限)
? super 类:接收该类及其父类(下限)
public void method(List<? extends Number> list) { } // Number 及子类
public void method(List<? super Number> list) { } // Number 及父类
- Map 集合
双列集合,每个元素由键(Key)和值(Value)组成,键不可重复。
12.1 HashMap
特点:无序、无索引、键唯一、值可重复、允许 null 键和 null 值、线程不安全。
数据结构:哈希表
常用方法
方法 描述
V put(K key, V value) 添加/覆盖,返回旧值
V remove(Object key) 根据键删除,返回被删值
V get(Object key) 根据键获取值
boolean containsKey(Object key) 是否包含指定键
Set<K> keySet() 获取所有键的 Set 集合
Set<Map.Entry<K,V>> entrySet()获取所有键值对
Collection<V> values() 获取所有值的 Collection
遍历方式
HashMap<String, String> map = new HashMap<>();
map.put("1", "苹果");
map.put("2", "香蕉");
// 方式1:keySet + get
for (String key : map.keySet()) {
System.out.println(key + " = " + map.get(key));
}
// 方式2:entrySet
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
12.2 LinkedHashMap
特点:有序(插入顺序)、其余同 HashMap。
数据结构:哈希表 + 双向链表
12.3 TreeMap
特点:可以对键排序、键唯一、值可重复、键不能为 null。
数据结构:红黑树
排序规则:自然排序或构造函数传入 Comparator。
TreeMap<Person, String> map = new TreeMap<>((o1, o2) -> o2.getAge() - o1.getAge());
map.put(new Person("张三", 18), "北京");
12.4 Hashtable(了解)
特点:线程安全、不允许 null 键和值,其余类似 HashMap。
| 功能 | HashMap | Hashtable |
|---|---|---|
| 线程安全 | 否 | 是 |
| null 键/值 | 允许 | 不允许 |
| 效率 | 高 | 低 |
12.5 Properties
说明:Hashtable 的子类,键和值都必须是 String 类型。
主要用途:读取配置文件(.properties)
示例:读取 resources/config.properties
文件内容:
properties
username=root
password=123456
Java 代码:
Properties props = new Properties();
InputStream in = Demo.class.getClassLoader().getResourceAsStream("config.properties");
props.load(in);
System.out.println(props.getProperty("username")); // root
- 集合嵌套
13.1 List 嵌套 List
List<String> list1 = Arrays.asList("张三", "李四");
List<String> list2 = Arrays.asList("王五", "赵六");
List<List<String>> bigList = new ArrayList<>();
bigList.add(list1); bigList.add(list2);
13.2 List 嵌套 Map
Map<String, String> map1 = new HashMap<>();
map1.put("1", "小明");
List<Map<String, String>> list = new ArrayList<>();
list.add(map1);
13.3 Map 嵌套 Map
// 结构:课程名 -> (学号 -> 姓名)
Map<String, Map<Integer, String>> school = new HashMap<>();
Map<Integer, String> javaSE = new HashMap<>();
javaSE.put(1, "张三");
school.put("JavaSE", javaSE);
- 哈希表底层原理
14.1 存储结构
JDK 8 开始:数组 + 链表 + 红黑树
14.2 重要参数
默认初始容量:16
加载因子:0.75(达到容量的 75% 时扩容)
扩容:容量变为原来的 2 倍
树化阈值:链表长度 ≥ 8 且数组长度 ≥ 64 时,转红黑树
退化阈值:红黑树节点数 ≤ 6 时,转回链表
14.3 添加元素流程
根据 key 的 hashCode() 计算哈希值。
通过哈希值确定数组索引。
若该索引无元素,直接放入。
若冲突,通过 equals 判断 key 是否相同:
相同则覆盖 value;
不同则以链表或红黑树形式存储。
更多推荐



所有评论(0)