【第9天】Java中字串的處理
1 String類的初始化、與StringBuffer類和StringBuilder類三者的區別
1.1 String類的初始化兩種方式
-
字串在底層的儲存方式是char[]陣列。
-
String類是final的,沒有子類。
-
“new String”和直接賦值這兩種方式的區別
看到了segmentfault的大神@Seven_Nee的總結:
拓展:
字串的分配,和其他的物件分配一樣,耗費高昂的時間與空間代價,作為最基礎的資料型別,大量頻繁的建立字串,極大程度地影響程式的效能。
JVM為了提高效能和減少記憶體開銷,在例項化字串常量的時候進行了一些優化,為字串在方法區開闢一個字串常量池,類似於快取區。
建立字串常量時,首先檢視字串常量池是否存在該字串,如果存在該字串,返回引用例項,不存在,例項化該字串並放入池中。
常量池中儲存著String型別的字面值。
//常量池 : 先查詢常量池中是否有“OK”,沒有就先建立對應的例項物件
//直接比較常量池中的值
String x = "OK";
String y = "OK";
System.out.println(x == y);//true
//new的方式比較的是堆記憶體中的地址
String a = new String("OK");
String b = new String("OK");
System.out.println(a == b);//地址:false
//注意:
String i = "O";
String j = "K";
String k = "OK" ;
String m = i + k;
//false,一旦i+k進行字串連線,底層涉及到new
//結果得到地址,與常量池的值不相等
System.out.println(m == k);
- 無論直接賦給String字面值還是new一個新的String物件,值都會去常量池中搜索,如果常量池中搜索到這個值則返回對應的引用例項,如果沒有則建立對應的例項物件。只是因為用到“new”,後者會在堆中建立String物件。關係如下圖:
-
所以,它們兩個的區別是:同樣建立一個值為"OK"的變數,直接賦值可能建立一個或不建立物件,如果"OK"字串在常量池中不存在,會在常量池建立一個例項物件;如果已經存在,直接引用這個常量池中的物件。“new String”至少建立一個物件。起碼要在堆new一個String物件存放了“OK”,同時,若"OK"這個字串在常量池裡不存在,在常量池建立例項物件。
-
上面的“==”的意思也不同,直接賦值的意義是比較值是否相同;“new String”的含義是比較地址。
-
注意程式碼中的第三個“+”操作符的例子。
1.2 String類、StringBuffer類和StringBuilder類三者的區別
String、StringBuffer和StringBuilder都表示Java中的字串。
- 區別(面試題這麼答)
-
String和StringBuffer/StringBuilder之間的區別(StringBuffer的append方法是為了提高字串拼串的效率,就不要再在裡面寫"+"拼串了):
- String str = new String(“OK”);
//底層char陣列開闢2塊空間,如果要追加連線,底層需要再次新建char[]型別陣列,並依次賦連線的值。 雖然效率低,省記憶體。(str+=“需要連線的值”)
//底層char陣列開闢2塊空間,並附16塊緩衝區(字串的快取)。 緩衝區的作用是便於追加連線。如果需要頻繁變更字串,這個效率更高。(str.append(“需要連線的值”))
當StringBuffer的緩衝區裝完之後,需要擴容,擴容大小為(陣列大小 + 字串大小)*2,容量如果還不夠,可直接擴充到需要的容量大小。
StringBuffer buffer = new StringBuffer(“OK”);//OK+16塊緩衝區 總長18
buffer.append(“OKOK”);//(原陣列長度 + 連線的字串長度)*2,即(18 + 4) * 2 - String str = new String(“OK”);
-
StringBuffer和StringBuilder之間的區別:
StringBuffer同一時間允許一個執行緒進行訪問 ,效率較低,但是 不會 出現併發錯誤。
StringBuilder同一時間允許多個執行緒進行訪問,效率較高,但是可能 會 出現併發錯誤。
-
2 String類常用的20個方法
2.1 與長度相關的方法
返回型別 | 方法簽名 | 作用 |
---|---|---|
int | length() | 得到一個字串的字元個數。 |
- 無論是中文還是英文,只要一個退格鍵能刪除的都是一個字元。轉義字元佔一個字元,不論進行了什麼操作有什麼輸出,最後總是輸出字串定義時文字字元的長度與轉義字元的一個字元的長度的和。
例如:
String str = "ABCD\bEFGH";
System.out.println(str);//-->ABCEFGH
//雖然輸出時D被轉義字元退格,但是長度還算
System.out.println(str.length());//-->9
2.2 與陣列相關的方法
返回型別 | 方法簽名 | 作用 |
---|---|---|
byte[] | getBytes() | 將一個字串全部轉換成位元組陣列 |
char[] | toCharArray() | 將一個字串全部轉換成字元陣列 |
String[] | split(String) | 將一個字串按照指定的內容劈開 |
- getBytes()方法,若字串中含有轉義字元,算一個字元(元素)存於陣列,儲存的位元組陣列值為ascii碼。toCharArray()中相同,只是元素按char的字面值儲存。
- 常用於打碎字串並取出其中符合要求的字元。
- 如果想利用split()匹配關鍵詞來看關鍵詞在字串中出現了幾次:如果字串在開頭,這時陣列第0個會擷取到一個空串,對結果不會產生影響;但是如果這個關鍵詞出現在結尾,此時結尾不會多出一個空串,判斷時會少算一個。所以使用split來查詢關鍵詞出現幾次時, 一定用endwith判斷結尾是否為要查詢的關鍵詞,或者先在結尾追加一個字元,使其不以匹配的關鍵字結尾 。另外注意當以點號".“分割時,要寫”\\."。
2.3 與判斷相關的方法
返回型別 | 方法簽名 | 作用 |
---|---|---|
boolean | equals(String) | 區分大小寫地比較兩個字串的內容是否完全一致 |
boolean | equalsIgnoreCase(String) | 忽略大小寫地比較兩個字串的內容是否完全一致 |
boolean | contains(String) | 判斷一個字串裡面是夠出現指定內容 |
boolean | startsWith(String) | 判斷一個字串是否以指定的內容開頭 |
boolean | endsWith(String) | 判斷一個字串是否以指定的內容結尾 |
- equals()對於new String獲得的字串,比較兩個字串的內容是否相同,當比較地址時使用“==”。使用equals()時要用一定存在值的String變數呼叫這個方法,避免空指標。
2.4 與改變內容相關的方法
返回型別 | 方法簽名 | 作用 |
---|---|---|
String | toUpperCase() | 將一個字串全部轉換成大寫 |
String | toLowerCase() | 將一個字串全部轉換成小寫 |
String | replace(String,String) | 將一個字串裡面出現的某個內容全部替換成指定的內容 |
String | replaceAll(String,String) | 將一個字串裡面出現的某個內容全部替換成指定的內容(支援使用正則表示式) |
String | replaceFirst(String,String) | 將一個字串裡面第一次出現的某個內容替換成指定的內容 |
String | trim() | 將一個字串的前後空格刪除 |
String | substring(int x,int y) | 從下標x擷取到下標y-1對應的元素 |
String | substring(int x) | 從下標x擷取到字串的最後 |
- 和改變內容有關的方法,都不會直接的操作原本的字串,只是返回符合條件的字串,所以注意使用變數接收。
- 需要注意的replace的三種方法,這裡replaceFirst與replaceAll都是基於規則表示式的替換,因“\”在java中是一個轉義字元,所以需要用兩個代表一個。但是“\”也是正則表示式中的轉義字元,也需要用兩個代表一個。所以:\\被java轉換成\,\又被正則表示式轉換成\。如果要用replaceFirst和replaceAll替換“\”為"\",就要用replaceFirst/All("\\","\\\\"),而replace則replace("\","\\")。 replaceFirst與replaceAll的區別顯而易見,前者只替換第一次出現的符合第一個引數中的值,第二個替換所有的。這一點replaceAll與replace作用相同。另外replace還支援字元的替換。
2.5 與位置相關的方法
返回型別 | 方法簽名 | 作用 |
---|---|---|
char | charAt(int x) | 找到指定下標對應的元素 |
int | indexOf(String) | 找到指定內容第一次出現的下標 |
int | lastIndexOf(String) | 找到指定內容最後一次出現的下標 |
- 這裡的第幾個,指的是陣列中的序號。找第n個元素,傳入引數n-1;第二、三個方法返回n,則字元位於字串中的第n+1個。
2.6 綜合應用例題
//1 定義一個方法 判斷傳進去的內容是不是純數字
//如果是 return true
//如果不是 return false
public static boolean check(String str){
//方法1
if(str.replaceAll("[0-9]","").length() == 0){
return true;
}else{
return false;
}
//方法2
return str.replaceAll("[0-9]","").length() == 0;
//方法3
char[] data = str.toCharArray();
int count = 0;
for(char x : data){
if('0' <= x && x <= '9'){
count++;
}
}
if(count == str.length()){
return true;
}else{
return false;
}
}
//2 按照首字母從小到大排序
//Animal Bird Cat Dog
public class Test2{
public static void main(String[] args){
String[] data = {"Cat","Animal","Bird","Dog"};
for(int x = 0; x < data.length -1; x++){
for(int y = 0; y < data.length -1 -x; y++){
//獲取前一個元素的首字母
char a = data[y].charAt(0);
//獲取後一個元素的首字母
char b = data[y + 1].charAt(0);
if(a > b){
String z = data[y];
data[y] = data[y + 1];
data[y + 1] = z;
}
}
}
for(String x : data){
System.out.println(x);//Animal Bird Cat Dog(換行輸出)
}
}
}
//3 根據輸入的身份證號判斷性別
public class Test3{
public static void main(String[] args){
String id = "341101199808062065";
char gender = str.charAt(16);
//if("02468".contains(gender)){
if(gender % 2 == 0){//0-9與對應的ascii碼奇偶相同,也可以用ascii碼模運算
System.out.println("female");
}else{
System.out.println("male");
}
}
}
//4 擷取字串中地點、最低和最高氣溫並輸出
//濟南今天最低氣溫:2
//濟南今天最高氣溫:18
public class Test4{
public static void main(String[] args){
String str = "濟南今天氣溫:2℃ - 18℃";
//使用:indexOf/lastIndexOf + substring擷取字串
//擷取地方名:
String cityName = str.substring(0,str.indexOf("今"));
//擷取最低溫度
String min = str.substring(str.indexOf(":") + 1,str.indexOf("℃"));
//擷取最高溫度
String max = str.substring(str.lastIndexOf(" ") + 1,str.lastIndexOf("℃"));
System.out.println(cityName + "今天最低氣溫:" + min);
System.out.println(cityName + "今天最高氣溫:" + max);
}
}
//5 使用至少4種方式計算葡萄出現了幾次
public class Test5{
public static void main(String args[]){
//方法1
String str = "吃葡萄不吐葡萄皮,不吃葡萄倒吐葡萄皮。";
String[] data = str.split("葡萄");
System.out.println("次數為:" + (data.length - 1));
//方法2
String str = "吃葡萄不吐葡萄皮,不吃葡萄倒吐葡萄皮。";
char[] strCharArray = str.toCharArray();
int count = 0;
//如果條件不減一,最後一個字是葡的情況下會越界。如果不出現葡,if中的&&會有短路效果,不判斷x+1所以不會出現越界現象
for(int x = 0; x < strCharArray.length - 1; x++){
if(strCharArray[x] == '葡' && strCharArray[x + 1] == '萄') count++;
}
System.out.println("次數為:" + count);
//方法3
String str = "吃葡萄不吐葡萄皮,不吃葡萄倒吐葡萄皮。";
String strReplace = str.replaceAll("葡萄", "");
System.out.println("次數為:" + ((str.length() - strReplace.length()) / 2));
//方法4
String str = "吃葡萄不吐葡萄皮,不吃葡萄倒吐葡萄皮。";
int count = 0;
String twoWords;
while(str.contains("葡萄")){
int x = str.indexOf("葡萄");
str = str.substring(x + 2);
count++;
}
System.out.println("次數為:" + count);
//方法5
String str = "吃葡萄不吐葡萄皮,不吃葡萄倒吐葡萄皮。";
int count = 0;
while(str.contains("葡萄")){
str = str.replaceFirst("葡萄", "核桃");
count++;
}
System.out.println("次數為:" + count);
}
}
//6 只輸出第一次出現的字母
public class Test6{
public static void main(String args[]){
String str = "aaaaaa";
char[] strArray = str.toCharArray();
//方法1
String strNew = "";
for(char x : strArray){
if(!strNew.contains(x + "")){
strNew += x;
}
}
System.out.println(strNew);//--->a
//方法2
while(str.length() != 0){
//char x = str.charAt(0);
String x = str.substring(0, 1);
System.out.print(x);
//刪除這個值
str = str.replace(x, "");
}
System.out.println(str);//--->a
//方法3
String newStr = "";
for(int i = 0; i < strArray.length; i++){
newStr += strArray[i];
for(int j = i+1; j < strArray.length; j++){
if(strArray[i] == strArray[j]) {
str = str.replace(strArray[i] + "", "");
//去重之後重新整理陣列
strArray = str.toCharArray();
}
}
}
System.out.println(newStr);//--->輸出a
}
}
//7 將第一個非重複元素打印出來
public class Test7{
public static void main(String[] args){
String str = "swwiss";
char[] strArray = str.toCharArray();
//初始化一個字元型別
char first = 0;
for(char x : strArray){
String strNew = str.replace(x + "", "");
if((str.length() - strNew.length())== 1){
first = x;
break;
}
}
System.out.println(first);//--->i
}
}
//8 統計兩個字串裡面重複的元素有多少個
public class Test8{
public static void main(String[] args){
String str1 = "ok";
String str2 = "hello";
char[] str1Array = str1.toCharArray();
char[] str2Array = str2.toCharArray();
int count = 0;
for(int i = 0; i < str1Array.length; i++){
for(int j = i; j < str2Array.length; j++){
if(str1Array[i] == str2Array[j]){
count++;
}
}
}
System.out.println(count);//--->1
}
}
//9 對當前的字串進行貨幣價值的格式化,例如輸入1234567
//輸出1,234,567
public class Test9{
public static void main(String[] args){
String str = "000123456776543210";
String newStr = "";
//如果左端有0,除掉
char[] strArray = str.toCharArray();
int j = 0;
while(strArray[j] == '0'){
str = str.replaceFirst(strArray[j] + "", "");
j++;
}
//將處理後的str先留存,在while中判斷是否應該首先加“,”
String strBefore = str;
//對最左端先進行格式化,將剩下的數字變為三的倍數便於迴圈
if(str.length() % 3 != 0){
//將左端數值開始放入新的字串
newStr += str.substring(0, str.length());
//原字串縮短
str = str.replaceFirst(str.substring(0, str.length()), "");
}
while(str.length() > 0){
//如果迴圈前恰好是三的倍數,第一位不加“,”,其餘位數每三位加一個
if(str.length() != strBefore.length()) newStr += ",";
//左端繼續放入新陣列,每三位一放
newStr += str.substring(0, 3);
//原字串持續縮短,每三位一短
str = str.replaceFirst(str.substring(0, 3), "");
}
//列印轉化好的新陣