欢迎光临
我们一直在努力

03-Java常用API与集合框架

03-Java常用API与集合框架

学习目标

  • 掌握String类的常用方法和使用技巧
  • 理解String、StringBuilder、StringBuffer的区别
  • 掌握日期时间API的使用
  • 掌握Math和Random类的常用方法
  • 深入理解Java集合框架的体系结构
  • 掌握List、Set、Map的使用和区别
  • 掌握集合的遍历和排序方法
  • 理解集合的底层实现原理
  • 能够根据场景选择合适的集合类型
  • 掌握集合的常见操作和最佳实践

知识结构思维导图

#mermaid-svg-lMnT9kfBRDOvqzgd{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-lMnT9kfBRDOvqzgd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-lMnT9kfBRDOvqzgd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-lMnT9kfBRDOvqzgd .error-icon{fill:#552222;}#mermaid-svg-lMnT9kfBRDOvqzgd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lMnT9kfBRDOvqzgd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-lMnT9kfBRDOvqzgd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lMnT9kfBRDOvqzgd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lMnT9kfBRDOvqzgd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-lMnT9kfBRDOvqzgd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lMnT9kfBRDOvqzgd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lMnT9kfBRDOvqzgd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lMnT9kfBRDOvqzgd .marker.cross{stroke:#333333;}#mermaid-svg-lMnT9kfBRDOvqzgd svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lMnT9kfBRDOvqzgd p{margin:0;}#mermaid-svg-lMnT9kfBRDOvqzgd .edge{stroke-width:3;}#mermaid-svg-lMnT9kfBRDOvqzgd .section–1 rect,#mermaid-svg-lMnT9kfBRDOvqzgd .section–1 path,#mermaid-svg-lMnT9kfBRDOvqzgd .section–1 circle,#mermaid-svg-lMnT9kfBRDOvqzgd .section–1 polygon,#mermaid-svg-lMnT9kfBRDOvqzgd .section–1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-lMnT9kfBRDOvqzgd .section–1 text{fill:#ffffff;}#mermaid-svg-lMnT9kfBRDOvqzgd .node-icon–1{font-size:40px;color:#ffffff;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-edge–1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-lMnT9kfBRDOvqzgd .edge-depth–1{stroke-width:17;}#mermaid-svg-lMnT9kfBRDOvqzgd .section–1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled circle,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:lightgray;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:#efefef;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-0 rect,#mermaid-svg-lMnT9kfBRDOvqzgd .section-0 path,#mermaid-svg-lMnT9kfBRDOvqzgd .section-0 circle,#mermaid-svg-lMnT9kfBRDOvqzgd .section-0 polygon,#mermaid-svg-lMnT9kfBRDOvqzgd .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-lMnT9kfBRDOvqzgd .section-0 text{fill:black;}#mermaid-svg-lMnT9kfBRDOvqzgd .node-icon-0{font-size:40px;color:black;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-lMnT9kfBRDOvqzgd .edge-depth-0{stroke-width:14;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled circle,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:lightgray;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:#efefef;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-1 rect,#mermaid-svg-lMnT9kfBRDOvqzgd .section-1 path,#mermaid-svg-lMnT9kfBRDOvqzgd .section-1 circle,#mermaid-svg-lMnT9kfBRDOvqzgd .section-1 polygon,#mermaid-svg-lMnT9kfBRDOvqzgd .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-lMnT9kfBRDOvqzgd .section-1 text{fill:black;}#mermaid-svg-lMnT9kfBRDOvqzgd .node-icon-1{font-size:40px;color:black;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-lMnT9kfBRDOvqzgd .edge-depth-1{stroke-width:11;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled circle,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:lightgray;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:#efefef;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-2 rect,#mermaid-svg-lMnT9kfBRDOvqzgd .section-2 path,#mermaid-svg-lMnT9kfBRDOvqzgd .section-2 circle,#mermaid-svg-lMnT9kfBRDOvqzgd .section-2 polygon,#mermaid-svg-lMnT9kfBRDOvqzgd .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-lMnT9kfBRDOvqzgd .section-2 text{fill:#ffffff;}#mermaid-svg-lMnT9kfBRDOvqzgd .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-lMnT9kfBRDOvqzgd .edge-depth-2{stroke-width:8;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled circle,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:lightgray;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:#efefef;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-3 rect,#mermaid-svg-lMnT9kfBRDOvqzgd .section-3 path,#mermaid-svg-lMnT9kfBRDOvqzgd .section-3 circle,#mermaid-svg-lMnT9kfBRDOvqzgd .section-3 polygon,#mermaid-svg-lMnT9kfBRDOvqzgd .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-lMnT9kfBRDOvqzgd .section-3 text{fill:black;}#mermaid-svg-lMnT9kfBRDOvqzgd .node-icon-3{font-size:40px;color:black;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-lMnT9kfBRDOvqzgd .edge-depth-3{stroke-width:5;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled circle,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:lightgray;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:#efefef;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-4 rect,#mermaid-svg-lMnT9kfBRDOvqzgd .section-4 path,#mermaid-svg-lMnT9kfBRDOvqzgd .section-4 circle,#mermaid-svg-lMnT9kfBRDOvqzgd .section-4 polygon,#mermaid-svg-lMnT9kfBRDOvqzgd .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-lMnT9kfBRDOvqzgd .section-4 text{fill:black;}#mermaid-svg-lMnT9kfBRDOvqzgd .node-icon-4{font-size:40px;color:black;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-lMnT9kfBRDOvqzgd .edge-depth-4{stroke-width:2;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled circle,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:lightgray;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:#efefef;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-5 rect,#mermaid-svg-lMnT9kfBRDOvqzgd .section-5 path,#mermaid-svg-lMnT9kfBRDOvqzgd .section-5 circle,#mermaid-svg-lMnT9kfBRDOvqzgd .section-5 polygon,#mermaid-svg-lMnT9kfBRDOvqzgd .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-lMnT9kfBRDOvqzgd .section-5 text{fill:black;}#mermaid-svg-lMnT9kfBRDOvqzgd .node-icon-5{font-size:40px;color:black;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-lMnT9kfBRDOvqzgd .edge-depth-5{stroke-width:-1;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled circle,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:lightgray;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:#efefef;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-6 rect,#mermaid-svg-lMnT9kfBRDOvqzgd .section-6 path,#mermaid-svg-lMnT9kfBRDOvqzgd .section-6 circle,#mermaid-svg-lMnT9kfBRDOvqzgd .section-6 polygon,#mermaid-svg-lMnT9kfBRDOvqzgd .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-lMnT9kfBRDOvqzgd .section-6 text{fill:black;}#mermaid-svg-lMnT9kfBRDOvqzgd .node-icon-6{font-size:40px;color:black;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-lMnT9kfBRDOvqzgd .edge-depth-6{stroke-width:-4;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled circle,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:lightgray;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:#efefef;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-7 rect,#mermaid-svg-lMnT9kfBRDOvqzgd .section-7 path,#mermaid-svg-lMnT9kfBRDOvqzgd .section-7 circle,#mermaid-svg-lMnT9kfBRDOvqzgd .section-7 polygon,#mermaid-svg-lMnT9kfBRDOvqzgd .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-lMnT9kfBRDOvqzgd .section-7 text{fill:black;}#mermaid-svg-lMnT9kfBRDOvqzgd .node-icon-7{font-size:40px;color:black;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-lMnT9kfBRDOvqzgd .edge-depth-7{stroke-width:-7;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled circle,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:lightgray;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:#efefef;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-8 rect,#mermaid-svg-lMnT9kfBRDOvqzgd .section-8 path,#mermaid-svg-lMnT9kfBRDOvqzgd .section-8 circle,#mermaid-svg-lMnT9kfBRDOvqzgd .section-8 polygon,#mermaid-svg-lMnT9kfBRDOvqzgd .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-lMnT9kfBRDOvqzgd .section-8 text{fill:black;}#mermaid-svg-lMnT9kfBRDOvqzgd .node-icon-8{font-size:40px;color:black;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-lMnT9kfBRDOvqzgd .edge-depth-8{stroke-width:-10;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled circle,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:lightgray;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:#efefef;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-9 rect,#mermaid-svg-lMnT9kfBRDOvqzgd .section-9 path,#mermaid-svg-lMnT9kfBRDOvqzgd .section-9 circle,#mermaid-svg-lMnT9kfBRDOvqzgd .section-9 polygon,#mermaid-svg-lMnT9kfBRDOvqzgd .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-lMnT9kfBRDOvqzgd .section-9 text{fill:black;}#mermaid-svg-lMnT9kfBRDOvqzgd .node-icon-9{font-size:40px;color:black;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-lMnT9kfBRDOvqzgd .edge-depth-9{stroke-width:-13;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled circle,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:lightgray;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:#efefef;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-10 rect,#mermaid-svg-lMnT9kfBRDOvqzgd .section-10 path,#mermaid-svg-lMnT9kfBRDOvqzgd .section-10 circle,#mermaid-svg-lMnT9kfBRDOvqzgd .section-10 polygon,#mermaid-svg-lMnT9kfBRDOvqzgd .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-lMnT9kfBRDOvqzgd .section-10 text{fill:black;}#mermaid-svg-lMnT9kfBRDOvqzgd .node-icon-10{font-size:40px;color:black;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-lMnT9kfBRDOvqzgd .edge-depth-10{stroke-width:-16;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled circle,#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:lightgray;}#mermaid-svg-lMnT9kfBRDOvqzgd .disabled text{fill:#efefef;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-root rect,#mermaid-svg-lMnT9kfBRDOvqzgd .section-root path,#mermaid-svg-lMnT9kfBRDOvqzgd .section-root circle,#mermaid-svg-lMnT9kfBRDOvqzgd .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-lMnT9kfBRDOvqzgd .section-root text{fill:#ffffff;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-root span{color:#ffffff;}#mermaid-svg-lMnT9kfBRDOvqzgd .section-2 span{color:#ffffff;}#mermaid-svg-lMnT9kfBRDOvqzgd .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-lMnT9kfBRDOvqzgd .edge{fill:none;}#mermaid-svg-lMnT9kfBRDOvqzgd .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-lMnT9kfBRDOvqzgd :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

Java常用API与集合框架

String类

String基础

常用方法

StringBuilder

StringBuffer

字符串池

日期时间API

Date类

Calendar类

SimpleDateFormat

Java 8新API

LocalDate

LocalTime

LocalDateTime

Math与Random

Math常用方法

Random随机数

UUID生成

集合框架

Collection接口

List接口

ArrayList

LinkedList

Vector

Set接口

HashSet

LinkedHashSet

TreeSet

Queue接口

PriorityQueue

Deque

Map接口

HashMap

LinkedHashMap

TreeMap

Hashtable

ConcurrentHashMap

集合操作

遍历方法

排序方法

查找方法

转换方法

一、为什么需要学习常用API?

1.1 API的重要性

什么是API?

  • API(Application Programming Interface)是应用程序编程接口
  • Java提供了大量的类库和方法供开发者使用
  • 掌握常用API可以大大提高开发效率

不使用API的问题:

// 不使用API:手动实现字符串反转
public class WithoutAPI {
public static String reverse(String str) {
char[] chars = new char[str.length()];
for (int i = 0; i < str.length(); i++) {
chars[i] = str.charAt(str.length() 1 i);
}
return new String(chars);
}

public static void main(String[] args) {
String str = "Hello";
String reversed = reverse(str);
System.out.println(reversed); // olleH
}
}

使用API的优势:

// 使用API:直接调用StringBuilder的reverse方法
public class WithAPI {
public static void main(String[] args) {
String str = "Hello";
String reversed = new StringBuilder(str).reverse().toString();
System.out.println(reversed); // olleH

// 优势:
// 1. 代码简洁
// 2. 性能优化
// 3. 经过充分测试
// 4. 易于维护
}
}

1.2 集合框架的重要性

为什么需要集合框架?

// 不使用集合:使用数组存储数据
public class WithoutCollection {
public static void main(String[] args) {
// 问题1:数组长度固定
String[] names = new String[3];
names[0] = "张三";
names[1] = "李四";
names[2] = "王五";
// names[3] = "赵六"; // 错误!数组越界

// 问题2:添加元素需要创建新数组
String[] newNames = new String[4];
for (int i = 0; i < names.length; i++) {
newNames[i] = names[i];
}
newNames[3] = "赵六";

// 问题3:删除元素很麻烦
// 需要手动移动元素
}
}

使用集合框架的优势:

import java.util.ArrayList;
import java.util.List;

// 使用集合:动态数组
public class WithCollection {
public static void main(String[] args) {
// 优势1:长度可变
List<String> names = new ArrayList<>();
names.add("张三");
names.add("李四");
names.add("王五");
names.add("赵六"); // 自动扩容

// 优势2:添加元素简单
names.add("孙七");

// 优势3:删除元素简单
names.remove("李四");

// 优势4:提供丰富的方法
System.out.println("元素个数:" + names.size());
System.out.println("是否包含张三:" + names.contains("张三"));
System.out.println("第一个元素:" + names.get(0));
}
}

二、String类详解

2.1 String类基础

String类的特点:

  • String是不可变的(immutable)
  • String对象一旦创建,内容不能改变
  • 字符串字面量存储在字符串池中
  • String类是final的,不能被继承
  • String的创建方式:

    public class StringCreation {
    public static void main(String[] args) {
    // 方式1:字面量创建(推荐)
    String str1 = "Hello";

    // 方式2:使用new关键字
    String str2 = new String("Hello");

    // 方式3:使用字符数组
    char[] chars = {'H', 'e', 'l', 'l', 'o'};
    String str3 = new String(chars);

    // 方式4:使用字节数组
    byte[] bytes = {72, 101, 108, 108, 111};
    String str4 = new String(bytes);

    System.out.println(str1); // Hello
    System.out.println(str2); // Hello
    System.out.println(str3); // Hello
    System.out.println(str4); // Hello
    }
    }

    String的内存结构:

    字符串池(String Pool) 堆内存(Heap)
    ┌─────────────────┐ ┌─────────────────┐
    │ "Hello" │◄─────────────│ str1 │
    └─────────────────┘ └─────────────────┘
    ▲ ┌─────────────────┐
    │ │ str2 │
    │ │ new String │
    │ │ value="Hello" │
    │ └─────────────────┘

    └────────────────────────字面量直接指向字符串池
    new创建的对象在堆内存

    String的不可变性:

    public class StringImmutable {
    public static void main(String[] args) {
    String str = "Hello";
    System.out.println("原始字符串:" + str);
    System.out.println("原始地址:" + System.identityHashCode(str));

    // 看起来修改了字符串,实际上创建了新对象
    str = str + " World";
    System.out.println("修改后字符串:" + str);
    System.out.println("修改后地址:" + System.identityHashCode(str));

    // 输出结果显示地址不同,说明是新对象

    // 为什么String要设计成不可变的?
    // 1. 安全性:字符串常用于参数传递,不可变保证安全
    // 2. 线程安全:不可变对象天然线程安全
    // 3. 字符串池:可以共享字符串,节省内存
    // 4. hashCode缓存:不可变对象的hashCode可以缓存
    }
    }

    2.2 String常用方法

    字符串长度和字符访问:

    public class StringBasicMethods {
    public static void main(String[] args) {
    String str = "Hello World";

    // 1. 获取字符串长度
    int length = str.length();
    System.out.println("字符串长度:" + length); // 11

    // 2. 获取指定位置的字符
    char ch = str.charAt(0);
    System.out.println("第一个字符:" + ch); // H

    // 3. 获取字符的索引位置
    int index = str.indexOf('o');
    System.out.println("字符o的位置:" + index); // 4

    // 4. 获取字符最后出现的位置
    int lastIndex = str.lastIndexOf('o');
    System.out.println("字符o最后出现的位置:" + lastIndex); // 7

    // 5. 判断字符串是否为空
    boolean isEmpty = str.isEmpty();
    System.out.println("字符串是否为空:" + isEmpty); // false

    // 6. 判断字符串是否为空或只包含空白字符(Java 11+)
    String blank = " ";
    // boolean isBlank = blank.isBlank();
    // System.out.println("字符串是否为空白:" + isBlank); // true
    }
    }

    字符串比较:

    public class StringComparison {
    public static void main(String[] args) {
    String str1 = "Hello";
    String str2 = "Hello";
    String str3 = new String("Hello");
    String str4 = "hello";

    // 1. 使用==比较(比较地址)
    System.out.println("str1 == str2: " + (str1 == str2)); // true(字符串池)
    System.out.println("str1 == str3: " + (str1 == str3)); // false(不同对象)

    // 2. 使用equals比较(比较内容)
    System.out.println("str1.equals(str2): " + str1.equals(str2)); // true
    System.out.println("str1.equals(str3): " + str1.equals(str3)); // true
    System.out.println("str1.equals(str4): " + str1.equals(str4)); // false

    // 3. 使用equalsIgnoreCase比较(忽略大小写)
    System.out.println("str1.equalsIgnoreCase(str4): " + str1.equalsIgnoreCase(str4)); // true

    // 4. 使用compareTo比较(字典顺序)
    System.out.println("str1.compareTo(str2): " + str1.compareTo(str2)); // 0(相等)
    System.out.println("str1.compareTo(str4): " + str1.compareTo(str4)); // -32(str1小于str4)

    // 5. 使用compareToIgnoreCase比较(忽略大小写的字典顺序)
    System.out.println("str1.compareToIgnoreCase(str4): " + str1.compareToIgnoreCase(str4)); // 0
    }
    }

    字符串查找和判断:

    public class StringSearch {
    public static void main(String[] args) {
    String str = "Hello World, Welcome to Java";

    // 1. 判断是否包含子字符串
    boolean contains = str.contains("World");
    System.out.println("是否包含World:" + contains); // true

    // 2. 判断是否以指定字符串开头
    boolean startsWith = str.startsWith("Hello");
    System.out.println("是否以Hello开头:" + startsWith); // true

    // 3. 判断是否以指定字符串结尾
    boolean endsWith = str.endsWith("Java");
    System.out.println("是否以Java结尾:" + endsWith); // true

    // 4. 查找子字符串的位置
    int index = str.indexOf("World");
    System.out.println("World的位置:" + index); // 6

    // 5. 从指定位置开始查找
    int index2 = str.indexOf("o", 5);
    System.out.println("从位置5开始查找o:" + index2); // 7

    // 6. 查找子字符串最后出现的位置
    int lastIndex = str.lastIndexOf("o");
    System.out.println("o最后出现的位置:" + lastIndex); // 22
    }
    }

    字符串截取和拆分:

    public class StringSubstring {
    public static void main(String[] args) {
    String str = "Hello World, Welcome to Java";

    // 1. 截取子字符串(从指定位置到末尾)
    String sub1 = str.substring(6);
    System.out.println("从位置6截取:" + sub1); // World, Welcome to Java

    // 2. 截取子字符串(指定开始和结束位置)
    String sub2 = str.substring(6, 11);
    System.out.println("截取6到11:" + sub2); // World

    // 3. 拆分字符串
    String[] parts = str.split(",");
    System.out.println("拆分结果:");
    for (String part : parts) {
    System.out.println(" " + part.trim());
    }

    // 4. 按正则表达式拆分
    String str2 = "apple,banana;orange:grape";
    String[] fruits = str2.split("[,;:]");
    System.out.println("水果列表:");
    for (String fruit : fruits) {
    System.out.println(" " + fruit);
    }

    // 5. 限制拆分次数
    String str3 = "a-b-c-d-e";
    String[] parts2 = str3.split("-", 3);
    System.out.println("限制拆分次数:");
    for (String part : parts2) {
    System.out.println(" " + part);
    }
    }
    }

    字符串替换和转换:

    public class StringReplace {
    public static void main(String[] args) {
    String str = "Hello World, Hello Java";

    // 1. 替换字符
    String replaced1 = str.replace('o', 'O');
    System.out.println("替换字符o:" + replaced1);

    // 2. 替换子字符串
    String replaced2 = str.replace("Hello", "Hi");
    System.out.println("替换Hello:" + replaced2);

    // 3. 替换第一个匹配的子字符串
    String replaced3 = str.replaceFirst("Hello", "Hi");
    System.out.println("替换第一个Hello:" + replaced3);

    // 4. 使用正则表达式替换
    String str2 = "abc123def456";
    String replaced4 = str2.replaceAll("\\\\d+", "#");
    System.out.println("替换所有数字:" + replaced4);

    // 5. 转换为大写
    String upper = str.toUpperCase();
    System.out.println("转换为大写:" + upper);

    // 6. 转换为小写
    String lower = str.toLowerCase();
    System.out.println("转换为小写:" + lower);

    // 7. 去除首尾空白字符
    String str3 = " Hello World ";
    String trimmed = str3.trim();
    System.out.println("去除空白:[" + trimmed + "]");

    // 8. 去除所有空白字符(Java 11+)
    // String stripped = str3.strip();
    // System.out.println("去除空白:[" + stripped + "]");
    }
    }

    字符串格式化和连接:

    public class StringFormat {
    public static void main(String[] args) {
    // 1. 使用+连接字符串(不推荐,性能差)
    String name = "张三";
    int age = 25;
    String info1 = "姓名:" + name + ",年龄:" + age;
    System.out.println(info1);

    // 2. 使用String.format格式化(推荐)
    String info2 = String.format("姓名:%s,年龄:%d", name, age);
    System.out.println(info2);

    // 3. 常用格式化符号
    System.out.println(String.format("整数:%d", 100));
    System.out.println(String.format("浮点数:%.2f", 3.14159));
    System.out.println(String.format("字符串:%s", "Hello"));
    System.out.println(String.format("布尔值:%b", true));
    System.out.println(String.format("十六进制:%x", 255));

    // 4. 使用String.join连接字符串(Java 8+)
    String joined = String.join(", ", "apple", "banana", "orange");
    System.out.println("连接结果:" + joined);

    // 5. 连接集合中的字符串
    List<String> list = Arrays.asList("Java", "Python", "C++");
    String languages = String.join(" | ", list);
    System.out.println("编程语言:" + languages);

    // 6. 重复字符串(Java 11+)
    // String repeated = "Hello".repeat(3);
    // System.out.println(repeated); // HelloHelloHello
    }
    }

    2.3 StringBuilder和StringBuffer

    为什么需要StringBuilder?

    public class StringBuilderDemo {
    public static void main(String[] args) {
    // 问题:使用String拼接字符串,性能差
    long start1 = System.currentTimeMillis();
    String str = "";
    for (int i = 0; i < 10000; i++) {
    str += i; // 每次都创建新对象
    }
    long end1 = System.currentTimeMillis();
    System.out.println("String拼接耗时:" + (end1 start1) + "ms");

    // 解决方案:使用StringBuilder
    long start2 = System.currentTimeMillis();
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 10000; i++) {
    sb.append(i); // 在原对象上修改
    }
    String result = sb.toString();
    long end2 = System.currentTimeMillis();
    System.out.println("StringBuilder拼接耗时:" + (end2 start2) + "ms");

    // 性能对比:StringBuilder比String快几十倍甚至上百倍
    }
    }

    StringBuilder常用方法:

    public class StringBuilderMethods {
    public static void main(String[] args) {
    StringBuilder sb = new StringBuilder("Hello");

    // 1. 追加内容
    sb.append(" World");
    System.out.println("追加后:" + sb); // Hello World

    // 2. 插入内容
    sb.insert(5, ",");
    System.out.println("插入后:" + sb); // Hello, World

    // 3. 删除内容
    sb.delete(5, 6);
    System.out.println("删除后:" + sb); // Hello World

    // 4. 替换内容
    sb.replace(0, 5, "Hi");
    System.out.println("替换后:" + sb); // Hi World

    // 5. 反转字符串
    sb.reverse();
    System.out.println("反转后:" + sb); // dlroW iH

    // 6. 获取长度
    int length = sb.length();
    System.out.println("长度:" + length);

    // 7. 获取容量
    int capacity = sb.capacity();
    System.out.println("容量:" + capacity);

    // 8. 设置长度
    sb.setLength(5);
    System.out.println("设置长度后:" + sb);

    // 9. 转换为String
    String str = sb.toString();
    System.out.println("转换为String:" + str);
    }
    }

    StringBuilder vs StringBuffer:

    public class StringBuilderVsStringBuffer {
    public static void main(String[] args) {
    // StringBuilder:线程不安全,性能高(推荐)
    StringBuilder sb = new StringBuilder();
    sb.append("Hello");
    sb.append(" World");

    // StringBuffer:线程安全,性能较低
    StringBuffer sbf = new StringBuffer();
    sbf.append("Hello");
    sbf.append(" World");

    System.out.println("StringBuilder:" + sb);
    System.out.println("StringBuffer:" + sbf);
    }
    }

    对比总结:

    特性StringStringBuilderStringBuffer
    可变性 不可变 可变 可变
    线程安全 安全 不安全 安全
    性能 拼接时差 较高
    使用场景 字符串不变 单线程拼接 多线程拼接

    2.4 字符串池(String Pool)

    字符串池的概念:

    public class StringPoolDemo {
    public static void main(String[] args) {
    // 字符串池:JVM为了优化字符串操作,维护的一个特殊内存区域

    // 1. 字面量创建的字符串在字符串池中
    String str1 = "Hello";
    String str2 = "Hello";
    System.out.println("str1 == str2: " + (str1 == str2)); // true

    // 2. new创建的字符串在堆内存中
    String str3 = new String("Hello");
    System.out.println("str1 == str3: " + (str1 == str3)); // false

    // 3. intern()方法:将字符串放入字符串池
    String str4 = new String("Hello").intern();
    System.out.println("str1 == str4: " + (str1 == str4)); // true

    // 4. 字符串拼接
    String str5 = "Hel" + "lo"; // 编译期优化,直接变成"Hello"
    System.out.println("str1 == str5: " + (str1 == str5)); // true

    String s1 = "Hel";
    String s2 = "lo";
    String str6 = s1 + s2; // 运行时拼接,创建新对象
    System.out.println("str1 == str6: " + (str1 == str6)); // false

    // 5. 使用final修饰的变量拼接
    final String s3 = "Hel";
    final String s4 = "lo";
    String str7 = s3 + s4; // 编译期优化
    System.out.println("str1 == str7: " + (str1 == str7)); // true
    }
    }

    字符串池的内存结构:

    #mermaid-svg-curwwLnqv0s1H9o1{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-curwwLnqv0s1H9o1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-curwwLnqv0s1H9o1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-curwwLnqv0s1H9o1 .error-icon{fill:#552222;}#mermaid-svg-curwwLnqv0s1H9o1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-curwwLnqv0s1H9o1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-curwwLnqv0s1H9o1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-curwwLnqv0s1H9o1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-curwwLnqv0s1H9o1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-curwwLnqv0s1H9o1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-curwwLnqv0s1H9o1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-curwwLnqv0s1H9o1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-curwwLnqv0s1H9o1 .marker.cross{stroke:#333333;}#mermaid-svg-curwwLnqv0s1H9o1 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-curwwLnqv0s1H9o1 p{margin:0;}#mermaid-svg-curwwLnqv0s1H9o1 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-curwwLnqv0s1H9o1 .cluster-label text{fill:#333;}#mermaid-svg-curwwLnqv0s1H9o1 .cluster-label span{color:#333;}#mermaid-svg-curwwLnqv0s1H9o1 .cluster-label span p{background-color:transparent;}#mermaid-svg-curwwLnqv0s1H9o1 .label text,#mermaid-svg-curwwLnqv0s1H9o1 span{fill:#333;color:#333;}#mermaid-svg-curwwLnqv0s1H9o1 .node rect,#mermaid-svg-curwwLnqv0s1H9o1 .node circle,#mermaid-svg-curwwLnqv0s1H9o1 .node ellipse,#mermaid-svg-curwwLnqv0s1H9o1 .node polygon,#mermaid-svg-curwwLnqv0s1H9o1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-curwwLnqv0s1H9o1 .rough-node .label text,#mermaid-svg-curwwLnqv0s1H9o1 .node .label text,#mermaid-svg-curwwLnqv0s1H9o1 .image-shape .label,#mermaid-svg-curwwLnqv0s1H9o1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-curwwLnqv0s1H9o1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-curwwLnqv0s1H9o1 .rough-node .label,#mermaid-svg-curwwLnqv0s1H9o1 .node .label,#mermaid-svg-curwwLnqv0s1H9o1 .image-shape .label,#mermaid-svg-curwwLnqv0s1H9o1 .icon-shape .label{text-align:center;}#mermaid-svg-curwwLnqv0s1H9o1 .node.clickable{cursor:pointer;}#mermaid-svg-curwwLnqv0s1H9o1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-curwwLnqv0s1H9o1 .arrowheadPath{fill:#333333;}#mermaid-svg-curwwLnqv0s1H9o1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-curwwLnqv0s1H9o1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-curwwLnqv0s1H9o1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-curwwLnqv0s1H9o1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-curwwLnqv0s1H9o1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-curwwLnqv0s1H9o1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-curwwLnqv0s1H9o1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-curwwLnqv0s1H9o1 .cluster text{fill:#333;}#mermaid-svg-curwwLnqv0s1H9o1 .cluster span{color:#333;}#mermaid-svg-curwwLnqv0s1H9o1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-curwwLnqv0s1H9o1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-curwwLnqv0s1H9o1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-curwwLnqv0s1H9o1 .icon-shape,#mermaid-svg-curwwLnqv0s1H9o1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-curwwLnqv0s1H9o1 .icon-shape p,#mermaid-svg-curwwLnqv0s1H9o1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-curwwLnqv0s1H9o1 .icon-shape rect,#mermaid-svg-curwwLnqv0s1H9o1 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-curwwLnqv0s1H9o1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-curwwLnqv0s1H9o1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-curwwLnqv0s1H9o1 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    指向

    指向

    指向

    字符串池 String Pool

    Hello

    World

    Java

    堆内存 Heap

    new String Hello

    new String World

    str1 = Hello

    str2 = Hello

    str3 = new String Hello

    2.5 实战案例:字符串工具类

    /**
    * 字符串工具类
    */

    public class StringUtils {

    /**
    * 判断字符串是否为空
    */

    public static boolean isEmpty(String str) {
    return str == null || str.length() == 0;
    }

    /**
    * 判断字符串是否为空或只包含空白字符
    */

    public static boolean isBlank(String str) {
    if (str == null || str.length() == 0) {
    return true;
    }
    for (int i = 0; i < str.length(); i++) {
    if (!Character.isWhitespace(str.charAt(i))) {
    return false;
    }
    }
    return true;
    }

    /**
    * 反转字符串
    */

    public static String reverse(String str) {
    if (isEmpty(str)) {
    return str;
    }
    return new StringBuilder(str).reverse().toString();
    }

    /**
    * 判断是否是回文字符串
    */

    public static boolean isPalindrome(String str) {
    if (isEmpty(str)) {
    return false;
    }
    int left = 0;
    int right = str.length() 1;
    while (left < right) {
    if (str.charAt(left) != str.charAt(right)) {
    return false;
    }
    left++;
    right;
    }
    return true;
    }

    /**
    * 统计字符出现次数
    */

    public static int countChar(String str, char ch) {
    if (isEmpty(str)) {
    return 0;
    }
    int count = 0;
    for (int i = 0; i < str.length(); i++) {
    if (str.charAt(i) == ch) {
    count++;
    }
    }
    return count;
    }

    /**
    * 首字母大写
    */

    public static String capitalize(String str) {
    if (isEmpty(str)) {
    return str;
    }
    return str.substring(0, 1).toUpperCase() + str.substring(1);
    }

    /**
    * 驼峰命名转下划线命名
    */

    public static String camelToUnderscore(String str) {
    if (isEmpty(str)) {
    return str;
    }
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < str.length(); i++) {
    char ch = str.charAt(i);
    if (Character.isUpperCase(ch)) {
    if (i > 0) {
    sb.append('_');
    }
    sb.append(Character.toLowerCase(ch));
    } else {
    sb.append(ch);
    }
    }
    return sb.toString();
    }

    /**
    * 下划线命名转驼峰命名
    */

    public static String underscoreToCamel(String str) {
    if (isEmpty(str)) {
    return str;
    }
    StringBuilder sb = new StringBuilder();
    boolean nextUpperCase = false;
    for (int i = 0; i < str.length(); i++) {
    char ch = str.charAt(i);
    if (ch == '_') {
    nextUpperCase = true;
    } else {
    if (nextUpperCase) {
    sb.append(Character.toUpperCase(ch));
    nextUpperCase = false;
    } else {
    sb.append(ch);
    }
    }
    }
    return sb.toString();
    }

    /**
    * 测试方法
    */

    public static void main(String[] args) {
    // 测试isEmpty
    System.out.println("isEmpty(null): " + isEmpty(null));
    System.out.println("isEmpty(\\"\\"): " + isEmpty(""));
    System.out.println("isEmpty(\\"Hello\\"): " + isEmpty("Hello"));

    // 测试isBlank
    System.out.println("\\nisBlank(\\" \\"): " + isBlank(" "));
    System.out.println("isBlank(\\"Hello\\"): " + isBlank("Hello"));

    // 测试reverse
    System.out.println("\\nreverse(\\"Hello\\"): " + reverse("Hello"));

    // 测试isPalindrome
    System.out.println("\\nisPalindrome(\\"level\\"): " + isPalindrome("level"));
    System.out.println("isPalindrome(\\"hello\\"): " + isPalindrome("hello"));

    // 测试countChar
    System.out.println("\\ncountChar(\\"hello\\", 'l'): " + countChar("hello", 'l'));

    // 测试capitalize
    System.out.println("\\ncapitalize(\\"hello\\"): " + capitalize("hello"));

    // 测试camelToUnderscore
    System.out.println("\\ncamelToUnderscore(\\"userName\\"): " + camelToUnderscore("userName"));

    // 测试underscoreToCamel
    System.out.println("\\nunderscoreToCamel(\\"user_name\\"): " + underscoreToCamel("user_name"));
    }
    }

    三、日期时间API

    3.1 Date类(旧API)

    Date类的基本使用:

    import java.util.Date;

    public class DateDemo {
    public static void main(String[] args) {
    // 1. 创建当前时间的Date对象
    Date now = new Date();
    System.out.println("当前时间:" + now);

    // 2. 创建指定时间的Date对象(毫秒值)
    Date date = new Date(1000L * 60 * 60 * 24 * 365 * 50); // 1970年后50年
    System.out.println("指定时间:" + date);

    // 3. 获取时间戳(毫秒值)
    long timestamp = now.getTime();
    System.out.println("时间戳:" + timestamp);

    // 4. 设置时间
    now.setTime(timestamp + 1000 * 60 * 60); // 加1小时
    System.out.println("1小时后:" + now);

    // 5. 比较时间
    Date date1 = new Date();
    Date date2 = new Date(date1.getTime() + 1000);
    System.out.println("date1在date2之前:" + date1.before(date2));
    System.out.println("date1在date2之后:" + date1.after(date2));

    // 注意:Date类的很多方法已经过时,不推荐使用
    }
    }

    3.2 SimpleDateFormat类

    日期格式化和解析:

    import java.text.SimpleDateFormat;
    import java.text.ParseException;
    import java.util.Date;

    public class SimpleDateFormatDemo {
    public static void main(String[] args) throws ParseException {
    // 1. 创建SimpleDateFormat对象
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    // 2. 格式化日期(Date -> String)
    Date now = new Date();
    String dateStr = sdf.format(now);
    System.out.println("格式化后:" + dateStr);

    // 3. 解析日期(String -> Date)
    String str = "2024-01-06 15:30:00";
    Date date = sdf.parse(str);
    System.out.println("解析后:" + date);

    // 4. 常用日期格式
    System.out.println("\\n常用日期格式:");

    // yyyy-MM-dd
    SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
    System.out.println("yyyy-MM-dd: " + sdf1.format(now));

    // yyyy年MM月dd日
    SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日");
    System.out.println("yyyy年MM月dd日: " + sdf2.format(now));

    // HH:mm:ss
    SimpleDateFormat sdf3 = new SimpleDateFormat("HH:mm:ss");
    System.out.println("HH:mm:ss: " + sdf3.format(now));

    // yyyy-MM-dd HH:mm:ss E
    SimpleDateFormat sdf4 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss E");
    System.out.println("yyyy-MM-dd HH:mm:ss E: " + sdf4.format(now));

    // 5. 格式化符号说明
    System.out.println("\\n格式化符号说明:");
    System.out.println("y – 年");
    System.out.println("M – 月");
    System.out.println("d – 日");
    System.out.println("H – 时(24小时制)");
    System.out.println("h – 时(12小时制)");
    System.out.println("m – 分");
    System.out.println("s – 秒");
    System.out.println("S – 毫秒");
    System.out.println("E – 星期");
    System.out.println("a – 上午/下午");
    }
    }

    SimpleDateFormat线程安全问题:

    import java.text.SimpleDateFormat;
    import java.util.Date;

    public class SimpleDateFormatThreadSafe {
    // 问题:SimpleDateFormat不是线程安全的
    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static void main(String[] args) {
    // 多线程环境下可能出现问题
    for (int i = 0; i < 10; i++) {
    new Thread(() -> {
    try {
    Date date = sdf.parse("2024-01-06 15:30:00");
    System.out.println(Thread.currentThread().getName() + ": " + date);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }).start();
    }

    // 解决方案1:每次创建新对象
    // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    // 解决方案2:使用ThreadLocal
    // ThreadLocal<SimpleDateFormat> threadLocal = ThreadLocal.withInitial(
    // () -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
    // );

    // 解决方案3:使用Java 8的DateTimeFormatter(推荐)
    }
    }

    3.3 Calendar类

    Calendar类的使用:

    import java.util.Calendar;
    import java.util.Date;

    public class CalendarDemo {
    public static void main(String[] args) {
    // 1. 获取Calendar实例
    Calendar calendar = Calendar.getInstance();
    System.out.println("当前时间:" + calendar.getTime());

    // 2. 获取日期字段
    int year = calendar.get(Calendar.YEAR);
    int month = calendar.get(Calendar.MONTH) + 1; // 月份从0开始
    int day = calendar.get(Calendar.DAY_OF_MONTH);
    int hour = calendar.get(Calendar.HOUR_OF_DAY);
    int minute = calendar.get(Calendar.MINUTE);
    int second = calendar.get(Calendar.SECOND);

    System.out.println(String.format("%d年%d月%d日 %d:%d:%d",
    year, month, day, hour, minute, second));

    // 3. 设置日期字段
    calendar.set(Calendar.YEAR, 2025);
    calendar.set(Calendar.MONTH, 11); // 12月
    calendar.set(Calendar.DAY_OF_MONTH, 25);
    System.out.println("设置后:" + calendar.getTime());

    // 4. 增加或减少日期
    calendar.add(Calendar.DAY_OF_MONTH, 7); // 加7天
    System.out.println("7天后:" + calendar.getTime());

    calendar.add(Calendar.MONTH, 1); // 减1个月
    System.out.println("1个月前:" + calendar.getTime());

    // 5. 获取星期几
    int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
    String[] weeks = {"", "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
    System.out.println("星期:" + weeks[dayOfWeek]);

    // 6. 获取当月最大天数
    int maxDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
    System.out.println("当月最大天数:" + maxDay);

    // 7. Calendar和Date互转
    Date date = calendar.getTime(); // Calendar -> Date
    calendar.setTime(date); // Date -> Calendar
    }
    }

    3.4 Java 8新日期时间API

    LocalDate、LocalTime、LocalDateTime:

    import java.time.*;
    import java.time.format.DateTimeFormatter;

    public class Java8DateTimeDemo {
    public static void main(String[] args) {
    // 1. LocalDate:只包含日期
    LocalDate today = LocalDate.now();
    System.out.println("今天:" + today);

    LocalDate birthday = LocalDate.of(1990, 1, 1);
    System.out.println("生日:" + birthday);

    // 2. LocalTime:只包含时间
    LocalTime now = LocalTime.now();
    System.out.println("现在:" + now);

    LocalTime time = LocalTime.of(15, 30, 0);
    System.out.println("指定时间:" + time);

    // 3. LocalDateTime:包含日期和时间
    LocalDateTime dateTime = LocalDateTime.now();
    System.out.println("当前日期时间:" + dateTime);

    LocalDateTime dt = LocalDateTime.of(2024, 1, 6, 15, 30, 0);
    System.out.println("指定日期时间:" + dt);

    // 4. 获取日期字段
    int year = today.getYear();
    int month = today.getMonthValue();
    int day = today.getDayOfMonth();
    System.out.println(String.format("%d年%d月%d日", year, month, day));

    // 5. 日期计算
    LocalDate tomorrow = today.plusDays(1);
    System.out.println("明天:" + tomorrow);

    LocalDate nextWeek = today.plusWeeks(1);
    System.out.println("下周:" + nextWeek);

    LocalDate lastMonth = today.minusMonths(1);
    System.out.println("上个月:" + lastMonth);

    // 6. 日期比较
    boolean isBefore = today.isBefore(tomorrow);
    System.out.println("今天在明天之前:" + isBefore);

    boolean isAfter = today.isAfter(lastMonth);
    System.out.println("今天在上个月之后:" + isAfter);

    // 7. 格式化和解析
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    String formatted = dateTime.format(formatter);
    System.out.println("格式化后:" + formatted);

    LocalDateTime parsed = LocalDateTime.parse("2024-01-06 15:30:00", formatter);
    System.out.println("解析后:" + parsed);

    // 8. 时间间隔
    Period period = Period.between(birthday, today);
    System.out.println("年龄:" + period.getYears() + "岁");

    Duration duration = Duration.between(time, now);
    System.out.println("时间差:" + duration.toHours() + "小时");
    }
    }

    Java 8日期时间API的优势:

    public class Java8DateTimeAdvantage {
    public static void main(String[] args) {
    // 优势1:不可变性(线程安全)
    LocalDate date = LocalDate.now();
    LocalDate newDate = date.plusDays(1); // 返回新对象,原对象不变
    System.out.println("原日期:" + date);
    System.out.println("新日期:" + newDate);

    // 优势2:清晰的API
    LocalDateTime dt = LocalDateTime.now();
    System.out.println("年:" + dt.getYear());
    System.out.println("月:" + dt.getMonthValue());
    System.out.println("日:" + dt.getDayOfMonth());

    // 优势3:链式调用
    LocalDate result = LocalDate.now()
    .plusDays(1)
    .plusMonths(1)
    .plusYears(1);
    System.out.println("1年1月1天后:" + result);

    // 优势4:时区支持
    ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
    System.out.println("上海时间:" + zonedDateTime);

    // 优势5:DateTimeFormatter线程安全
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    String formatted = date.format(formatter);
    System.out.println("格式化:" + formatted);
    }
    }

    四、Math和Random类

    4.1 Math类常用方法

    Math类的基本使用:

    public class MathDemo {
    public static void main(String[] args) {
    // 1. 常量
    System.out.println("圆周率π:" + Math.PI);
    System.out.println("自然对数e:" + Math.E);

    // 2. 绝对值
    System.out.println("\\n绝对值:");
    System.out.println("abs(-10): " + Math.abs(10));
    System.out.println("abs(-3.14): " + Math.abs(3.14));

    // 3. 最大值和最小值
    System.out.println("\\n最大值和最小值:");
    System.out.println("max(10, 20): " + Math.max(10, 20));
    System.out.println("min(10, 20): " + Math.min(10, 20));

    // 4. 幂运算
    System.out.println("\\n幂运算:");
    System.out.println("pow(2, 3): " + Math.pow(2, 3)); // 2的3次方
    System.out.println("pow(5, 2): " + Math.pow(5, 2)); // 5的2次方

    // 5. 平方根
    System.out.println("\\n平方根:");
    System.out.println("sqrt(16): " + Math.sqrt(16));
    System.out.println("sqrt(2): " + Math.sqrt(2));

    // 6. 立方根
    System.out.println("\\n立方根:");
    System.out.println("cbrt(27): " + Math.cbrt(27));

    // 7. 向上取整
    System.out.println("\\n向上取整:");
    System.out.println("ceil(3.1): " + Math.ceil(3.1)); // 4.0
    System.out.println("ceil(3.9): " + Math.ceil(3.9)); // 4.0
    System.out.println("ceil(-3.1): " + Math.ceil(3.1)); // -3.0

    // 8. 向下取整
    System.out.println("\\n向下取整:");
    System.out.println("floor(3.1): " + Math.floor(3.1)); // 3.0
    System.out.println("floor(3.9): " + Math.floor(3.9)); // 3.0
    System.out.println("floor(-3.1): " + Math.floor(3.1)); // -4.0

    // 9. 四舍五入
    System.out.println("\\n四舍五入:");
    System.out.println("round(3.4): " + Math.round(3.4)); // 3
    System.out.println("round(3.5): " + Math.round(3.5)); // 4
    System.out.println("round(3.6): " + Math.round(3.6)); // 4

    // 10. 随机数
    System.out.println("\\n随机数:");
    double random = Math.random(); // [0.0, 1.0)
    System.out.println("random(): " + random);

    // 生成[0, 100)的随机整数
    int randomInt = (int)(Math.random() * 100);
    System.out.println("0-99的随机整数:" + randomInt);

    // 11. 三角函数
    System.out.println("\\n三角函数:");
    System.out.println("sin(π/2): " + Math.sin(Math.PI / 2));
    System.out.println("cos(π): " + Math.cos(Math.PI));
    System.out.println("tan(π/4): " + Math.tan(Math.PI / 4));
    }
    }

    Math类实战案例:

    public class MathPractice {
    /**
    * 计算两点之间的距离
    */

    public static double distance(double x1, double y1, double x2, double y2) {
    double dx = x2 x1;
    double dy = y2 y1;
    return Math.sqrt(dx * dx + dy * dy);
    }

    /**
    * 计算圆的面积
    */

    public static double circleArea(double radius) {
    return Math.PI * radius * radius;
    }

    /**
    * 计算圆的周长
    */

    public static double circlePerimeter(double radius) {
    return 2 * Math.PI * radius;
    }

    /**
    * 判断是否是质数
    */

    public static boolean isPrime(int n) {
    if (n <= 1) return false;
    if (n == 2) return true;
    if (n % 2 == 0) return false;

    int sqrt = (int)Math.sqrt(n);
    for (int i = 3; i <= sqrt; i += 2) {
    if (n % i == 0) {
    return false;
    }
    }
    return true;
    }

    /**
    * 生成指定范围的随机整数
    */

    public static int randomInt(int min, int max) {
    return (int)(Math.random() * (max min + 1)) + min;
    }

    public static void main(String[] args) {
    // 测试距离计算
    double dist = distance(0, 0, 3, 4);
    System.out.println("两点距离:" + dist);

    // 测试圆的面积和周长
    double radius = 5.0;
    System.out.println("圆的面积:" + circleArea(radius));
    System.out.println("圆的周长:" + circlePerimeter(radius));

    // 测试质数判断
    System.out.println("17是质数:" + isPrime(17));
    System.out.println("18是质数:" + isPrime(18));

    // 测试随机数生成
    System.out.println("1-100的随机数:" + randomInt(1, 100));
    }
    }

    4.2 Random类

    Random类的使用:

    import java.util.Random;

    public class RandomDemo {
    public static void main(String[] args) {
    // 1. 创建Random对象
    Random random = new Random();

    // 2. 生成随机整数
    int randomInt = random.nextInt();
    System.out.println("随机整数:" + randomInt);

    // 3. 生成指定范围的随机整数
    int randomInt10 = random.nextInt(10); // [0, 10)
    System.out.println("0-9的随机整数:" + randomInt10);

    int randomInt100 = random.nextInt(100) + 1; // [1, 100]
    System.out.println("1-100的随机整数:" + randomInt100);

    // 4. 生成随机浮点数
    double randomDouble = random.nextDouble(); // [0.0, 1.0)
    System.out.println("随机浮点数:" + randomDouble);

    // 5. 生成随机布尔值
    boolean randomBoolean = random.nextBoolean();
    System.out.println("随机布尔值:" + randomBoolean);

    // 6. 生成随机长整数
    long randomLong = random.nextLong();
    System.out.println("随机长整数:" + randomLong);

    // 7. 使用种子创建Random对象(相同种子产生相同序列)
    Random random1 = new Random(100);
    Random random2 = new Random(100);
    System.out.println("random1: " + random1.nextInt(10));
    System.out.println("random2: " + random2.nextInt(10)); // 相同
    }
    }

    Random实战案例:

    import java.util.*;

    public class RandomPractice {
    private static Random random = new Random();

    /**
    * 生成随机密码
    */

    public static String generatePassword(int length) {
    String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*";
    StringBuilder password = new StringBuilder();
    for (int i = 0; i < length; i++) {
    int index = random.nextInt(chars.length());
    password.append(chars.charAt(index));
    }
    return password.toString();
    }

    /**
    * 随机打乱数组
    */

    public static void shuffle(int[] arr) {
    for (int i = arr.length 1; i > 0; i) {
    int j = random.nextInt(i + 1);
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
    }
    }

    /**
    * 从数组中随机选择n个元素
    */

    public static <T> List<T> randomSelect(T[] arr, int n) {
    if (n > arr.length) {
    n = arr.length;
    }

    List<T> result = new ArrayList<>();
    List<Integer> indices = new ArrayList<>();
    for (int i = 0; i < arr.length; i++) {
    indices.add(i);
    }

    for (int i = 0; i < n; i++) {
    int index = random.nextInt(indices.size());
    result.add(arr[indices.get(index)]);
    indices.remove(index);
    }

    return result;
    }

    /**
    * 生成随机颜色(RGB)
    */

    public static String randomColor() {
    int r = random.nextInt(256);
    int g = random.nextInt(256);
    int b = random.nextInt(256);
    return String.format("RGB(%d, %d, %d)", r, g, b);
    }

    public static void main(String[] args) {
    // 测试生成随机密码
    System.out.println("随机密码:" + generatePassword(12));

    // 测试随机打乱数组
    int[] arr = {1, 2, 3, 4, 5};
    shuffle(arr);
    System.out.println("打乱后的数组:" + Arrays.toString(arr));

    // 测试随机选择
    String[] fruits = {"apple", "banana", "orange", "grape", "watermelon"};
    List<String> selected = randomSelect(fruits, 3);
    System.out.println("随机选择3个水果:" + selected);

    // 测试生成随机颜色
    System.out.println("随机颜色:" + randomColor());
    }
    }

    4.3 UUID生成

    UUID的使用:

    import java.util.UUID;

    public class UUIDDemo {
    public static void main(String[] args) {
    // 1. 生成随机UUID
    UUID uuid = UUID.randomUUID();
    System.out.println("UUID:" + uuid);
    System.out.println("UUID字符串:" + uuid.toString());

    // 2. 生成多个UUID
    System.out.println("\\n生成5个UUID:");
    for (int i = 0; i < 5; i++) {
    System.out.println(UUID.randomUUID());
    }

    // 3. 去掉UUID中的横线
    String uuidStr = UUID.randomUUID().toString().replace("-", "");
    System.out.println("\\n无横线UUID:" + uuidStr);

    // 4. UUID的应用场景
    System.out.println("\\nUUID应用场景:");
    System.out.println("1. 生成唯一的文件名");
    System.out.println("2. 生成唯一的订单号");
    System.out.println("3. 生成唯一的会话ID");
    System.out.println("4. 分布式系统中的唯一标识");
    }
    }

    五、集合框架概述

    5.1 集合框架体系结构

    集合框架的层次结构:

    #mermaid-svg-oW3oevo7nZiKTIjL{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-oW3oevo7nZiKTIjL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oW3oevo7nZiKTIjL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oW3oevo7nZiKTIjL .error-icon{fill:#552222;}#mermaid-svg-oW3oevo7nZiKTIjL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oW3oevo7nZiKTIjL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oW3oevo7nZiKTIjL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oW3oevo7nZiKTIjL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oW3oevo7nZiKTIjL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oW3oevo7nZiKTIjL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oW3oevo7nZiKTIjL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oW3oevo7nZiKTIjL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oW3oevo7nZiKTIjL .marker.cross{stroke:#333333;}#mermaid-svg-oW3oevo7nZiKTIjL svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oW3oevo7nZiKTIjL p{margin:0;}#mermaid-svg-oW3oevo7nZiKTIjL .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-oW3oevo7nZiKTIjL .cluster-label text{fill:#333;}#mermaid-svg-oW3oevo7nZiKTIjL .cluster-label span{color:#333;}#mermaid-svg-oW3oevo7nZiKTIjL .cluster-label span p{background-color:transparent;}#mermaid-svg-oW3oevo7nZiKTIjL .label text,#mermaid-svg-oW3oevo7nZiKTIjL span{fill:#333;color:#333;}#mermaid-svg-oW3oevo7nZiKTIjL .node rect,#mermaid-svg-oW3oevo7nZiKTIjL .node circle,#mermaid-svg-oW3oevo7nZiKTIjL .node ellipse,#mermaid-svg-oW3oevo7nZiKTIjL .node polygon,#mermaid-svg-oW3oevo7nZiKTIjL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oW3oevo7nZiKTIjL .rough-node .label text,#mermaid-svg-oW3oevo7nZiKTIjL .node .label text,#mermaid-svg-oW3oevo7nZiKTIjL .image-shape .label,#mermaid-svg-oW3oevo7nZiKTIjL .icon-shape .label{text-anchor:middle;}#mermaid-svg-oW3oevo7nZiKTIjL .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-oW3oevo7nZiKTIjL .rough-node .label,#mermaid-svg-oW3oevo7nZiKTIjL .node .label,#mermaid-svg-oW3oevo7nZiKTIjL .image-shape .label,#mermaid-svg-oW3oevo7nZiKTIjL .icon-shape .label{text-align:center;}#mermaid-svg-oW3oevo7nZiKTIjL .node.clickable{cursor:pointer;}#mermaid-svg-oW3oevo7nZiKTIjL .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-oW3oevo7nZiKTIjL .arrowheadPath{fill:#333333;}#mermaid-svg-oW3oevo7nZiKTIjL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-oW3oevo7nZiKTIjL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-oW3oevo7nZiKTIjL .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oW3oevo7nZiKTIjL .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oW3oevo7nZiKTIjL .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oW3oevo7nZiKTIjL .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-oW3oevo7nZiKTIjL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-oW3oevo7nZiKTIjL .cluster text{fill:#333;}#mermaid-svg-oW3oevo7nZiKTIjL .cluster span{color:#333;}#mermaid-svg-oW3oevo7nZiKTIjL div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-oW3oevo7nZiKTIjL .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oW3oevo7nZiKTIjL rect.text{fill:none;stroke-width:0;}#mermaid-svg-oW3oevo7nZiKTIjL .icon-shape,#mermaid-svg-oW3oevo7nZiKTIjL .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oW3oevo7nZiKTIjL .icon-shape p,#mermaid-svg-oW3oevo7nZiKTIjL .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-oW3oevo7nZiKTIjL .icon-shape rect,#mermaid-svg-oW3oevo7nZiKTIjL .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oW3oevo7nZiKTIjL .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-oW3oevo7nZiKTIjL .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-oW3oevo7nZiKTIjL :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    Collection接口

    List接口

    Set接口

    Queue接口

    ArrayList

    LinkedList

    Vector

    HashSet

    LinkedHashSet

    TreeSet

    PriorityQueue

    Deque接口

    ArrayDeque

    Map接口

    HashMap

    LinkedHashMap

    TreeMap

    Hashtable

    Collection接口和Map接口的区别:

    特性CollectionMap
    存储方式 单列集合,存储单个元素 双列集合,存储键值对
    元素类型 只有值 键和值
    是否有序 取决于具体实现 取决于具体实现
    是否允许重复 取决于具体实现 键不允许重复,值可以重复

    5.2 集合框架的选择

    如何选择合适的集合?

    public class CollectionChoice {
    public static void main(String[] args) {
    // 1. 需要存储单个元素,且需要保证顺序 -> List
    // – 需要频繁随机访问 -> ArrayList
    // – 需要频繁插入删除 -> LinkedList

    // 2. 需要存储单个元素,且不允许重复 -> Set
    // – 不需要排序 -> HashSet
    // – 需要保持插入顺序 -> LinkedHashSet
    // – 需要自动排序 -> TreeSet

    // 3. 需要存储键值对 -> Map
    // – 不需要排序 -> HashMap
    // – 需要保持插入顺序 -> LinkedHashMap
    // – 需要自动排序 -> TreeMap

    // 4. 需要队列功能 -> Queue
    // – 需要优先级队列 -> PriorityQueue
    // – 需要双端队列 -> ArrayDeque

    System.out.println("集合选择指南:");
    System.out.println("1. 需要快速随机访问 -> ArrayList");
    System.out.println("2. 需要频繁插入删除 -> LinkedList");
    System.out.println("3. 需要去重 -> HashSet");
    System.out.println("4. 需要排序 -> TreeSet/TreeMap");
    System.out.println("5. 需要键值对 -> HashMap");
    System.out.println("6. 需要线程安全 -> ConcurrentHashMap");
    }
    }

    六、List接口详解

    6.1 ArrayList详解

    ArrayList的特点:

  • 底层使用数组实现
  • 查询快,增删慢
  • 线程不安全
  • 允许存储null值
  • 有序,可重复
  • ArrayList的基本使用:

    import java.util.ArrayList;
    import java.util.List;

    public class ArrayListDemo {
    public static void main(String[] args) {
    // 1. 创建ArrayList
    List<String> list = new ArrayList<>();

    // 2. 添加元素
    list.add("Java");
    list.add("Python");
    list.add("C++");
    System.out.println("添加后:" + list);

    // 3. 在指定位置插入元素
    list.add(1, "JavaScript");
    System.out.println("插入后:" + list);

    // 4. 获取元素
    String first = list.get(0);
    System.out.println("第一个元素:" + first);

    // 5. 修改元素
    list.set(0, "Java SE");
    System.out.println("修改后:" + list);

    // 6. 删除元素
    list.remove(1); // 按索引删除
    System.out.println("删除索引1后:" + list);

    list.remove("C++"); // 按对象删除
    System.out.println("删除C++后:" + list);

    // 7. 判断是否包含元素
    boolean contains = list.contains("Python");
    System.out.println("是否包含Python:" + contains);

    // 8. 获取元素索引
    int index = list.indexOf("Python");
    System.out.println("Python的索引:" + index);

    // 9. 获取集合大小
    int size = list.size();
    System.out.println("集合大小:" + size);

    // 10. 判断集合是否为空
    boolean isEmpty = list.isEmpty();
    System.out.println("集合是否为空:" + isEmpty);

    // 11. 清空集合
    list.clear();
    System.out.println("清空后:" + list);
    }
    }

    ArrayList的遍历方式:

    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;

    public class ArrayListTraversal {
    public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("Java");
    list.add("Python");
    list.add("C++");
    list.add("JavaScript");

    // 方式1:普通for循环
    System.out.println("方式1:普通for循环");
    for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
    }

    // 方式2:增强for循环(推荐)
    System.out.println("\\n方式2:增强for循环");
    for (String item : list) {
    System.out.println(item);
    }

    // 方式3:Iterator迭代器
    System.out.println("\\n方式3:Iterator迭代器");
    Iterator<String> iterator = list.iterator();
    while (iterator.hasNext()) {
    String item = iterator.next();
    System.out.println(item);
    }

    // 方式4:forEach + Lambda表达式(Java 8+)
    System.out.println("\\n方式4:forEach + Lambda");
    list.forEach(item -> System.out.println(item));

    // 方式5:Stream API(Java 8+)
    System.out.println("\\n方式5:Stream API");
    list.stream().forEach(System.out::println);
    }
    }

    ArrayList的底层原理:

    public class ArrayListPrinciple {
    public static void main(String[] args) {
    // ArrayList底层原理:
    // 1. 底层使用Object[]数组存储元素
    // 2. 默认初始容量为10
    // 3. 当容量不足时,扩容为原来的1.5倍
    // 4. 扩容过程:创建新数组,复制旧数组元素到新数组

    List<Integer> list = new ArrayList<>();

    // 添加元素时的扩容过程
    System.out.println("添加元素观察扩容:");
    for (int i = 0; i < 15; i++) {
    list.add(i);
    // 第11个元素时会触发扩容
    if (i == 10) {
    System.out.println("添加第11个元素,触发扩容");
    }
    }

    // ArrayList的优缺点
    System.out.println("\\nArrayList的优点:");
    System.out.println("1. 查询快:O(1)时间复杂度");
    System.out.println("2. 支持随机访问");
    System.out.println("3. 内存连续,缓存友好");

    System.out.println("\\nArrayList的缺点:");
    System.out.println("1. 插入删除慢:需要移动元素");
    System.out.println("2. 扩容有性能开销");
    System.out.println("3. 线程不安全");
    }
    }

    6.2 LinkedList详解

    LinkedList的特点:

  • 底层使用双向链表实现
  • 查询慢,增删快
  • 线程不安全
  • 允许存储null值
  • 有序,可重复
  • LinkedList的基本使用:

    import java.util.LinkedList;

    public class LinkedListDemo {
    public static void main(String[] args) {
    LinkedList<String> list = new LinkedList<>();

    // 1. 添加元素
    list.add("Java");
    list.add("Python");
    list.add("C++");
    System.out.println("添加后:" + list);

    // 2. 在头部添加元素
    list.addFirst("JavaScript");
    System.out.println("头部添加后:" + list);

    // 3. 在尾部添加元素
    list.addLast("Go");
    System.out.println("尾部添加后:" + list);

    // 4. 获取头部元素
    String first = list.getFirst();
    System.out.println("头部元素:" + first);

    // 5. 获取尾部元素
    String last = list.getLast();
    System.out.println("尾部元素:" + last);

    // 6. 删除头部元素
    list.removeFirst();
    System.out.println("删除头部后:" + list);

    // 7. 删除尾部元素
    list.removeLast();
    System.out.println("删除尾部后:" + list);

    // 8. LinkedList可以作为栈使用
    System.out.println("\\n作为栈使用:");
    LinkedList<Integer> stack = new LinkedList<>();
    stack.push(1); // 入栈
    stack.push(2);
    stack.push(3);
    System.out.println("栈:" + stack);
    System.out.println("出栈:" + stack.pop()); // 出栈
    System.out.println("栈顶:" + stack.peek()); // 查看栈顶

    // 9. LinkedList可以作为队列使用
    System.out.println("\\n作为队列使用:");
    LinkedList<Integer> queue = new LinkedList<>();
    queue.offer(1); // 入队
    queue.offer(2);
    queue.offer(3);
    System.out.println("队列:" + queue);
    System.out.println("出队:" + queue.poll()); // 出队
    System.out.println("队首:" + queue.peek()); // 查看队首
    }
    }

    ArrayList vs LinkedList:

    import java.util.ArrayList;
    import java.util.LinkedList;
    import java.util.List;

    public class ArrayListVsLinkedList {
    public static void main(String[] args) {
    int size = 100000;

    // 测试ArrayList
    List<Integer> arrayList = new ArrayList<>();
    long start1 = System.currentTimeMillis();
    for (int i = 0; i < size; i++) {
    arrayList.add(i);
    }
    long end1 = System.currentTimeMillis();
    System.out.println("ArrayList添加" + size + "个元素耗时:" + (end1 start1) + "ms");

    // 测试LinkedList
    List<Integer> linkedList = new LinkedList<>();
    long start2 = System.currentTimeMillis();
    for (int i = 0; i < size; i++) {
    linkedList.add(i);
    }
    long end2 = System.currentTimeMillis();
    System.out.println("LinkedList添加" + size + "个元素耗时:" + (end2 start2) + "ms");

    // 测试随机访问
    long start3 = System.currentTimeMillis();
    for (int i = 0; i < 10000; i++) {
    arrayList.get(i);
    }
    long end3 = System.currentTimeMillis();
    System.out.println("\\nArrayList随机访问10000次耗时:" + (end3 start3) + "ms");

    long start4 = System.currentTimeMillis();
    for (int i = 0; i < 10000; i++) {
    linkedList.get(i);
    }
    long end4 = System.currentTimeMillis();
    System.out.println("LinkedList随机访问10000次耗时:" + (end4 start4) + "ms");

    // 结论
    System.out.println("\\n结论:");
    System.out.println("1. ArrayList适合随机访问");
    System.out.println("2. LinkedList适合频繁插入删除");
    System.out.println("3. 大多数情况下使用ArrayList");
    }
    }

    6.3 Vector详解

    Vector的特点:

  • 底层使用数组实现
  • 线程安全(方法使用synchronized修饰)
  • 性能较差(因为同步开销)
  • 已过时,不推荐使用
  • import java.util.Vector;

    public class VectorDemo {
    public static void main(String[] args) {
    // Vector是线程安全的ArrayList
    Vector<String> vector = new Vector<>();

    vector.add("Java");
    vector.add("Python");
    vector.add("C++");

    System.out.println("Vector:" + vector);

    // Vector vs ArrayList
    System.out.println("\\nVector vs ArrayList:");
    System.out.println("1. Vector线程安全,ArrayList线程不安全");
    System.out.println("2. Vector性能较差,ArrayList性能较好");
    System.out.println("3. Vector扩容2倍,ArrayList扩容1.5倍");
    System.out.println("4. 推荐使用ArrayList + Collections.synchronizedList()");
    }
    }

    七、Set接口详解

    7.1 HashSet详解

    HashSet的特点:

  • 底层使用HashMap实现
  • 无序,不可重复
  • 允许存储null值
  • 线程不安全
  • 查询速度快
  • HashSet的基本使用:

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

    public class HashSetDemo {
    public static void main(String[] args) {
    // 1. 创建HashSet
    Set<String> set = new HashSet<>();

    // 2. 添加元素
    set.add("Java");
    set.add("Python");
    set.add("C++");
    set.add("Java"); // 重复元素不会被添加
    System.out.println("添加后:" + set);

    // 3. 删除元素
    set.remove("Python");
    System.out.println("删除后:" + set);

    // 4. 判断是否包含元素
    boolean contains = set.contains("Java");
    System.out.println("是否包含Java:" + contains);

    // 5. 获取集合大小
    int size = set.size();
    System.out.println("集合大小:" + size);

    // 6. 遍历集合
    System.out.println("\\n遍历集合:");
    for (String item : set) {
    System.out.println(item);
    }

    // 7. 集合运算
    Set<Integer> set1 = new HashSet<>();
    set1.add(1);
    set1.add(2);
    set1.add(3);

    Set<Integer> set2 = new HashSet<>();
    set2.add(2);
    set2.add(3);
    set2.add(4);

    // 并集
    Set<Integer> union = new HashSet<>(set1);
    union.addAll(set2);
    System.out.println("\\n并集:" + union);

    // 交集
    Set<Integer> intersection = new HashSet<>(set1);
    intersection.retainAll(set2);
    System.out.println("交集:" + intersection);

    // 差集
    Set<Integer> difference = new HashSet<>(set1);
    difference.removeAll(set2);
    System.out.println("差集:" + difference);
    }
    }

    HashSet去重原理:

    import java.util.HashSet;
    import java.util.Objects;
    import java.util.Set;

    public class HashSetDuplicateRemoval {
    public static void main(String[] args) {
    // HashSet去重原理:
    // 1. 计算对象的hashCode
    // 2. 根据hashCode找到存储位置
    // 3. 如果该位置没有元素,直接存储
    // 4. 如果该位置有元素,使用equals比较
    // 5. 如果equals返回true,认为是重复元素,不存储
    // 6. 如果equals返回false,存储到该位置的链表中

    Set<Person> set = new HashSet<>();
    set.add(new Person("张三", 25));
    set.add(new Person("李四", 30));
    set.add(new Person("张三", 25)); // 重复

    System.out.println("集合大小:" + set.size()); // 2
    System.out.println("集合内容:");
    for (Person person : set) {
    System.out.println(person);
    }
    }
    }

    class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
    this.name = name;
    this.age = age;
    }

    // 重写hashCode和equals方法
    @Override
    public int hashCode() {
    return Objects.hash(name, age);
    }

    @Override
    public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null || getClass() != obj.getClass()) return false;
    Person person = (Person) obj;
    return age == person.age && Objects.equals(name, person.name);
    }

    @Override
    public String toString() {
    return "Person{name='" + name + "', age=" + age + "}";
    }
    }

    7.2 LinkedHashSet详解

    LinkedHashSet的特点:

  • 底层使用LinkedHashMap实现
  • 有序(保持插入顺序),不可重复
  • 性能略低于HashSet
  • import java.util.LinkedHashSet;
    import java.util.Set;

    public class LinkedHashSetDemo {
    public static void main(String[] args) {
    // LinkedHashSet保持插入顺序
    Set<String> set = new LinkedHashSet<>();

    set.add("Java");
    set.add("Python");
    set.add("C++");
    set.add("JavaScript");

    System.out.println("LinkedHashSet:" + set);
    // 输出顺序与插入顺序一致

    // 对比HashSet
    Set<String> hashSet = new HashSet<>();
    hashSet.add("Java");
    hashSet.add("Python");
    hashSet.add("C++");
    hashSet.add("JavaScript");

    System.out.println("HashSet:" + hashSet);
    // 输出顺序不确定
    }
    }

    7.3 TreeSet详解

    TreeSet的特点:

  • 底层使用TreeMap实现
  • 有序(自动排序),不可重复
  • 不允许存储null值
  • 性能较低(需要排序)
  • TreeSet的基本使用:

    import java.util.TreeSet;
    import java.util.Set;

    public class TreeSetDemo {
    public static void main(String[] args) {
    // 1. 创建TreeSet(自然排序)
    Set<Integer> set = new TreeSet<>();

    set.add(5);
    set.add(2);
    set.add(8);
    set.add(1);
    set.add(9);

    System.out.println("TreeSet(自然排序):" + set);
    // 输出:[1, 2, 5, 8, 9]

    // 2. 字符串排序
    Set<String> strSet = new TreeSet<>();
    strSet.add("banana");
    strSet.add("apple");
    strSet.add("orange");
    strSet.add("grape");

    System.out.println("字符串排序:" + strSet);
    // 输出:[apple, banana, grape, orange]

    // 3. 自定义排序(使用Comparator)
    Set<Integer> customSet = new TreeSet<>((a, b) -> b a); // 降序
    customSet.add(5);
    customSet.add(2);
    customSet.add(8);
    customSet.add(1);

    System.out.println("自定义排序(降序):" + customSet);
    // 输出:[8, 5, 2, 1]
    }
    }

    TreeSet自定义对象排序:

    import java.util.TreeSet;
    import java.util.Set;

    public class TreeSetCustomSort {
    public static void main(String[] args) {
    // 方式1:实现Comparable接口
    Set<Student> set1 = new TreeSet<>();
    set1.add(new Student("张三", 85));
    set1.add(new Student("李四", 92));
    set1.add(new Student("王五", 78));

    System.out.println("按成绩排序:");
    for (Student student : set1) {
    System.out.println(student);
    }

    // 方式2:使用Comparator
    Set<Student> set2 = new TreeSet<>((s1, s2) -> s1.getName().compareTo(s2.getName()));
    set2.add(new Student("张三", 85));
    set2.add(new Student("李四", 92));
    set2.add(new Student("王五", 78));

    System.out.println("\\n按姓名排序:");
    for (Student student : set2) {
    System.out.println(student);
    }
    }
    }

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

    public Student(String name, int score) {
    this.name = name;
    this.score = score;
    }

    public String getName() {
    return name;
    }

    public int getScore() {
    return score;
    }

    @Override
    public int compareTo(Student other) {
    // 按成绩升序排序
    return this.score other.score;
    }

    @Override
    public String toString() {
    return "Student{name='" + name + "', score=" + score + "}";
    }
    }

    Set集合对比总结:

    特性HashSetLinkedHashSetTreeSet
    底层实现 HashMap LinkedHashMap TreeMap
    是否有序 无序 有序(插入顺序) 有序(排序)
    是否允许null
    性能 最快 较快 较慢
    使用场景 去重 去重+保持顺序 去重+排序

    八、Map接口详解

    8.1 HashMap详解

    HashMap的特点:

  • 底层使用数组+链表+红黑树实现(JDK 8+)
  • 键不可重复,值可以重复
  • 允许存储null键和null值
  • 无序
  • 线程不安全
  • HashMap的基本使用:

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

    public class HashMapDemo {
    public static void main(String[] args) {
    // 1. 创建HashMap
    Map<String, Integer> map = new HashMap<>();

    // 2. 添加键值对
    map.put("Java", 95);
    map.put("Python", 88);
    map.put("C++", 92);
    System.out.println("添加后:" + map);

    // 3. 获取值
    Integer score = map.get("Java");
    System.out.println("Java的分数:" + score);

    // 4. 修改值
    map.put("Java", 98); // 键相同,覆盖旧值
    System.out.println("修改后:" + map);

    // 5. 删除键值对
    map.remove("Python");
    System.out.println("删除后:" + map);

    // 6. 判断是否包含键
    boolean containsKey = map.containsKey("Java");
    System.out.println("是否包含Java:" + containsKey);

    // 7. 判断是否包含值
    boolean containsValue = map.containsValue(92);
    System.out.println("是否包含值92:" + containsValue);

    // 8. 获取所有键
    System.out.println("\\n所有键:" + map.keySet());

    // 9. 获取所有值
    System.out.println("所有值:" + map.values());

    // 10. 获取所有键值对
    System.out.println("所有键值对:" + map.entrySet());

    // 11. 获取集合大小
    int size = map.size();
    System.out.println("集合大小:" + size);

    // 12. 判断集合是否为空
    boolean isEmpty = map.isEmpty();
    System.out.println("集合是否为空:" + isEmpty);
    }
    }

    HashMap的遍历方式:

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

    public class HashMapTraversal {
    public static void main(String[] args) {
    Map<String, Integer> map = new HashMap<>();
    map.put("Java", 95);
    map.put("Python", 88);
    map.put("C++", 92);

    // 方式1:遍历键,通过键获取值
    System.out.println("方式1:遍历键");
    for (String key : map.keySet()) {
    Integer value = map.get(key);
    System.out.println(key + " = " + value);
    }

    // 方式2:遍历值
    System.out.println("\\n方式2:遍历值");
    for (Integer value : map.values()) {
    System.out.println(value);
    }

    // 方式3:遍历键值对(推荐)
    System.out.println("\\n方式3:遍历键值对");
    for (Map.Entry<String, Integer> entry : map.entrySet()) {
    System.out.println(entry.getKey() + " = " + entry.getValue());
    }

    // 方式4:forEach + Lambda(Java 8+,推荐)
    System.out.println("\\n方式4:forEach + Lambda");
    map.forEach((key, value) -> System.out.println(key + " = " + value));
    }
    }

    HashMap的底层原理:

    public class HashMapPrinciple {
    public static void main(String[] args) {
    // HashMap底层原理(JDK 8+):
    // 1. 底层使用数组+链表+红黑树
    // 2. 默认初始容量16,负载因子0.75
    // 3. 当容量达到阈值时,扩容为原来的2倍
    // 4. 当链表长度超过8且数组长度>=64时,转换为红黑树
    // 5. 当红黑树节点数<=6时,转换回链表

    System.out.println("HashMap底层结构:");
    System.out.println("数组:存储Entry对象");
    System.out.println("链表:解决哈希冲突");
    System.out.println("红黑树:优化链表过长的情况");

    System.out.println("\\nHashMap的put过程:");
    System.out.println("1. 计算key的hashCode");
    System.out.println("2. 根据hashCode计算数组索引");
    System.out.println("3. 如果该位置为空,直接存储");
    System.out.println("4. 如果该位置不为空,判断key是否相同");
    System.out.println("5. 如果key相同,覆盖旧值");
    System.out.println("6. 如果key不同,添加到链表或红黑树");

    System.out.println("\\nHashMap的扩容过程:");
    System.out.println("1. 创建新数组,容量为原来的2倍");
    System.out.println("2. 重新计算每个元素的位置");
    System.out.println("3. 将元素移动到新数组");
    }
    }

    8.2 LinkedHashMap详解

    LinkedHashMap的特点:

  • 底层使用哈希表+双向链表实现
  • 有序(保持插入顺序或访问顺序)
  • 性能略低于HashMap
  • import java.util.LinkedHashMap;
    import java.util.Map;

    public class LinkedHashMapDemo {
    public static void main(String[] args) {
    // 1. 保持插入顺序
    Map<String, Integer> map = new LinkedHashMap<>();
    map.put("Java", 95);
    map.put("Python", 88);
    map.put("C++", 92);

    System.out.println("LinkedHashMap(插入顺序):");
    map.forEach((k, v) -> System.out.println(k + " = " + v));

    // 2. 保持访问顺序(LRU缓存)
    Map<String, Integer> lruMap = new LinkedHashMap<>(16, 0.75f, true);
    lruMap.put("Java", 95);
    lruMap.put("Python", 88);
    lruMap.put("C++", 92);

    lruMap.get("Java"); // 访问Java

    System.out.println("\\nLinkedHashMap(访问顺序):");
    lruMap.forEach((k, v) -> System.out.println(k + " = " + v));
    // Java会排在最后
    }
    }

    8.3 TreeMap详解

    TreeMap的特点:

  • 底层使用红黑树实现
  • 有序(自动排序)
  • 不允许存储null键
  • 性能较低
  • import java.util.TreeMap;
    import java.util.Map;

    public class TreeMapDemo {
    public static void main(String[] args) {
    // 1. 自然排序(按键排序)
    Map<String, Integer> map = new TreeMap<>();
    map.put("Java", 95);
    map.put("Python", 88);
    map.put("C++", 92);
    map.put("JavaScript", 85);

    System.out.println("TreeMap(自然排序):");
    map.forEach((k, v) -> System.out.println(k + " = " + v));

    // 2. 自定义排序
    Map<String, Integer> customMap = new TreeMap<>((k1, k2) -> k2.compareTo(k1));
    customMap.put("Java", 95);
    customMap.put("Python", 88);
    customMap.put("C++", 92);

    System.out.println("\\nTreeMap(降序):");
    customMap.forEach((k, v) -> System.out.println(k + " = " + v));

    // 3. TreeMap的特有方法
    TreeMap<Integer, String> treeMap = new TreeMap<>();
    treeMap.put(3, "three");
    treeMap.put(1, "one");
    treeMap.put(5, "five");
    treeMap.put(2, "two");

    System.out.println("\\nTreeMap特有方法:");
    System.out.println("第一个键:" + treeMap.firstKey());
    System.out.println("最后一个键:" + treeMap.lastKey());
    System.out.println("小于3的键:" + treeMap.headMap(3));
    System.out.println("大于等于3的键:" + treeMap.tailMap(3));
    }
    }

    8.4 Hashtable详解

    Hashtable的特点:

  • 线程安全(方法使用synchronized修饰)
  • 不允许存储null键和null值
  • 性能较差
  • 已过时,不推荐使用
  • import java.util.Hashtable;
    import java.util.Map;

    public class HashtableDemo {
    public static void main(String[] args) {
    Map<String, Integer> map = new Hashtable<>();

    map.put("Java", 95);
    map.put("Python", 88);
    // map.put(null, 100); // 错误!不允许null键
    // map.put("C++", null); // 错误!不允许null值

    System.out.println("Hashtable:" + map);

    System.out.println("\\nHashtable vs HashMap:");
    System.out.println("1. Hashtable线程安全,HashMap线程不安全");
    System.out.println("2. Hashtable不允许null,HashMap允许null");
    System.out.println("3. Hashtable性能较差,HashMap性能较好");
    System.out.println("4. 推荐使用ConcurrentHashMap代替Hashtable");
    }
    }

    Map集合对比总结:

    特性HashMapLinkedHashMapTreeMapHashtable
    底层实现 数组+链表+红黑树 哈希表+双向链表 红黑树 数组+链表
    是否有序 无序 有序(插入/访问) 有序(排序) 无序
    是否允许null键
    是否允许null值
    线程安全
    性能 最快 较快 较慢 较慢

    九、集合工具类

    9.1 Collections工具类

    Collections常用方法:

    import java.util.*;

    public class CollectionsDemo {
    public static void main(String[] args) {
    List<Integer> list = new ArrayList<>(Arrays.asList(5, 2, 8, 1, 9, 3));

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

    // 2. 反转
    Collections.reverse(list);
    System.out.println("反转后:" + list);

    // 3. 打乱
    Collections.shuffle(list);
    System.out.println("打乱后:" + list);

    // 4. 查找最大值
    Integer max = Collections.max(list);
    System.out.println("最大值:" + max);

    // 5. 查找最小值
    Integer min = Collections.min(list);
    System.out.println("最小值:" + min);

    // 6. 二分查找(需要先排序)
    Collections.sort(list);
    int index = Collections.binarySearch(list, 5);
    System.out.println("5的位置:" + index);

    // 7. 替换所有元素
    Collections.replaceAll(list, 5, 50);
    System.out.println("替换后:" + list);

    // 8. 填充
    Collections.fill(list, 0);
    System.out.println("填充后:" + list);

    // 9. 复制
    List<Integer> dest = new ArrayList<>(Arrays.asList(0, 0, 0, 0, 0, 0));
    Collections.copy(dest, Arrays.asList(1, 2, 3));
    System.out.println("复制后:" + dest);

    // 10. 创建不可变集合
    List<String> immutableList = Collections.unmodifiableList(Arrays.asList("a", "b", "c"));
    // immutableList.add("d"); // 错误!不可修改

    // 11. 创建线程安全集合
    List<String> syncList = Collections.synchronizedList(new ArrayList<>());
    syncList.add("a");
    syncList.add("b");

    // 12. 创建空集合
    List<String> emptyList = Collections.emptyList();
    Set<String> emptySet = Collections.emptySet();
    Map<String, String> emptyMap = Collections.emptyMap();
    }
    }

    9.2 Arrays工具类

    Arrays常用方法:

    import java.util.Arrays;
    import java.util.List;

    public class ArraysDemo {
    public static void main(String[] args) {
    int[] arr = {5, 2, 8, 1, 9, 3};

    // 1. 排序
    Arrays.sort(arr);
    System.out.println("排序后:" + Arrays.toString(arr));

    // 2. 二分查找
    int index = Arrays.binarySearch(arr, 5);
    System.out.println("5的位置:" + index);

    // 3. 填充
    int[] arr2 = new int[5];
    Arrays.fill(arr2, 10);
    System.out.println("填充后:" + Arrays.toString(arr2));

    // 4. 复制
    int[] arr3 = Arrays.copyOf(arr, 3);
    System.out.println("复制前3个:" + Arrays.toString(arr3));

    // 5. 复制指定范围
    int[] arr4 = Arrays.copyOfRange(arr, 1, 4);
    System.out.println("复制1-4:" + Arrays.toString(arr4));

    // 6. 比较数组
    int[] arr5 = {1, 2, 3, 5, 8, 9};
    boolean equals = Arrays.equals(arr, arr5);
    System.out.println("数组相等:" + equals);

    // 7. 转换为List
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
    System.out.println("转换为List:" + list);

    // 8. 转换为字符串
    String str = Arrays.toString(arr);
    System.out.println("转换为字符串:" + str);
    }
    }

    十、集合实战案例

    10.1 案例1:学生成绩管理系统

    import java.util.*;

    class Student {
    private String id;
    private String name;
    private Map<String, Integer> scores; // 科目-成绩

    public Student(String id, String name) {
    this.id = id;
    this.name = name;
    this.scores = new HashMap<>();
    }

    public void addScore(String subject, int score) {
    scores.put(subject, score);
    }

    public double getAverageScore() {
    if (scores.isEmpty()) return 0;
    int sum = 0;
    for (int score : scores.values()) {
    sum += score;
    }
    return (double) sum / scores.size();
    }

    public String getId() { return id; }
    public String getName() { return name; }
    public Map<String, Integer> getScores() { return scores; }

    @Override
    public String toString() {
    return String.format("Student{id='%s', name='%s', scores=%s, avg=%.2f}",
    id, name, scores, getAverageScore());
    }
    }

    public class StudentScoreSystem {
    private Map<String, Student> students = new HashMap<>();

    public void addStudent(Student student) {
    students.put(student.getId(), student);
    }

    public void addScore(String studentId, String subject, int score) {
    Student student = students.get(studentId);
    if (student != null) {
    student.addScore(subject, score);
    }
    }

    public void showAllStudents() {
    System.out.println("=== 所有学生信息 ===");
    students.values().forEach(System.out::println);
    }

    public void showTopStudents(int n) {
    System.out.println("\\n=== 成绩前" + n + "名 ===");
    students.values().stream()
    .sorted((s1, s2) -> Double.compare(s2.getAverageScore(), s1.getAverageScore()))
    .limit(n)
    .forEach(System.out::println);
    }

    public void showSubjectAverage(String subject) {
    double avg = students.values().stream()
    .mapToInt(s -> s.getScores().getOrDefault(subject, 0))
    .average()
    .orElse(0);
    System.out.println("\\n" + subject + "平均分:" + String.format("%.2f", avg));
    }

    public static void main(String[] args) {
    StudentScoreSystem system = new StudentScoreSystem();

    // 添加学生
    Student s1 = new Student("001", "张三");
    s1.addScore("语文", 85);
    s1.addScore("数学", 92);
    s1.addScore("英语", 88);
    system.addStudent(s1);

    Student s2 = new Student("002", "李四");
    s2.addScore("语文", 90);
    s2.addScore("数学", 88);
    s2.addScore("英语", 95);
    system.addStudent(s2);

    Student s3 = new Student("003", "王五");
    s3.addScore("语文", 78);
    s3.addScore("数学", 85);
    s3.addScore("英语", 82);
    system.addStudent(s3);

    // 显示所有学生
    system.showAllStudents();

    // 显示前2名
    system.showTopStudents(2);

    // 显示各科平均分
    system.showSubjectAverage("语文");
    system.showSubjectAverage("数学");
    system.showSubjectAverage("英语");
    }
    }

    10.2 案例2:单词统计

    import java.util.*;

    public class WordCount {
    public static void main(String[] args) {
    String text = "Java is a programming language. " +
    "Java is object-oriented. " +
    "Java is platform-independent. " +
    "Programming in Java is fun.";

    // 1. 分词
    String[] words = text.toLowerCase()
    .replaceAll("[^a-z ]", "")
    .split("\\\\s+");

    // 2. 统计词频
    Map<String, Integer> wordCount = new HashMap<>();
    for (String word : words) {
    wordCount.put(word, wordCount.getOrDefault(word, 0) + 1);
    }

    // 3. 按词频排序
    List<Map.Entry<String, Integer>> list = new ArrayList<>(wordCount.entrySet());
    list.sort((e1, e2) -> e2.getValue() e1.getValue());

    // 4. 输出结果
    System.out.println("=== 词频统计(前10) ===");
    list.stream()
    .limit(10)
    .forEach(e -> System.out.println(e.getKey() + ": " + e.getValue()));
    }
    }

    10.3 案例3:购物车系统

    import java.util.*;

    class Product {
    private String id;
    private String name;
    private double price;

    public Product(String id, String name, double price) {
    this.id = id;
    this.name = name;
    this.price = price;
    }

    public String getId() { return id; }
    public String getName() { return name; }
    public double getPrice() { return price; }

    @Override
    public String toString() {
    return String.format("%s(%.2f元)", name, price);
    }
    }

    class CartItem {
    private Product product;
    private int quantity;

    public CartItem(Product product, int quantity) {
    this.product = product;
    this.quantity = quantity;
    }

    public Product getProduct() { return product; }
    public int getQuantity() { return quantity; }
    public void setQuantity(int quantity) { this.quantity = quantity; }

    public double getSubtotal() {
    return product.getPrice() * quantity;
    }

    @Override
    public String toString() {
    return String.format("%s x %d = %.2f元",
    product.getName(), quantity, getSubtotal());
    }
    }

    public class ShoppingCart {
    private Map<String, CartItem> items = new LinkedHashMap<>();

    public void addProduct(Product product, int quantity) {
    String id = product.getId();
    if (items.containsKey(id)) {
    CartItem item = items.get(id);
    item.setQuantity(item.getQuantity() + quantity);
    } else {
    items.put(id, new CartItem(product, quantity));
    }
    System.out.println("已添加:" + product.getName() + " x " + quantity);
    }

    public void removeProduct(String productId) {
    CartItem removed = items.remove(productId);
    if (removed != null) {
    System.out.println("已移除:" + removed.getProduct().getName());
    }
    }

    public void updateQuantity(String productId, int quantity) {
    CartItem item = items.get(productId);
    if (item != null) {
    item.setQuantity(quantity);
    System.out.println("已更新数量:" + item);
    }
    }

    public double getTotal() {
    return items.values().stream()
    .mapToDouble(CartItem::getSubtotal)
    .sum();
    }

    public void showCart() {
    System.out.println("\\n=== 购物车 ===");
    if (items.isEmpty()) {
    System.out.println("购物车为空");
    return;
    }

    items.values().forEach(System.out::println);
    System.out.println("总计:" + String.format("%.2f元", getTotal()));
    }

    public static void main(String[] args) {
    ShoppingCart cart = new ShoppingCart();

    // 添加商品
    cart.addProduct(new Product("P001", "Java编程思想", 108.0), 1);
    cart.addProduct(new Product("P002", "Effective Java", 79.0), 2);
    cart.addProduct(new Product("P003", "深入理解JVM", 89.0), 1);

    // 显示购物车
    cart.showCart();

    // 更新数量
    cart.updateQuantity("P002", 3);
    cart.showCart();

    // 移除商品
    cart.removeProduct("P003");
    cart.showCart();
    }
    }

    十一、面试题精选

    11.1 String相关面试题

    面试题1:String、StringBuilder、StringBuffer的区别?

    答案:

    特性StringStringBuilderStringBuffer
    可变性 不可变 可变 可变
    线程安全 安全 不安全 安全
    性能 拼接时差 较高
    使用场景 字符串不变 单线程拼接 多线程拼接

    详细解释:

  • String是不可变的,每次修改都会创建新对象
  • StringBuilder是可变的,适合单线程环境下的字符串拼接
  • StringBuffer是线程安全的StringBuilder,方法使用synchronized修饰
  • 大多数情况下使用StringBuilder,性能最好

  • 面试题2:String str = “hello” 和 String str = new String(“hello”) 的区别?

    答案:

    // 方式1:字面量创建
    String str1 = "hello"; // 在字符串池中创建

    // 方式2:new创建
    String str2 = new String("hello"); // 在堆内存中创建

    // 区别:
    System.out.println(str1 == str2); // false(地址不同)
    System.out.println(str1.equals(str2)); // true(内容相同)

    // 内存分析:
    // str1指向字符串池中的"hello"
    // str2指向堆内存中的String对象,该对象的value指向字符串池中的"hello"

    详细解释:

  • 字面量创建的字符串存储在字符串池中,可以被复用
  • new创建的字符串存储在堆内存中,每次都创建新对象
  • 推荐使用字面量方式创建字符串,节省内存

  • 面试题3:String的intern()方法有什么作用?

    答案:

    String s1 = new String("hello");
    String s2 = s1.intern(); // 将字符串放入字符串池
    String s3 = "hello";

    System.out.println(s1 == s2); // false
    System.out.println(s2 == s3); // true

    详细解释:

  • intern()方法会检查字符串池中是否存在该字符串
  • 如果存在,返回字符串池中的引用
  • 如果不存在,将字符串添加到字符串池,并返回引用
  • 可以用于减少内存占用,但要注意性能开销

  • 11.2 集合相关面试题

    面试题4:ArrayList和LinkedList的区别?

    答案:

    特性ArrayListLinkedList
    底层实现 数组 双向链表
    随机访问 快O(1) 慢O(n)
    插入删除 慢O(n) 快O(1)
    内存占用 连续内存 不连续内存
    使用场景 查询多 插入删除多

    详细解释:

  • ArrayList适合随机访问,查询效率高
  • LinkedList适合频繁插入删除,特别是头尾操作
  • 大多数情况下使用ArrayList,性能更好
  • LinkedList可以作为栈、队列、双端队列使用

  • 面试题5:HashMap的底层实现原理?

    答案:

    JDK 7:数组+链表

    • 使用Entry数组存储元素
    • 哈希冲突使用链表解决
    • 链表过长时性能下降

    JDK 8+:数组+链表+红黑树

    • 使用Node数组存储元素
    • 哈希冲突先使用链表
    • 链表长度>8且数组长度>=64时,转换为红黑树
    • 红黑树节点数<=6时,转换回链表

    put过程:

    1. 计算key的hashCode
    2. 根据hashCode计算数组索引:(n1) & hash
    3. 如果该位置为空,直接存储
    4. 如果该位置不为空:
    如果key相同,覆盖旧值
    如果key不同,添加到链表或红黑树
    5. 如果容量超过阈值,扩容为原来的2

    扩容过程:

    1. 创建新数组,容量为原来的2
    2. 重新计算每个元素的位置
    3. 将元素移动到新数组
    4. 默认初始容量16,负载因子0.75
    5. 阈值 = 容量 * 负载因子


    面试题6:HashMap为什么线程不安全?如何解决?

    答案:

    线程不安全的原因:

  • 多线程put时可能导致数据丢失
  • 多线程扩容时可能导致死循环(JDK 7)
  • 多线程操作时可能导致数据不一致
  • 解决方案:

    // 方案1:使用Hashtable(不推荐,性能差)
    Map<String, Integer> map1 = new Hashtable<>();

    // 方案2:使用Collections.synchronizedMap(不推荐)
    Map<String, Integer> map2 = Collections.synchronizedMap(new HashMap<>());

    // 方案3:使用ConcurrentHashMap(推荐)
    Map<String, Integer> map3 = new ConcurrentHashMap<>();

    ConcurrentHashMap的优势:

  • 使用分段锁(JDK 7)或CAS+synchronized(JDK 8+)
  • 并发性能好
  • 线程安全

  • 面试题7:HashSet如何保证元素不重复?

    答案:

    // HashSet底层使用HashMap实现
    // 元素作为HashMap的key,value是固定的PRESENT对象

    public class HashSet<E> {
    private static final Object PRESENT = new Object();
    private HashMap<E, Object> map;

    public boolean add(E e) {
    return map.put(e, PRESENT) == null;
    }
    }

    去重原理:

  • 计算元素的hashCode
  • 根据hashCode找到存储位置
  • 如果该位置没有元素,直接存储
  • 如果该位置有元素,使用equals比较
  • 如果equals返回true,认为是重复元素,不存储
  • 如果equals返回false,存储到链表或红黑树
  • 注意事项:

    • 自定义对象必须重写hashCode和equals方法
    • hashCode相同,equals不一定相同
    • equals相同,hashCode必须相同

    面试题8:ArrayList的扩容机制?

    答案:

    // ArrayList扩容过程:
    1. 默认初始容量为10
    2. 当容量不足时,扩容为原来的1.5
    3. 扩容公式:newCapacity = oldCapacity + (oldCapacity >> 1)
    4. 创建新数组,复制旧数组元素到新数组
    5. 使用Arrays.copyOf()方法复制

    // 示例代码:
    List<Integer> list = new ArrayList<>(); // 初始容量0
    list.add(1); // 第一次添加,扩容到10
    // 添加第11个元素时,扩容到15
    // 添加第16个元素时,扩容到22

    优化建议:

    // 如果知道大概容量,建议指定初始容量
    List<Integer> list = new ArrayList<>(100); // 避免多次扩容


    面试题9:HashMap和Hashtable的区别?

    答案:

    特性HashMapHashtable
    线程安全
    允许null键
    允许null值
    性能
    扩容 2倍 2倍+1
    继承 AbstractMap Dictionary
    推荐使用 否(已过时)

    详细解释:

  • Hashtable是线程安全的,但性能较差
  • HashMap线程不安全,但性能较好
  • 推荐使用ConcurrentHashMap代替Hashtable
  • HashMap允许一个null键和多个null值
  • Hashtable不允许null键和null值

  • 面试题10:如何选择合适的集合?

    答案:

    选择流程图:

    需要存储键值对?
    ├─ 是 → Map
    │ ├─ 需要排序?
    │ │ ├─ 是 → TreeMap
    │ │ └─ 否 → HashMap
    │ └─ 需要保持插入顺序?
    │ └─ 是 → LinkedHashMap

    └─ 否 → Collection
    ├─ 需要去重?
    │ ├─ 是 → Set
    │ │ ├─ 需要排序?
    │ │ │ ├─ 是 → TreeSet
    │ │ │ └─ 否 → HashSet
    │ │ └─ 需要保持插入顺序?
    │ │ └─ 是 → LinkedHashSet
    │ │
    │ └─ 否 → List
    │ ├─ 需要频繁随机访问?
    │ │ └─ 是 → ArrayList
    │ └─ 需要频繁插入删除?
    │ └─ 是 → LinkedList

    └─ 需要队列功能?
    └─ 是 → Queue
    ├─ 需要优先级?
    │ └─ 是 → PriorityQueue
    └─ 需要双端队列?
    └─ 是 → ArrayDeque

    使用建议:

  • 默认使用ArrayList和HashMap
  • 需要去重使用HashSet
  • 需要排序使用TreeSet或TreeMap
  • 需要线程安全使用ConcurrentHashMap
  • 需要队列功能使用ArrayDeque

  • 十二、学习检查清单

    12.1 String类检查

    • 理解String的不可变性
    • 掌握String的常用方法
    • 理解字符串池的概念
    • 掌握String、StringBuilder、StringBuffer的区别
    • 能够选择合适的字符串类型
    • 理解字符串拼接的性能问题
    • 掌握字符串的格式化方法

    12.2 日期时间API检查

    • 掌握Date类的基本使用
    • 掌握SimpleDateFormat的使用
    • 理解SimpleDateFormat的线程安全问题
    • 掌握Calendar类的使用
    • 掌握Java 8新日期时间API
    • 能够进行日期的格式化和解析
    • 能够进行日期的计算和比较

    12.3 Math和Random检查

    • 掌握Math类的常用方法
    • 掌握Random类的使用
    • 能够生成指定范围的随机数
    • 掌握UUID的生成和使用
    • 能够使用Math类解决实际问题

    12.4 集合框架检查

    • 理解集合框架的体系结构
    • 掌握Collection和Map的区别
    • 能够根据场景选择合适的集合
    • 理解集合的底层实现原理
    • 掌握集合的常用操作

    12.5 List接口检查

    • 掌握ArrayList的使用和原理
    • 掌握LinkedList的使用和原理
    • 理解ArrayList和LinkedList的区别
    • 掌握List的遍历方式
    • 理解ArrayList的扩容机制
    • 能够选择合适的List实现

    12.6 Set接口检查

    • 掌握HashSet的使用和原理
    • 掌握LinkedHashSet的使用
    • 掌握TreeSet的使用和排序
    • 理解HashSet的去重原理
    • 掌握Set的集合运算
    • 能够选择合适的Set实现

    12.7 Map接口检查

    • 掌握HashMap的使用和原理
    • 掌握LinkedHashMap的使用
    • 掌握TreeMap的使用和排序
    • 理解HashMap的底层实现
    • 理解HashMap的扩容机制
    • 掌握Map的遍历方式
    • 能够选择合适的Map实现

    12.8 集合工具类检查

    • 掌握Collections工具类的使用
    • 掌握Arrays工具类的使用
    • 能够使用工具类进行排序、查找等操作
    • 掌握创建不可变集合的方法
    • 掌握创建线程安全集合的方法

    12.9 实战能力检查

    • 能够使用集合解决实际问题
    • 能够设计合理的数据结构
    • 能够进行性能优化
    • 能够处理集合的并发问题
    • 能够编写高质量的集合代码

    12.10 面试准备检查

    • 能够解释String的不可变性
    • 能够说出ArrayList和LinkedList的区别
    • 能够解释HashMap的底层原理
    • 能够说出HashMap的扩容机制
    • 能够解释HashSet的去重原理
    • 能够选择合适的集合类型
    • 能够回答集合相关的面试题

    十三、知识总结

    13.1 核心概念回顾

    String类:

    • 不可变性:String对象一旦创建,内容不能改变
    • 字符串池:字面量创建的字符串存储在字符串池中
    • 性能:字符串拼接使用StringBuilder,避免创建大量对象

    日期时间API:

    • 旧API:Date、Calendar、SimpleDateFormat
    • 新API:LocalDate、LocalTime、LocalDateTime(Java 8+)
    • 优势:新API不可变、线程安全、API清晰

    集合框架:

    • Collection:单列集合,存储单个元素
    • Map:双列集合,存储键值对
    • 选择:根据场景选择合适的集合类型

    13.2 集合选择指南

    ArrayList:
    – 优点:查询快、支持随机访问
    – 缺点:插入删除慢、扩容有开销
    – 场景:查询多、插入删除少

    LinkedList:
    – 优点:插入删除快、可作为栈和队列
    – 缺点:查询慢、内存占用大
    – 场景:插入删除多、查询少

    HashSet:
    – 优点:去重、查询快
    – 缺点:无序
    – 场景:需要去重、不需要排序

    TreeSet:
    – 优点:去重、自动排序
    – 缺点:性能较低
    – 场景:需要去重和排序

    HashMap:
    – 优点:查询快、支持null
    – 缺点:无序、线程不安全
    – 场景:键值对存储、不需要排序

    TreeMap:
    – 优点:自动排序
    – 缺点:性能较低、不支持null键
    – 场景:键值对存储、需要排序

    13.3 性能优化建议

    1. 指定初始容量

    // 避免多次扩容
    List<Integer> list = new ArrayList<>(100);
    Map<String, Integer> map = new HashMap<>(100);

    2. 使用合适的集合类型

    // 查询多用ArrayList
    List<Integer> list = new ArrayList<>();

    // 插入删除多用LinkedList
    List<Integer> list = new LinkedList<>();

    // 需要去重用HashSet
    Set<Integer> set = new HashSet<>();

    3. 避免在循环中创建对象

    // 不推荐
    for (int i = 0; i < 1000; i++) {
    List<Integer> list = new ArrayList<>(); // 创建1000个对象
    }

    // 推荐
    List<Integer> list = new ArrayList<>();
    for (int i = 0; i < 1000; i++) {
    list.clear(); // 复用对象
    }

    4. 使用StringBuilder拼接字符串

    // 不推荐
    String str = "";
    for (int i = 0; i < 1000; i++) {
    str += i; // 创建1000个String对象
    }

    // 推荐
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 1000; i++) {
    sb.append(i);
    }
    String str = sb.toString();

    13.4 常见错误总结

    错误1:在循环中使用+拼接字符串

    // 错误
    String str = "";
    for (int i = 0; i < 1000; i++) {
    str += i; // 性能差
    }

    // 正确
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 1000; i++) {
    sb.append(i);
    }

    错误2:使用==比较字符串

    // 错误
    String str1 = "hello";
    String str2 = new String("hello");
    if (str1 == str2) { // false
    }

    // 正确
    if (str1.equals(str2)) { // true
    }

    错误3:在遍历时修改集合

    // 错误
    List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
    for (Integer num : list) {
    if (num == 3) {
    list.remove(num); // ConcurrentModificationException
    }
    }

    // 正确
    Iterator<Integer> iterator = list.iterator();
    while (iterator.hasNext()) {
    Integer num = iterator.next();
    if (num == 3) {
    iterator.remove();
    }
    }

    错误4:HashMap的key没有重写hashCode和equals

    // 错误
    class Person {
    String name;
    int age;
    // 没有重写hashCode和equals
    }

    Map<Person, String> map = new HashMap<>();
    map.put(new Person("张三", 25), "员工1");
    String value = map.get(new Person("张三", 25)); // null

    // 正确
    class Person {
    String name;
    int age;

    @Override
    public int hashCode() {
    return Objects.hash(name, age);
    }

    @Override
    public boolean equals(Object obj) {
    // 实现equals方法
    }
    }

    13.5 学习路线建议

    第一阶段:掌握String类(1周)

  • 学习String的基本使用
  • 理解String的不可变性
  • 掌握StringBuilder和StringBuffer
  • 完成String相关练习题
  • 第二阶段:掌握日期时间API(1周)

  • 学习Date和Calendar
  • 掌握SimpleDateFormat
  • 学习Java 8新日期时间API
  • 完成日期时间相关练习题
  • 第三阶段:掌握集合框架(2-3周)

  • 理解集合框架体系结构
  • 掌握List、Set、Map的使用
  • 理解集合的底层实现原理
  • 完成集合相关练习题
  • 第四阶段:综合实战(1-2周)

  • 完成学生成绩管理系统
  • 完成单词统计程序
  • 完成购物车系统
  • 总结和复习
  • 13.6 进阶学习方向

    1. 深入学习集合源码

    • ArrayList源码分析
    • HashMap源码分析
    • ConcurrentHashMap源码分析

    2. 学习并发集合

    • ConcurrentHashMap
    • CopyOnWriteArrayList
    • BlockingQueue

    3. 学习Stream API

    • Stream的基本使用
    • Stream的中间操作和终止操作
    • 并行Stream

    4. 学习Lambda表达式

    • Lambda表达式的语法
    • 函数式接口
    • 方法引用

    13.7 总结

    通过本文档的学习,你应该:

  • 掌握String类:理解不可变性,掌握常用方法,能够选择合适的字符串类型
  • 掌握日期时间API:能够进行日期的格式化、解析和计算
  • 掌握集合框架:理解体系结构,掌握常用集合的使用和原理
  • 具备实战能力:能够使用集合解决实际问题,能够进行性能优化
  • 准备面试:能够回答常见的面试题,能够解释核心概念
  • 下一步学习建议:

    • 继续学习Java异常处理
    • 深入学习Java IO流
    • 学习Java多线程编程
    • 学习Java反射和注解

    记住:

    • 集合是Java开发的基础
    • 多写代码,多做练习
    • 理解底层原理
    • 持续学习,不断进步

    祝你学习顺利,早日成为Java后端大神!


    文档完成时间: 2024年1月
    文档版本: v1.0
    适用人群: Java初学者、准备面试的开发者
    学习时长: 建议2-3周
    前置知识: Java基础语法、面向对象编程
    后续学习: Java异常处理、IO流、多线程


    附录:快速参考

    A. String常用方法速查表

    方法说明示例
    length() 获取长度 “hello”.length() → 5
    charAt(int) 获取字符 “hello”.charAt(0) → ‘h’
    substring(int) 截取子串 “hello”.substring(2) → “llo”
    indexOf(String) 查找位置 “hello”.indexOf(“l”) → 2
    replace(char, char) 替换字符 “hello”.replace(‘l’, ‘L’) → “heLLo”
    toUpperCase() 转大写 “hello”.toUpperCase() → “HELLO”
    toLowerCase() 转小写 “HELLO”.toLowerCase() → “hello”
    trim() 去空格 " hello ".trim() → “hello”
    split(String) 拆分 “a,b,c”.split(“,”) → [“a”,“b”,“c”]
    equals(String) 比较内容 “hello”.equals(“hello”) → true

    B. 集合选择速查表

    需求推荐集合原因
    存储单个元素,需要快速查询 ArrayList 底层数组,查询O(1)
    存储单个元素,需要频繁插入删除 LinkedList 底层链表,插入删除O(1)
    存储单个元素,需要去重 HashSet 底层HashMap,去重快
    存储单个元素,需要去重和排序 TreeSet 底层红黑树,自动排序
    存储键值对,需要快速查询 HashMap 底层哈希表,查询O(1)
    存储键值对,需要排序 TreeMap 底层红黑树,自动排序
    存储键值对,需要保持插入顺序 LinkedHashMap 底层哈希表+链表
    需要线程安全 ConcurrentHashMap 分段锁,并发性能好

    C. 集合时间复杂度速查表

    操作ArrayListLinkedListHashSetTreeSetHashMapTreeMap
    添加 O(1) O(1) O(1) O(log n) O(1) O(log n)
    删除 O(n) O(1) O(1) O(log n) O(1) O(log n)
    查询 O(1) O(n) O(1) O(log n) O(1) O(log n)
    遍历 O(n) O(n) O(n) O(n) O(n) O(n)

    恭喜你完成了Java常用API与集合框架的学习!

    继续加油,向着Java后端大神的目标前进!💪

    十四、综合练习题

    14.1 String练习题

    练习1:字符串反转

    /**
    * 实现字符串反转
    * 输入:"hello"
    * 输出:"olleh"
    */

    public class StringReverseExercise {
    // 方法1:使用StringBuilder
    public static String reverse1(String str) {
    return new StringBuilder(str).reverse().toString();
    }

    // 方法2:使用字符数组
    public static String reverse2(String str) {
    char[] chars = str.toCharArray();
    int left = 0, right = chars.length 1;
    while (left < right) {
    char temp = chars[left];
    chars[left] = chars[right];
    chars[right] = temp;
    left++;
    right;
    }
    return new String(chars);
    }

    // 方法3:使用递归
    public static String reverse3(String str) {
    if (str.length() <= 1) {
    return str;
    }
    return reverse3(str.substring(1)) + str.charAt(0);
    }

    public static void main(String[] args) {
    String str = "hello";
    System.out.println("方法1:" + reverse1(str));
    System.out.println("方法2:" + reverse2(str));
    System.out.println("方法3:" + reverse3(str));
    }
    }

    练习2:判断回文字符串

    /**
    * 判断是否是回文字符串
    * 输入:"level"
    * 输出:true
    */

    public class PalindromeExercise {
    public static boolean isPalindrome(String str) {
    if (str == null || str.length() == 0) {
    return false;
    }

    int left = 0, right = str.length() 1;
    while (left < right) {
    if (str.charAt(left) != str.charAt(right)) {
    return false;
    }
    left++;
    right;
    }
    return true;
    }

    public static void main(String[] args) {
    System.out.println(isPalindrome("level")); // true
    System.out.println(isPalindrome("hello")); // false
    System.out.println(isPalindrome("noon")); // true
    }
    }

    练习3:统计字符出现次数

    /**
    * 统计字符串中每个字符出现的次数
    * 输入:"hello"
    * 输出:h=1, e=1, l=2, o=1
    */

    public class CharCountExercise {
    public static Map<Character, Integer> countChars(String str) {
    Map<Character, Integer> map = new LinkedHashMap<>();
    for (char ch : str.toCharArray()) {
    map.put(ch, map.getOrDefault(ch, 0) + 1);
    }
    return map;
    }

    public static void main(String[] args) {
    String str = "hello";
    Map<Character, Integer> result = countChars(str);
    result.forEach((k, v) -> System.out.println(k + "=" + v));
    }
    }

    练习4:字符串压缩

    /**
    * 字符串压缩
    * 输入:"aabcccccaaa"
    * 输出:"a2b1c5a3"
    */

    public class StringCompressionExercise {
    public static String compress(String str) {
    if (str == null || str.length() == 0) {
    return str;
    }

    StringBuilder sb = new StringBuilder();
    int count = 1;
    char current = str.charAt(0);

    for (int i = 1; i < str.length(); i++) {
    if (str.charAt(i) == current) {
    count++;
    } else {
    sb.append(current).append(count);
    current = str.charAt(i);
    count = 1;
    }
    }
    sb.append(current).append(count);

    return sb.length() < str.length() ? sb.toString() : str;
    }

    public static void main(String[] args) {
    System.out.println(compress("aabcccccaaa")); // a2b1c5a3
    System.out.println(compress("abc")); // abc
    }
    }

    练习5:最长公共前缀

    /**
    * 找出字符串数组中的最长公共前缀
    * 输入:["flower","flow","flight"]
    * 输出:"fl"
    */

    public class LongestCommonPrefixExercise {
    public static String longestCommonPrefix(String[] strs) {
    if (strs == null || strs.length == 0) {
    return "";
    }

    String prefix = strs[0];
    for (int i = 1; i < strs.length; i++) {
    while (strs[i].indexOf(prefix) != 0) {
    prefix = prefix.substring(0, prefix.length() 1);
    if (prefix.isEmpty()) {
    return "";
    }
    }
    }
    return prefix;
    }

    public static void main(String[] args) {
    String[] strs1 = {"flower", "flow", "flight"};
    System.out.println(longestCommonPrefix(strs1)); // fl

    String[] strs2 = {"dog", "racecar", "car"};
    System.out.println(longestCommonPrefix(strs2)); // ""
    }
    }

    14.2 集合练习题

    练习6:两数之和

    /**
    * 给定一个整数数组和一个目标值,找出数组中和为目标值的两个数
    * 输入:nums = [2,7,11,15], target = 9
    * 输出:[0,1]
    */

    public class TwoSumExercise {
    public static int[] twoSum(int[] nums, int target) {
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
    int complement = target nums[i];
    if (map.containsKey(complement)) {
    return new int[]{map.get(complement), i};
    }
    map.put(nums[i], i);
    }
    return new int[]{};
    }

    public static void main(String[] args) {
    int[] nums = {2, 7, 11, 15};
    int target = 9;
    int[] result = twoSum(nums, target);
    System.out.println(Arrays.toString(result)); // [0, 1]
    }
    }

    练习7:删除重复元素

    /**
    * 删除数组中的重复元素
    * 输入:[1,1,2,2,3,4,4,5]
    * 输出:[1,2,3,4,5]
    */

    public class RemoveDuplicatesExercise {
    public static List<Integer> removeDuplicates(int[] nums) {
    Set<Integer> set = new LinkedHashSet<>();
    for (int num : nums) {
    set.add(num);
    }
    return new ArrayList<>(set);
    }

    public static void main(String[] args) {
    int[] nums = {1, 1, 2, 2, 3, 4, 4, 5};
    List<Integer> result = removeDuplicates(nums);
    System.out.println(result); // [1, 2, 3, 4, 5]
    }
    }

    练习8:找出数组中的众数

    /**
    * 找出数组中出现次数最多的元素
    * 输入:[1,2,2,3,3,3,4]
    * 输出:3
    */

    public class MajorityElementExercise {
    public static int majorityElement(int[] nums) {
    Map<Integer, Integer> map = new HashMap<>();
    for (int num : nums) {
    map.put(num, map.getOrDefault(num, 0) + 1);
    }

    int maxCount = 0;
    int result = 0;
    for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
    if (entry.getValue() > maxCount) {
    maxCount = entry.getValue();
    result = entry.getKey();
    }
    }
    return result;
    }

    public static void main(String[] args) {
    int[] nums = {1, 2, 2, 3, 3, 3, 4};
    System.out.println(majorityElement(nums)); // 3
    }
    }

    练习9:两个数组的交集

    /**
    * 找出两个数组的交集
    * 输入:nums1 = [1,2,2,1], nums2 = [2,2]
    * 输出:[2]
    */

    public class IntersectionExercise {
    public static int[] intersection(int[] nums1, int[] nums2) {
    Set<Integer> set1 = new HashSet<>();
    for (int num : nums1) {
    set1.add(num);
    }

    Set<Integer> result = new HashSet<>();
    for (int num : nums2) {
    if (set1.contains(num)) {
    result.add(num);
    }
    }

    int[] arr = new int[result.size()];
    int i = 0;
    for (int num : result) {
    arr[i++] = num;
    }
    return arr;
    }

    public static void main(String[] args) {
    int[] nums1 = {1, 2, 2, 1};
    int[] nums2 = {2, 2};
    System.out.println(Arrays.toString(intersection(nums1, nums2))); // [2]
    }
    }

    练习10:分组字母异位词

    /**
    * 给定一个字符串数组,将字母异位词组合在一起
    * 输入:["eat","tea","tan","ate","nat","bat"]
    * 输出:[["bat"],["nat","tan"],["ate","eat","tea"]]
    */

    public class GroupAnagramsExercise {
    public static List<List<String>> groupAnagrams(String[] strs) {
    Map<String, List<String>> map = new HashMap<>();

    for (String str : strs) {
    char[] chars = str.toCharArray();
    Arrays.sort(chars);
    String key = new String(chars);

    if (!map.containsKey(key)) {
    map.put(key, new ArrayList<>());
    }
    map.get(key).add(str);
    }

    return new ArrayList<>(map.values());
    }

    public static void main(String[] args) {
    String[] strs = {"eat", "tea", "tan", "ate", "nat", "bat"};
    List<List<String>> result = groupAnagrams(strs);
    System.out.println(result);
    }
    }

    14.3 高级练习题

    练习11:LRU缓存实现

    /**
    * 实现LRU(最近最少使用)缓存
    * 要求:get和put操作的时间复杂度都是O(1)
    */

    import java.util.LinkedHashMap;
    import java.util.Map;

    public class LRUCache<K, V> extends LinkedHashMap<K, V> {
    private final int capacity;

    public LRUCache(int capacity) {
    super(capacity, 0.75f, true); // accessOrder=true
    this.capacity = capacity;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
    return size() > capacity;
    }

    public static void main(String[] args) {
    LRUCache<Integer, String> cache = new LRUCache<>(3);

    cache.put(1, "one");
    cache.put(2, "two");
    cache.put(3, "three");
    System.out.println(cache); // {1=one, 2=two, 3=three}

    cache.get(1); // 访问1
    cache.put(4, "four"); // 添加4,移除最少使用的2
    System.out.println(cache); // {3=three, 1=one, 4=four}
    }
    }

    练习12:实现一个简单的HashMap

    /**
    * 实现一个简单的HashMap
    * 包含put、get、remove方法
    */

    public class SimpleHashMap<K, V> {
    private static class Node<K, V> {
    K key;
    V value;
    Node<K, V> next;

    Node(K key, V value) {
    this.key = key;
    this.value = value;
    }
    }

    private Node<K, V>[] table;
    private int size;
    private static final int DEFAULT_CAPACITY = 16;

    @SuppressWarnings("unchecked")
    public SimpleHashMap() {
    table = new Node[DEFAULT_CAPACITY];
    }

    private int hash(K key) {
    return key == null ? 0 : Math.abs(key.hashCode() % table.length);
    }

    public void put(K key, V value) {
    int index = hash(key);
    Node<K, V> node = table[index];

    // 查找是否已存在
    while (node != null) {
    if ((key == null && node.key == null) ||
    (key != null && key.equals(node.key))) {
    node.value = value;
    return;
    }
    node = node.next;
    }

    // 添加新节点
    Node<K, V> newNode = new Node<>(key, value);
    newNode.next = table[index];
    table[index] = newNode;
    size++;
    }

    public V get(K key) {
    int index = hash(key);
    Node<K, V> node = table[index];

    while (node != null) {
    if ((key == null && node.key == null) ||
    (key != null && key.equals(node.key))) {
    return node.value;
    }
    node = node.next;
    }
    return null;
    }

    public V remove(K key) {
    int index = hash(key);
    Node<K, V> node = table[index];
    Node<K, V> prev = null;

    while (node != null) {
    if ((key == null && node.key == null) ||
    (key != null && key.equals(node.key))) {
    if (prev == null) {
    table[index] = node.next;
    } else {
    prev.next = node.next;
    }
    size;
    return node.value;
    }
    prev = node;
    node = node.next;
    }
    return null;
    }

    public int size() {
    return size;
    }

    public static void main(String[] args) {
    SimpleHashMap<String, Integer> map = new SimpleHashMap<>();
    map.put("Java", 95);
    map.put("Python", 88);
    map.put("C++", 92);

    System.out.println("Java: " + map.get("Java"));
    System.out.println("Size: " + map.size());

    map.remove("Python");
    System.out.println("Size after remove: " + map.size());
    }
    }

    练习13:实现一个线程安全的计数器

    /**
    * 实现一个线程安全的计数器
    * 使用ConcurrentHashMap统计多线程环境下的访问次数
    */

    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.atomic.AtomicInteger;

    public class ThreadSafeCounter {
    private ConcurrentHashMap<String, AtomicInteger> counter = new ConcurrentHashMap<>();

    public void increment(String key) {
    counter.computeIfAbsent(key, k -> new AtomicInteger(0)).incrementAndGet();
    }

    public int get(String key) {
    AtomicInteger count = counter.get(key);
    return count == null ? 0 : count.get();
    }

    public void showAll() {
    counter.forEach((k, v) -> System.out.println(k + ": " + v.get()));
    }

    public static void main(String[] args) throws InterruptedException {
    ThreadSafeCounter counter = new ThreadSafeCounter();

    // 创建多个线程同时增加计数
    Thread[] threads = new Thread[10];
    for (int i = 0; i < 10; i++) {
    threads[i] = new Thread(() -> {
    for (int j = 0; j < 1000; j++) {
    counter.increment("page1");
    counter.increment("page2");
    }
    });
    threads[i].start();
    }

    // 等待所有线程完成
    for (Thread thread : threads) {
    thread.join();
    }

    // 显示结果
    counter.showAll();
    // 输出:page1: 10000, page2: 10000
    }
    }

    练习14:实现一个简单的JSON解析器

    /**
    * 实现一个简单的JSON解析器
    * 将JSON字符串解析为Map
    */

    import java.util.*;

    public class SimpleJSONParser {
    public static Map<String, Object> parse(String json) {
    Map<String, Object> result = new LinkedHashMap<>();

    // 去掉首尾的大括号
    json = json.trim();
    if (json.startsWith("{")) {
    json = json.substring(1);
    }
    if (json.endsWith("}")) {
    json = json.substring(0, json.length() 1);
    }

    // 分割键值对
    String[] pairs = json.split(",");
    for (String pair : pairs) {
    String[] kv = pair.split(":");
    if (kv.length == 2) {
    String key = kv[0].trim().replace("\\"", "");
    String value = kv[1].trim().replace("\\"", "");

    // 尝试解析为数字
    try {
    if (value.contains(".")) {
    result.put(key, Double.parseDouble(value));
    } else {
    result.put(key, Integer.parseInt(value));
    }
    } catch (NumberFormatException e) {
    // 解析为字符串
    result.put(key, value);
    }
    }
    }

    return result;
    }

    public static void main(String[] args) {
    String json = "{\\"name\\":\\"张三\\",\\"age\\":25,\\"score\\":95.5}";
    Map<String, Object> map = parse(json);
    System.out.println(map);
    // 输出:{name=张三, age=25, score=95.5}
    }
    }

    练习15:实现一个简单的表达式计算器

    /**
    * 实现一个简单的表达式计算器
    * 支持加减乘除和括号
    */

    import java.util.*;

    public class SimpleCalculator {
    public static double calculate(String expression) {
    Stack<Double> numbers = new Stack<>();
    Stack<Character> operators = new Stack<>();

    for (int i = 0; i < expression.length(); i++) {
    char ch = expression.charAt(i);

    if (ch == ' ') continue;

    if (Character.isDigit(ch)) {
    StringBuilder sb = new StringBuilder();
    while (i < expression.length() &&
    (Character.isDigit(expression.charAt(i)) ||
    expression.charAt(i) == '.')) {
    sb.append(expression.charAt(i++));
    }
    i;
    numbers.push(Double.parseDouble(sb.toString()));
    } else if (ch == '(') {
    operators.push(ch);
    } else if (ch == ')') {
    while (operators.peek() != '(') {
    numbers.push(applyOp(operators.pop(), numbers.pop(), numbers.pop()));
    }
    operators.pop();
    } else if (ch == '+' || ch == '-' || ch == '*' || ch == '/') {
    while (!operators.isEmpty() && hasPrecedence(ch, operators.peek())) {
    numbers.push(applyOp(operators.pop(), numbers.pop(), numbers.pop()));
    }
    operators.push(ch);
    }
    }

    while (!operators.isEmpty()) {
    numbers.push(applyOp(operators.pop(), numbers.pop(), numbers.pop()));
    }

    return numbers.pop();
    }

    private static boolean hasPrecedence(char op1, char op2) {
    if (op2 == '(' || op2 == ')') return false;
    if ((op1 == '*' || op1 == '/') && (op2 == '+' || op2 == '-')) return false;
    return true;
    }

    private static double applyOp(char op, double b, double a) {
    switch (op) {
    case '+': return a + b;
    case '-': return a b;
    case '*': return a * b;
    case '/':
    if (b == 0) throw new ArithmeticException("除数不能为0");
    return a / b;
    }
    return 0;
    }

    public static void main(String[] args) {
    System.out.println(calculate("10 + 2 * 6")); // 22.0
    System.out.println(calculate("100 * 2 + 12")); // 212.0
    System.out.println(calculate("100 * ( 2 + 12 )")); // 1400.0
    System.out.println(calculate("100 * ( 2 + 12 ) / 14")); // 100.0
    }
    }

    赞(0)
    未经允许不得转载:171主机测评 » 03-Java常用API与集合框架
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址