前言
数组和字符串是编程中最基础也是最重要的数据结构之一,几乎所有的编程语言都提供了对它们的原生支持。在Java中,数组和字符串的操作有着丰富的API和独特的特性。本文将深入探讨一维和多维数组的操作技巧,以及String和StringBuilder类的常用API,帮助开发者更高效地处理相关数据。
一、数组基础与操作
1.1 数组的基本概念
数组是一种线性数据结构,用于存储相同类型的元素集合。在Java中,数组是对象,具有固定长度,创建后无法改变大小。
// 声明并初始化一维数组
int[] numbers = new int[5]; // 长度为5的整型数组
String[] names = {"Alice", "Bob", "Charlie"}; // 初始化时赋值
1.2 一维数组的常见操作
遍历数组
// 传统for循环
for (int i = 0; i < numbers.length; i++) {
System.out.println(numbers[i]);
}
// 增强for循环(for-each)
for (String name : names) {
System.out.println(name);
}
数组排序
int[] nums = {5, 2, 9, 1, 5};
Arrays.sort(nums); // 升序排序
System.out.println(Arrays.toString(nums)); // [1, 2, 5, 5, 9]
数组复制
int[] original = {1, 2, 3, 4, 5};
int[] copy1 = Arrays.copyOf(original, original.length); // 完整复制
int[] copy2 = Arrays.copyOfRange(original, 1, 3); // 复制索引1到2的元素 [2, 3]
数组查找
int[] sorted = {1, 3, 5, 7, 9};
int index = Arrays.binarySearch(sorted, 5); // 返回2
1.3 多维数组操作
多维数组实际上是数组的数组。在Java中,最常见的多维数组是二维数组。
二维数组声明与初始化
// 声明并初始化二维数组
int[][] matrix = new int[3][4]; // 3行4列
int[][] identityMatrix = {
{1, 0, 0},
{0, 1, 0},
{0, 0, 1}
};
遍历二维数组
// 嵌套for循环遍历
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();
}
// 使用增强for循环
for (int[] row : matrix) {
for (int num : row) {
System.out.print(num + " ");
}
System.out.println();
}
不规则数组
Java支持不规则数组(每行长度不同):
int[][] irregular = new int[3][];
irregular[0] = new int[2];
irregular[1] = new int[3];
irregular[2] = new int[1];
1.4 数组工具类Arrays
Java提供了Arrays工具类,包含许多实用的静态方法:
// 数组比较
int[] arr1 = {1, 2, 3};
int[] arr2 = {1, 2, 3};
boolean equal = Arrays.equals(arr1, arr2); // true
// 数组填充
int[] filled = new int[5];
Arrays.fill(filled, 7); // [7, 7, 7, 7, 7]
// 数组转字符串
String arrStr = Arrays.toString(filled); // "[7, 7, 7, 7, 7]"
// 多维数组转字符串
int[][] deepArr = {{1, 2}, {3, 4}};
String deepStr = Arrays.deepToString(deepArr); // "[[1, 2], [3, 4]]"
二、字符串操作:String类
2.1 String的基本特性
在Java中,String是不可变的(immutable),任何修改操作都会返回一个新的String对象。
String s1 = "Hello"; // 字符串字面量
String s2 = new String("Hello"); // 使用构造函数
2.2 常用String API
字符串长度与空检查
String str = "Hello";
int len = str.length(); // 5
boolean isEmpty = str.isEmpty(); // false
字符串连接
String s1 = "Hello";
String s2 = "World";
String combined = s1.concat(" ").concat(s2); // "Hello World"
String withPlus = s1 + " " + s2; // 更常用的方式
字符串比较
String a = "hello";
String b = "HELLO";
boolean eq1 = a.equals(b); // false,严格比较
boolean eq2 = a.equalsIgnoreCase(b); // true,忽略大小写
int cmp = a.compareTo(b); // 32,字典序比较
字符串查找
String text = "Java programming is fun";
boolean contains = text.contains("pro"); // true
int index1 = text.indexOf('a'); // 1,第一次出现的位置
int index2 = text.lastIndexOf('a'); // 18,最后一次出现的位置
boolean starts = text.startsWith("Java"); // true
boolean ends = text.endsWith("fun"); // true
字符串截取与分割
String original = "Hello,World,Java";
String sub1 = original.substring(7); // "World,Java"
String sub2 = original.substring(7, 12); // "World"
String[] parts = original.split(","); // ["Hello", "World", "Java"]
字符串替换
String text = "apple banana apple";
String replaced1 = text.replace('a', 'A'); // "Apple bAnAnA Apple"
String replaced2 = text.replace("apple", "orange"); // "orange banana orange"
String replaced3 = text.replaceFirst("apple", "orange"); // "orange banana apple"
字符串大小写转换
String mixed = "Hello World";
String upper = mixed.toUpperCase(); // "HELLO WORLD"
String lower = mixed.toLowerCase(); // "hello world"
去除空白字符
String withSpaces = " hello world \t";
String trimmed = withSpaces.trim(); // "hello world"
String stripped = withSpaces.strip(); // Java 11+,同上但支持更多Unicode空白字符
字符串格式化
String formatted = String.format("Name: %s, Age: %d", "Alice", 25);
// "Name: Alice, Age: 25"
三、可变字符串:StringBuilder与StringBuffer
3.1 为什么需要可变字符串
由于String的不可变性,频繁修改字符串会产生大量临时对象,影响性能。StringBuilder(非线程安全)和StringBuffer(线程安全)提供了可变的字符串操作。
3.2 StringBuilder常用API
创建与初始化
StringBuilder sb1 = new StringBuilder(); // 默认容量16
StringBuilder sb2 = new StringBuilder(50); // 指定初始容量
StringBuilder sb3 = new StringBuilder("Hello"); // 初始内容
追加内容
StringBuilder sb = new StringBuilder("Hello");
sb.append(" ").append("World"); // "Hello World"
sb.append(123); // "Hello World123"
插入内容
sb.insert(5, ","); // "Hello, World123"
删除内容
sb.delete(5, 6); // 删除索引5的字符 "Hello World123"
sb.deleteCharAt(11); // 删除索引11的字符 "Hello World23"
替换内容
sb.replace(6, 11, "Java"); // "Hello Java23"
反转字符串
sb.reverse(); // "32 avaJ olleH"
其他操作
int len = sb.length(); // 获取长度
int cap = sb.capacity(); // 获取当前容量
sb.setLength(5); // 设置长度,截断或填充null字符
sb.ensureCapacity(100); // 确保最小容量
char ch = sb.charAt(2); // 获取指定位置字符
sb.setCharAt(2, 'X'); // 设置指定位置字符
3.3 StringBuffer与StringBuilder的区别
StringBuffer是线程安全的(方法同步),而StringBuilder不是。在单线程环境下,StringBuilder性能更好。
StringBuffer sbf = new StringBuffer(); // 线程安全版本
sbf.append("Hello").append(" World");
四、性能考虑与最佳实践
4.1 字符串拼接的性能
// 低效方式(产生多个临时String对象)
String result = "";
for (int i = 0; i < 100; i++) {
result += i; // 每次循环都创建新String对象
}
// 高效方式
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; i++) {
sb.append(i);
}
String result = sb.toString();
4.2 数组与集合的选择
对于大小固定且类型单一的数据,使用数组;对于需要动态大小或丰富操作的数据,考虑使用集合框架(如ArrayList)。
4.3 字符串常量池
Java使用字符串常量池来优化字符串存储:
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
五、实际应用示例
5.1 数组示例:矩阵转置
public static int[][] transposeMatrix(int[][] matrix) {
int rows = matrix.length;
int cols = matrix[0].length;
int[][] result = new int[cols][rows];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
result[j][i] = matrix[i][j];
}
}
return result;
}
5.2 字符串示例:回文检查
public static boolean isPalindrome(String str) {
if (str == null) return false;
str = str.toLowerCase().replaceAll("[^a-z0-9]", "");
int left = 0;
int right = str.length() - 1;
while (left < right) {
if (str.charAt(left++) != str.charAt(right--)) {
return false;
}
}
return true;
}
5.3 StringBuilder示例:字符串压缩
public static String compressString(String str) {
if (str == null || str.isEmpty()) return str;
StringBuilder compressed = 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 {
compressed.append(current).append(count);
current = str.charAt(i);
count = 1;
}
}
compressed.append(current).append(count);
return compressed.length() < str.length() ? compressed.toString() : str;
}
结语
数组和字符串是编程中最基础也是最重要的数据结构。掌握它们的高效操作方法,理解不同实现类(如String与StringBuilder)的特性差异,能够显著提高代码的性能和可读性。希望本文的内容能够帮助你在日常开发中更加得心应手地处理数组和字符串相关的问题。
在实际开发中,建议:
-
根据场景选择合适的结构(数组 vs 集合,String vs StringBuilder)
-
注意字符串操作的性能影响,特别是循环中的字符串拼接
-
充分利用Java提供的工具类(如Arrays)简化代码
-
编写清晰、可维护的代码,必要时添加注释说明复杂操作