字符串和数组
# 04.字符串和数组
# 目录介绍
# 4.1 字符串概述
# 4.1.1 String类介绍
Java 中的字符串是对象,使用 String 类表示。String 类在 java.lang 包中,自动导入,不需要手动 import。
String s1 = "Hello World"; // 字面量方式
String s2 = new String("Hello"); // new 方式
2
对比 C++:C++ 有两种字符串——C 风格字符串(char[])和 std::string。Java 只有 String 类,没有 C 风格的字符数组字符串。
# 4.1.2 字符串不可变性
Java 的 String 是不可变的(immutable):一旦创建,内容不能被修改。
String s = "Hello";
s = s + " World"; // 没有修改原字符串,而是创建了新字符串
// 原来的 "Hello" 对象没有改变,s 指向了新的 "Hello World" 对象
2
3
为什么设计成不可变?
- 线程安全:不可变对象天然是线程安全的。
- 字符串常量池:相同内容的字符串可以共享。
- 安全性:作为 HashMap 的 key、网络连接参数等不会被意外修改。
String 不可变性的底层原理:在 JDK 8 及之前,String 内部使用 private final char[] value 存储字符数据;JDK 9+ 改为 private final byte[] value(Latin1 编码用1字节/字符,UTF-16 用2字节/字符,称为 Compact Strings 优化)。final 保证引用不可变,而 private 且不提供修改方法保证内容不可变。hashCode() 只在首次调用时计算并缓存到 private int hash 字段,后续调用直接返回缓存值——这正是因为不可变性才能安全缓存。
# 4.1.3 字符串常量池
Java 维护了一个字符串常量池(String Pool),用于存储字符串字面量。
字符串常量池的底层原理:JDK 7 之前,字符串常量池位于方法区(永久代 PermGen);JDK 7+ 移至**堆内存(Heap)**中,这样常量池中的字符串对象也能被 GC 回收。字面量 "Hello" 在编译时写入 .class 文件的常量池(Constant Pool),类加载时调用 String.intern() 将其放入运行时字符串常量池。new String("Hello") 会在堆上创建新对象,但传入的 "Hello" 字面量仍来自常量池。可以手动调用 intern() 方法将堆上的字符串放入常量池并返回池中引用。
String s1 = "Hello"; // 放入常量池
String s2 = "Hello"; // 从常量池中获取同一个对象
String s3 = new String("Hello"); // 在堆上创建新对象
System.out.println(s1 == s2); // true(同一个对象)
System.out.println(s1 == s3); // false(不同对象)
System.out.println(s1.equals(s3)); // true(内容相同)
2
3
4
5
6
7
# 4.1.4 综合案例:字符串内存分析器
编写一个程序,演示字符串常量池、不可变性和 intern() 方法的行为,帮助理解 String 的内存模型。
public class StringMemoryAnalyzer {
public static void main(String[] args) {
// 常量池复用验证
System.out.println("===== 常量池复用 =====");
String s1 = "Hello";
String s2 = "Hello";
String s3 = new String("Hello");
String s4 = s3.intern();
System.out.println("s1 == s2: " + (s1 == s2)); // true 常量池同一对象
System.out.println("s1 == s3: " + (s1 == s3)); // false 堆上新对象
System.out.println("s1 == s4: " + (s1 == s4)); // true intern返回常量池引用
System.out.println("s1.equals(s3): " + s1.equals(s3)); // true 内容相同
// 编译期常量折叠
System.out.println("\n===== 编译期优化 =====");
String a = "Hello" + "World"; // 编译期合并为"HelloWorld"
String b = "HelloWorld";
String c = "Hello";
String d = c + "World"; // 运行时拼接,新对象
System.out.println("编译期拼接 == 字面量: " + (a == b)); // true
System.out.println("运行时拼接 == 字面量: " + (d == b)); // false
// 不可变性演示
System.out.println("\n===== 不可变性 =====");
String original = "Java";
String modified = original.concat(" Language");
System.out.println("original: " + original); // 未改变
System.out.println("modified: " + modified); // 新对象
System.out.println("原始未变: " + (original.equals("Java")));
// hashCode缓存验证
System.out.println("\n===== hashCode缓存 =====");
String test = "Test String";
long start = System.nanoTime();
for (int i = 0; i < 1000000; i++) test.hashCode();
long time1 = System.nanoTime() - start;
start = System.nanoTime();
for (int i = 0; i < 1000000; i++) test.hashCode();
long time2 = System.nanoTime() - start;
System.out.println("首次100万次hashCode: " + time1 + "ns");
System.out.println("再次100万次hashCode: " + time2 + "ns (缓存命中)");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 4.1.5 训练题
选择题:以下哪个说法是正确的?
- A.
String s = "abc"和String s = new String("abc")创建了相同数量的对象 - B.
String的不可变性是因为final修饰了char[]/byte[]的引用 - C.
String的hashCode()每次调用都会重新计算 - D. JDK 9 之后
String内部使用byte[]替代了char[]
- A.
代码题:以下代码共创建了几个
String对象?请分别说明在堆上和常量池中的分布:String s1 = "Hello"; String s2 = "Hello"; String s3 = new String("Hello"); String s4 = new String("Hello");1
2
3
4思考题:
String的不可变性设计虽然带来了线程安全和缓存优势,但也带来了性能问题(如频繁拼接产生大量临时对象)。假如让你重新设计String类,你会如何在不可变性和性能之间取得平衡?
# 4.2 字符串操作
# 4.2.1 创建字符串
// 字面量方式(推荐)
String s1 = "Hello";
// new 方式
String s2 = new String("Hello");
// 字符数组转字符串
char[] chars = {'H', 'e', 'l', 'l', 'o'};
String s3 = new String(chars);
// 字节数组转字符串
byte[] bytes = {72, 101, 108, 108, 111};
String s4 = new String(bytes);
2
3
4
5
6
7
8
9
10
11
12
13
# 4.2.2 字符串常用方法
String s = "Hello World";
// 获取长度
System.out.println(s.length()); // 11
// 获取字符
System.out.println(s.charAt(0)); // H
// 查找子串
System.out.println(s.indexOf("World")); // 6
System.out.println(s.contains("World")); // true
// 截取子串
System.out.println(s.substring(6)); // World
System.out.println(s.substring(0, 5)); // Hello
// 大小写转换
System.out.println(s.toUpperCase()); // HELLO WORLD
System.out.println(s.toLowerCase()); // hello world
// 去除首尾空格
String s2 = " Hello ";
System.out.println(s2.trim()); // Hello
// 替换
System.out.println(s.replace("World", "Java")); // Hello Java
// 分割
String csv = "张三,李四,王五";
String[] names = csv.split(",");
// names = ["张三", "李四", "王五"]
// 判断开头结尾
System.out.println(s.startsWith("Hello")); // true
System.out.println(s.endsWith("World")); // true
// 判断是否为空
System.out.println("".isEmpty()); // true
System.out.println(" ".isBlank()); // true (JDK 11+)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 4.2.3 字符串比较
String s1 = "Hello";
String s2 = "hello";
// equals:区分大小写
System.out.println(s1.equals(s2)); // false
// equalsIgnoreCase:不区分大小写
System.out.println(s1.equalsIgnoreCase(s2)); // true
// compareTo:字典序比较,返回差值
System.out.println("abc".compareTo("abd")); // -1
System.out.println("abc".compareTo("abc")); // 0
System.out.println("abd".compareTo("abc")); // 1
2
3
4
5
6
7
8
9
10
11
12
13
# 4.2.4 字符串转换
// 基本类型转字符串
String s1 = String.valueOf(123); // "123"
String s2 = String.valueOf(3.14); // "3.14"
String s3 = String.valueOf(true); // "true"
String s4 = 123 + ""; // "123"(简便写法)
// 字符串转基本类型
int i = Integer.parseInt("123");
double d = Double.parseDouble("3.14");
boolean b = Boolean.parseBoolean("true");
// 字符串转字符数组
char[] chars = "Hello".toCharArray();
// 字符数组转字符串
String s = new String(chars);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 4.2.5 字符串格式化
String name = "张三";
int age = 25;
double salary = 8500.50;
// String.format
String info = String.format("姓名:%s,年龄:%d,薪资:%.2f", name, age, salary);
System.out.println(info);
// 输出:姓名:张三,年龄:25,薪资:8500.50
// printf(直接输出)
System.out.printf("姓名:%s,年龄:%d%n", name, age);
2
3
4
5
6
7
8
9
10
11
# 4.2.6 综合案例:文本处理工具
编写一个程序,综合运用字符串的创建、常用方法、比较、转换和格式化,实现一个简单的文本分析工具。
public class TextProcessor {
public static void main(String[] args) {
String text = " Hello, World! Welcome to Java Programming. Java is great! ";
// 基本处理
System.out.println("===== 基本处理 =====");
String trimmed = text.trim();
System.out.println("原始: [" + text + "]");
System.out.println("去空格: [" + trimmed + "]");
System.out.println("长度: " + trimmed.length());
System.out.println("大写: " + trimmed.toUpperCase());
System.out.println("小写: " + trimmed.toLowerCase());
// 查找和替换
System.out.println("\n===== 查找和替换 =====");
System.out.println("包含Java: " + trimmed.contains("Java"));
System.out.println("Java首次位置: " + trimmed.indexOf("Java"));
System.out.println("Java末次位置: " + trimmed.lastIndexOf("Java"));
System.out.println("替换Java→Python: " + trimmed.replace("Java", "Python"));
System.out.println("以Hello开头: " + trimmed.startsWith("Hello"));
System.out.println("以!结尾: " + trimmed.endsWith("!"));
// 截取和分割
System.out.println("\n===== 截取和分割 =====");
System.out.println("前5字符: " + trimmed.substring(0, 5));
String[] words = trimmed.split("[\\s,!.]+");
System.out.println("单词数: " + words.length);
System.out.print("所有单词: ");
for (String w : words) System.out.print("[" + w + "] ");
System.out.println();
// 统计单词频率
System.out.println("\n===== 单词频率(简易版) =====");
String lower = trimmed.toLowerCase();
String[] allWords = lower.split("[\\s,!.]+");
for (String target : new String[]{"java", "hello", "to"}) {
int count = 0;
for (String w : allWords) {
if (w.equals(target)) count++;
}
System.out.println(" \"" + target + "\" 出现 " + count + " 次");
}
// 字符串格式化
System.out.println("\n===== 格式化输出 =====");
String name = "张三";
int age = 25;
double salary = 15800.50;
System.out.println(String.format("姓名:%-6s 年龄:%3d 薪资:¥%,.2f", name, age, salary));
// 字符串与其他类型互转
System.out.println("\n===== 类型转换 =====");
int num = Integer.parseInt("12345");
String numStr = String.valueOf(num * 2);
char[] chars = "Hello".toCharArray();
String fromChars = new String(chars);
System.out.println("字符串→int: " + num);
System.out.println("int→字符串: " + numStr);
System.out.println("字符串→char[]: " + java.util.Arrays.toString(chars));
System.out.println("char[]→字符串: " + fromChars);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# 4.2.7 字符串操作训练题
训练1:编写一个方法 boolean isPalindrome(String s),判断字符串是否为回文(忽略大小写和空格),如 "A man a plan a canal Panama" → true。
训练2:编写一个方法 String compress(String s),实现基本的字符串压缩。如 "aabcccccaaa" → "a2b1c5a3"。如果压缩后更长,返回原字符串。
训练3:以下代码创建了几个 String 对象?分别在哪里?
String s1 = "Hello";
String s2 = new String("Hello");
String s3 = s2.intern();
System.out.println(s1 == s3); // ?
2
3
4
思考:String.intern() 方法的作用是什么?在什么场景下使用可以优化内存?使用不当会有什么风险?(提示:JDK 6 的永久代 vs JDK 7+ 的堆)
# 4.3 StringBuilder和StringBuffer
# 4.3.1 为什么需要StringBuilder
String 是不可变的,每次拼接都会创建新对象,性能较差:
// 不推荐:大量拼接时性能差
String s = "";
for (int i = 0; i < 10000; i++) {
s += i; // 每次都创建新 String 对象
}
// 推荐:使用 StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append(i); // 在原对象上追加,不创建新对象
}
String result = sb.toString();
2
3
4
5
6
7
8
9
10
11
12
# 4.3.2 StringBuilder用法
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" ");
sb.append("World");
System.out.println(sb.toString()); // Hello World
// 链式调用
String result = new StringBuilder()
.append("姓名:").append("张三")
.append(",年龄:").append(25)
.toString();
// 其他常用方法
sb.insert(5, ","); // 在指定位置插入
sb.delete(5, 6); // 删除指定范围
sb.replace(0, 5, "Hi"); // 替换指定范围
sb.reverse(); // 反转
System.out.println(sb.length()); // 获取长度
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 4.3.3 StringBuilder和StringBuffer区别
| 对比项 | StringBuilder | StringBuffer |
|---|---|---|
| 线程安全 | 不安全 | 安全(方法加了 synchronized) |
| 性能 | 快 | 慢 |
| 适用场景 | 单线程环境 | 多线程环境 |
实际开发中绝大多数情况使用
StringBuilder即可。
# 4.3.4 StringBuilder底层原理
疑惑:StringBuilder 为什么比 String 拼接快?
答疑:String 每次拼接 + 都创建新的 String 对象和新的 char[]/byte[] 数组。而 StringBuilder 内部维护一个可扩展的数组,append() 只是向数组尾部追加数据,只有容量不足时才扩容。
论证:
// String 拼接(编译后的字节码,JDK 8):
String s = "a" + "b" + "c";
// 编译器优化为常量折叠:"abc"(编译期确定的常量会直接合并)
// 变量拼接(JDK 8 编译后):
String s = str1 + str2;
// 等价于 new StringBuilder().append(str1).append(str2).toString()
// 但如果在循环中,每次迭代都会创建新的 StringBuilder!
// JDK 9+ 使用 StringConcatFactory(invokedynamic),更高效
2
3
4
5
6
7
8
9
10
结果展示——性能对比(拼接10万次):
| 方式 | 耗时 | 内存消耗 |
|---|---|---|
| String += | ~5000ms | 大量临时对象 |
| StringBuilder | ~3ms | 单对象扩容 |
| StringBuffer | ~5ms | 单对象扩容 + 同步开销 |
# 4.3.5 综合案例:高效字符串拼接对比
编写一个程序,对比 String 拼接、StringBuilder 和 StringBuffer 的性能,并展示 StringBuilder 的常用操作。
public class StringBuilderDemo {
public static void main(String[] args) {
// 性能对比
System.out.println("===== 性能对比(拼接10000次) =====");
int count = 10000;
// String += 拼接
long start = System.currentTimeMillis();
String s = "";
for (int i = 0; i < count; i++) s += i;
long t1 = System.currentTimeMillis() - start;
System.out.println("String +=: " + t1 + "ms, 长度=" + s.length());
// StringBuilder
start = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; i++) sb.append(i);
long t2 = System.currentTimeMillis() - start;
System.out.println("StringBuilder: " + t2 + "ms, 长度=" + sb.length());
// StringBuffer(线程安全)
start = System.currentTimeMillis();
StringBuffer sbuf = new StringBuffer();
for (int i = 0; i < count; i++) sbuf.append(i);
long t3 = System.currentTimeMillis() - start;
System.out.println("StringBuffer: " + t3 + "ms, 长度=" + sbuf.length());
// StringBuilder常用操作
System.out.println("\n===== StringBuilder操作 =====");
StringBuilder builder = new StringBuilder("Hello World");
System.out.println("原始: " + builder);
builder.insert(5, ",");
System.out.println("insert(5,','): " + builder);
builder.delete(5, 6);
System.out.println("delete(5,6): " + builder);
builder.replace(6, 11, "Java");
System.out.println("replace World→Java: " + builder);
builder.reverse();
System.out.println("reverse: " + builder);
// 链式调用构建SQL
System.out.println("\n===== 链式调用构建文本 =====");
String sql = new StringBuilder()
.append("SELECT * FROM users")
.append(" WHERE age > ").append(18)
.append(" AND name LIKE '").append("%张%").append("'")
.append(" ORDER BY id")
.append(" LIMIT ").append(10)
.toString();
System.out.println(sql);
// 容量和长度
System.out.println("\n===== 容量机制 =====");
StringBuilder cap = new StringBuilder();
System.out.println("初始容量: " + cap.capacity() + ", 长度: " + cap.length());
cap.append("Hello");
System.out.println("追加后容量: " + cap.capacity() + ", 长度: " + cap.length());
cap.append("This is a longer string to trigger expansion");
System.out.println("扩容后容量: " + cap.capacity() + ", 长度: " + cap.length());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# 4.3.6 StringBuilder训练题
训练1:使用 StringBuilder 实现字符串反转,但只反转字母,不反转数字和符号。如 "a1b2c3" → "c1b2a3"。
训练2:StringBuilder 的默认初始容量是多少?扩容策略是什么?与 ArrayList 的扩容策略有何异同?
# 4.4 数组
# 4.4.1 数组基本概念
数组是一种用于存储相同类型元素的连续内存数据结构。Java 中数组的大小在创建后不可改变。
数组的底层原理:Java 数组在 JVM 中是一个特殊的对象,由 JVM 直接创建(不是通过类加载器)。数组对象的内存布局为:对象头(Mark Word + 类型指针 + 数组长度)+ 连续的元素数据。基本类型数组(如 int[])直接存储值,引用类型数组(如 String[])存储的是引用(指针)。数组越界检查由 JVM 在每次访问时执行,虽然有微小的性能开销,但 JIT 编译器会进行边界检查消除(Bounds Check Elimination)优化——如果编译器能证明索引不会越界,就会跳过检查。
对比 C++:C++ 的数组分为 C 风格数组和 std::array/std::vector。Java 的数组是对象,自带 length 属性,有边界检查(越界抛异常),比 C++ 安全。
# 4.4.2 数组的声明和初始化
// 声明方式(推荐第一种)
int[] arr1; // Java 风格(推荐)
int arr2[]; // C++ 风格(不推荐)
// 动态初始化:指定大小,默认值为0
int[] nums = new int[5]; // [0, 0, 0, 0, 0]
// 静态初始化:直接给出元素
int[] nums2 = {1, 2, 3, 4, 5};
int[] nums3 = new int[]{1, 2, 3, 4, 5};
// 字符串数组
String[] names = {"张三", "李四", "王五"};
2
3
4
5
6
7
8
9
10
11
12
13
# 4.4.3 访问数组元素
int[] arr = {10, 20, 30, 40, 50};
// 通过索引访问(从0开始)
System.out.println(arr[0]); // 10
System.out.println(arr[4]); // 50
// 获取数组长度
System.out.println(arr.length); // 5
// 越界访问会抛异常(比 C++ 安全)
// System.out.println(arr[5]); // ArrayIndexOutOfBoundsException
2
3
4
5
6
7
8
9
10
11
# 4.4.4 数组遍历
int[] arr = {10, 20, 30, 40, 50};
// 方式1:普通 for 循环
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
// 方式2:增强 for 循环(for-each)
for (int num : arr) {
System.out.println(num);
}
// 方式3:Arrays.toString 直接打印
System.out.println(Arrays.toString(arr));
// 输出:[10, 20, 30, 40, 50]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
对比 C++:C++11 也有范围 for 循环 for (int x : arr),但 C++ 没有 Arrays.toString 这样方便的工具。
# 4.4.5 多维数组
// 二维数组
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// 访问元素
System.out.println(matrix[1][2]); // 6
// 遍历二维数组
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
System.out.print(matrix[i][j] + " ");
}
System.out.println();
}
// Java 支持不规则数组(每行长度可以不同)
int[][] irregular = new int[3][];
irregular[0] = new int[]{1, 2};
irregular[1] = new int[]{3, 4, 5};
irregular[2] = new int[]{6};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 4.4.6 数组作为方法参数
public static void printArray(int[] arr) {
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
}
public static int[] reverseArray(int[] arr) {
int[] result = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
result[i] = arr[arr.length - 1 - i];
}
return result;
}
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
printArray(arr);
int[] reversed = reverseArray(arr);
printArray(reversed); // 5 4 3 2 1
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 4.4.7 Arrays工具类
java.util.Arrays 提供了大量数组操作的工具方法:
import java.util.Arrays;
int[] arr = {5, 3, 1, 4, 2};
// 排序
Arrays.sort(arr);
System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5]
// 二分查找(数组必须已排序)
int index = Arrays.binarySearch(arr, 3);
System.out.println(index); // 2
// 填充
int[] filled = new int[5];
Arrays.fill(filled, 10);
System.out.println(Arrays.toString(filled)); // [10, 10, 10, 10, 10]
// 复制
int[] copy = Arrays.copyOf(arr, 3);
System.out.println(Arrays.toString(copy)); // [1, 2, 3]
// 比较
int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
System.out.println(Arrays.equals(a, b)); // true
System.out.println(a == b); // false
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 4.4.8 综合案例:数组操作工具集
编写一个程序,综合运用数组的声明、初始化、遍历、排序、查找、多维数组和 Arrays 工具类。
import java.util.Arrays;
public class ArrayToolkit {
// 查找最大值和最小值
static int[] findMinMax(int[] arr) {
int min = arr[0], max = arr[0];
for (int n : arr) {
if (n < min) min = n;
if (n > max) max = n;
}
return new int[]{min, max};
}
// 合并两个有序数组
static int[] mergeSorted(int[] a, int[] b) {
int[] result = new int[a.length + b.length];
int i = 0, j = 0, k = 0;
while (i < a.length && j < b.length) {
result[k++] = a[i] <= b[j] ? a[i++] : b[j++];
}
while (i < a.length) result[k++] = a[i++];
while (j < b.length) result[k++] = b[j++];
return result;
}
// 去重(保持顺序)
static int[] removeDuplicates(int[] arr) {
if (arr.length == 0) return arr;
int[] temp = new int[arr.length];
int count = 0;
for (int n : arr) {
boolean exists = false;
for (int j = 0; j < count; j++) {
if (temp[j] == n) { exists = true; break; }
}
if (!exists) temp[count++] = n;
}
return Arrays.copyOf(temp, count);
}
public static void main(String[] args) {
// 基本操作
System.out.println("===== 基本操作 =====");
int[] arr = {64, 25, 12, 22, 11, 25, 64};
System.out.println("原数组: " + Arrays.toString(arr));
int[] minMax = findMinMax(arr);
System.out.println("最小值: " + minMax[0] + ", 最大值: " + minMax[1]);
// 排序和查找
int[] sorted = Arrays.copyOf(arr, arr.length);
Arrays.sort(sorted);
System.out.println("排序后: " + Arrays.toString(sorted));
int idx = Arrays.binarySearch(sorted, 22);
System.out.println("二分查找22: 索引=" + idx);
// 去重
int[] unique = removeDuplicates(arr);
System.out.println("去重后: " + Arrays.toString(unique));
// 合并有序数组
System.out.println("\n===== 合并有序数组 =====");
int[] a = {1, 3, 5, 7};
int[] b = {2, 4, 6, 8, 10};
int[] merged = mergeSorted(a, b);
System.out.println(Arrays.toString(a) + " + " + Arrays.toString(b));
System.out.println("合并: " + Arrays.toString(merged));
// Arrays工具类
System.out.println("\n===== Arrays工具 =====");
int[] filled = new int[5];
Arrays.fill(filled, 7);
System.out.println("fill(7): " + Arrays.toString(filled));
System.out.println("equals: " + Arrays.equals(new int[]{1,2}, new int[]{1,2}));
// 多维数组:杨辉三角
System.out.println("\n===== 杨辉三角(前6行) =====");
int[][] pascal = new int[6][];
for (int i = 0; i < 6; i++) {
pascal[i] = new int[i + 1];
pascal[i][0] = pascal[i][i] = 1;
for (int j = 1; j < i; j++) {
pascal[i][j] = pascal[i - 1][j - 1] + pascal[i - 1][j];
}
System.out.println(" " + " ".repeat(10 - i * 2) + Arrays.toString(pascal[i]));
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# 4.4.9 数组训练题
训练1:实现一个方法 int[] merge(int[] a, int[] b),将两个已排序的数组合并为一个新的有序数组(归并操作,时间复杂度 O(n+m))。
训练2:实现二维数组的"螺旋矩阵"打印。给定一个 m×n 的二维数组,按顺时针螺旋顺序打印所有元素。
思考:Java 数组和 ArrayList 都可以存储一组数据,什么时候用数组更好?什么时候用 ArrayList 更好?
# 4.4.10 字符串反转
public class Main {
public static String reverse(String str) {
return new StringBuilder(str).reverse().toString();
}
public static void main(String[] args) {
System.out.println(reverse("Hello World"));
// 输出:dlroW olleH
}
}
2
3
4
5
6
7
8
9
10
# 4.4.11 数组排序
import java.util.Arrays;
public class Main {
// 冒泡排序
public static void bubbleSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
public static void main(String[] args) {
int[] arr = {5, 3, 8, 1, 9, 2};
bubbleSort(arr);
System.out.println(Arrays.toString(arr));
// 输出:[1, 2, 3, 5, 8, 9]
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23