从0-1学习Java(三)快速了解字符串、数组、“==“与equals比较
从0-1学习Java(三)快速了解字符串、数组、"=="与equals比较
字符串
字符串特性
- 不可变性:字符串一旦创建,其值不能被修改。任何修改操作都会创建新的字符串对象。
String s = "hello";
s += " world"; // 创建新的字符串对象"hello world",原"hello"对象不变
- 字符串常量池:存放于方法区,相同内容的字符串只存储一份,节约内存。网上看了很多字符串常量池的内容,都看的云里雾里,这个先了解,等后面深入学习堆和栈再回过头了解
String a = "abc";
String b = "abc";
System.out.println(a == b); // true(引用同一对象)
String c = new String("abc");
System.out.println(a == c); // false(new创建的对象在堆中)
System.out.println(a.equals(c)); // true
字符串常用方法
方法名 | 功能描述 |
---|---|
length() | 返回字符串长度 |
charAt(int index) | 获取指定索引的字符 |
substring(int beginIndex, int endIndex) | 截取子串(左闭右开) |
equals(Object obj) | 比较字符串内容 |
equalsIgnoreCase(String anotherString) | 忽略大小写比较 |
indexOf(String str) | 查找子串首次出现位置 |
lastIndexOf(String str) | 查找子串最后出现位置 |
trim() | 去除首尾空白字符 |
replace(CharSequence target, CharSequence replacement) | 替换子串 |
split(String regex) | 按正则表达式拆分字符串 |
valueOf(Object obj) | 将其他类型转换为字符串 |
public class Main {public static void main(String[] args) {// 返回字符串长度String helloString = "Hello";System.out.println(helloString.length()); // 5// 将整形转化为字符串型int num = 123;String strNum = String.valueOf(num); // "123"System.out.println(strNum);// 判断字符串是否为空(长度为 0)String emptyStr = "";boolean isEmpty = emptyStr.isEmpty(); // trueSystem.out.println(isEmpty);// equals严格比较字符串内容(区分大小写) equalsIgnoreCase忽略大小写比较内容String s1 = "Java";String s2 = new String("Java");boolean isEqual = s1.equals(s2); // trueSystem.out.println(isEqual);// 判断是否包含指定子串String str = "Hello World";boolean hasWorld = str.contains("World"); // trueSystem.out.println(hasWorld);// 返回子串首次出现的索引(未找到返回 -1)String str1 = "apple banana apple";int firstIndex = str1.indexOf("apple"); // 0int lastIndex = str1.lastIndexOf("apple"); // 13System.out.println(firstIndex);System.out.println(lastIndex);// 从 beginIndex 开始截取到末尾String str2 = "HelloWorld";String sub1 = str2.substring(5); // "World"String sub2 = str2.substring(0, 5); // "Hello" 截取 [beginIndex, endIndex) 范围的子串System.out.println(sub1);System.out.println(sub2);// 分割字符串为数组String data = "A,B,C";String[] parts = data.split(",");// [Ljava.lang.String;@52cc8049String data1 = String.join(",", parts); // "A,B,C"System.out.println(parts);System.out.println(data1);// 替换所有匹配字符String str3 = "apple";String replaced = str3.replace('a', 'A'); // "Apple"System.out.println(replaced);// 转换为全小写或全大写String mixed = "Hello World";String lower = mixed.toLowerCase(); // "hello world"String upper = mixed.toUpperCase(); // "HELLO WORLD"System.out.println(lower);System.out.println(upper);// 去除字符串两端的空白字符String str4 = " Hello ";String trimmed = str4.trim(); // "Hello"System.out.println(trimmed);// 拼接字符串(等效于 + 操作符)String s11 = "Hello";String s12 = s11.concat(" World"); // "Hello World"System.out.println(s12);// 用分隔符拼接多个字符串String joined = String.join("-", "2023", "10", "01"); // "2023-10-01"System.out.println(joined);// 返回指定索引处的字符String str5 = "Java";char c = str5.charAt(0); // 'J'System.out.println(c);}
}
StringBuilder 与 StringBuffer
这两个类是专门为频繁进行字符串拼接而准备的
- StringBuilder:非线程安全,效率高,适用于单线程环境。
- StringBuffer:线程安全,效率较低,适用于多线程环境。
常用方法:append(), insert(), delete(), reverse()
public class Main {public static void main(String[] args) {// StringBuilder常用方法StringBuilder s2 = new StringBuilder("123456");// 追加s2.append("789");System.out.println(s2); // 123456789// 插入s2.insert(0,"0");System.out.println(s2); // 0123456789// 删除s2.delete(0,1);System.out.println(s2); // 123456789// 反转s2.reverse();System.out.println(s2); // 987654321}
}
上面也说了字符串是不可变的,那么在频繁拼接字符串的情况下,效率极低,我们需要使用StringBuilder来解决这个问题,下面案例可以显著的比较效率问题
public class Main {public static void main(String[] args) {// 字符串拼接long start = System.currentTimeMillis();String s = "1";for(int i = 0;i<100000;i++){s += i;}long end = System.currentTimeMillis();System.out.println(end-start); // 3694毫秒// StringBuilder进行字符串拼接long start1 = System.currentTimeMillis();StringBuilder s1 = new StringBuilder("1");for(int i = 0;i<100000;i++){s1.append(i);}long end1 = System.currentTimeMillis();System.out.println(end1-start1); // 6毫秒}
}
数组
数组定义
int[] arr1 = {1, 2, 3}; // 声明int型数组
String[] arr2 = {"a", "b", "c"}; // 声明String型数组
System.out.println(arr1); // [I@52cc8049
System.out.println(arr2); // [Ljava.lang.String;@5b6f7412
可以看到输出的是一串字符串,这是因为隐式调用了数组的toString()
方法,如果我们想到得到定义的数组,可以使用Arrays
工具类
import java.util.Arrays;public class Main {public static void main(String[] args) {// 数组声明int[] arr1 = {1, 2, 3}; // 声明int型数组String[] arr2 = {"a", "b", "c"}; // 声明String型数组System.out.println(Arrays.toString(arr1)); // [1, 2, 3]System.out.println(Arrays.toString(arr2)); // [a, b, c]}
}
动态初始化
数组的索引,也就是数组的位置,他的第一项都是从0开始,也就是说,想拿到第一项的值,需要使用arr[0]
int[] arr = new int[3];
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
数组的循环
for循环:通过索引访问,可修改元素值
for (int i = 0; i < arr.length; i++) {System.out.println(arr[i]);
}
for-each循环:不可修改元素值
for (int num : arr) {System.out.println(num);
}
二维数组定义与遍历
int[][] matrix = {{1, 2}, {3, 4}, {5, 6}}; // 静态初始化
// int[][] matrix = new int[3][2]; // 动态初始化(3行2列)// 遍历
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();
}
关于数组越界
数组长度固定,创建后不可改变;访问数组元素时注意索引越界异常ArrayIndexOutOfBoundsException
int [] arr = {1,2,3,4,5};
arr[6] = 6;
System.out.println(arr[6]);
// java.lang.ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 5
Arrays常用方法
import java.util.Arrays;
import java.util.List;public class Main {public static void main(String args[]) {int[] arr = {1, 2, 3, 4, 5};// toString() 方法System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5]// sort() 方法 parallelSort() 方法 并行排序(大数据量时效率更高)int [] arr2 = {5, 3, 14, 21, 2};Arrays.sort(arr2); // [2, 3, 5, 14, 21]// fill() 方法 填充数组int [] arr3 = new int[5];Arrays.fill(arr3, 8); // [8, 8, 8, 8, 8]// binarySearch() 方法 二分查找int [] arr4 = {2, 3, 5, 8, 14, 21};Arrays.binarySearch(arr4, 8); // 3// equals() 方法 比较两个数组是否相等int [] arr5 = {2, 3, 5, 8, 14, 21};int [] arr6 = {2, 3, 5, 8, 14, 21};Arrays.equals(arr5, arr6); // true// deepEquals() 方法 比较两个多维数组是否相等int [][] arr7 = {{2, 3, 5, 8, 14, 21}};int [][] arr8 = {{2, 3, 5, 8, 14, 21}};Arrays.deepEquals(arr7, arr8); // true// asList() 方法 将数组转换为列表 (不可增删元素)List<String> list = Arrays.asList("A", "B"); // [A, B]// list.add("C"); // UnsupportedOperationException异常报错// stream() 方法 将数组转换为流int [] arr9 = {2, 3, 5, 8, 14, 21};// Arrays.stream(arr9).forEach(System.out::println); // 2 3 5 8 14 21// copyOf() 方法 复制数组int [] arr10 = {2, 3, 5, 8, 14, 21};Arrays.copyOf(arr10, 3); // [2, 3, 5]// copyOfRange() 方法 复制数组指定范围int [] arr11 = {2, 3, 5, 8, 14, 21};Arrays.copyOfRange(arr11, 2, 5); // [5, 8, 14]// parallelPrefix() 方法 并行前缀计算int [] arr12 = {2, 3, 5, 8, 14, 21};Arrays.parallelPrefix(arr12, (x, y) -> x + y); // [2, 5, 10, 18, 32, 53]// setAll() 方法 设置数组元素int [] arr13 = new int[5];Arrays.setAll(arr13, i -> i * 2); // [0, 2, 4, 6, 8]}
}
"=="和equals
两者都是用于比较是否相等的,先来看看它们的区别
特性 | == 运算符 | equals() 方法 |
---|---|---|
操作对象 | 可比较基本类型和引用类型 | 只能比较对象(引用类型) |
比较内容 | - 基本类型:比较值是否相等 - 引用类型:比较内存地址是否相同(是否为同一对象) | - 默认(未重写):等同于== (比较地址) - 重写后(如 String):比较对象内容是否相等 |
空指针安全 | 比较引用类型时,若一方为null 会抛出NullPointerException | 若调用者为null 会抛出NullPointerException |
不难看出,"=="更加严格一些,equals()
只要看起来相等就可以了,==
需要内存地址也相等
基本数据类型比较
int a = 10;
int b = 10;
System.out.println(a == b); // true(值相等)
引用数据类型比较
public class Main {static class Person {private String name;public Person(String name) { this.name = name; }}public static void main(String[] args) {// 字符串比较(String重写了equals())String s1 = "hello";String s2 = "hello";String s3 = new String("hello");System.out.println(s1 == s2); // true(常量池同一对象,地址相同)System.out.println(s1 == s3); // false(new创建新对象,地址不同)System.out.println(s1.equals(s3)); // true(重写后比较内容,内容相同)// 自定义对象比较(未重写equals())Person p1 = new Person("张三");Person p2 = new Person("张三");System.out.println(p1 == p2); // false(不同对象,地址不同)System.out.println(p1.equals(p2)); // false(默认比较地址,未重写equals())}
}