初始Java篇(JavaSE基础语法)(8)认识String类(下)

CSDN 2024-07-07 11:05:02 阅读 70

找往期文章包括但不限于本期文章中不懂的知识点:

个人主页:我要学编程(ಥ_ಥ)-CSDN博客

所属专栏:JavaSE

接上文 初始Java篇(JavaSE基础语法)(8)认识String类(上)-CSDN博客

目录

字符串截取

其他操作方法

字符串的不可变性 

字符串修改

StringBuilder和StringBuffer

刷题练习

387.字符串中第一个唯一字符 

HJ1 字符串最后一个单词的长度 

 125.验证回文串


字符串截取

从一个完整的字符串之中截取出部分内容。可用方法如下:

方法 功能
String substring(int beginIndex) 从指定索引的位置截取到结尾
String substring(int beginIndex, int endIndex) 截取部分内容(从beginIndex位置到endIndex位置)

注意:

1. 索引从0下标开始。

2. 注意前闭后开区间的写法。substring(0, 5) 表示从0下标开始截取,一直到4下标,即[0,5) 。

示例: 

其他操作方法

方法 功能
String trim() 去掉字符串中的左右空格,保留中间空格

示例:

字符串的不可变性 

String是一种不可变对象。字符串中的内容是不可改变。字符串不可被修改。下面就是String的源码:

这里可能会有小伙伴会说是因为value数组被 final 修饰了,因此这个数组的内容就不可变了,也就是说这个数组里面存放的字符就不可修改了。

其实不然,这个final修饰的数组,只是让这个数组名,也就是数组对象的引用不能被修改了,并不是说这个数组的内容不能被修改了。

例如:

final修饰引用类型表明该引用变量不能去引用其他对象了,但是其引用对象中的内容是可以修改的。 

那既然如此,是什么不能让我们修改String的内容呢?其实是 private 修饰的整个数组。如果我们想要修改这个数组内容首先得拿到这个数组吧,但是 private 修饰就直接导致我们拿不到这个数组。因此我们就根本没有机会去修改这个数组。这也就是 String 不可修改的原因。

那可能小伙伴又有疑惑了:既然不能修改String,那前面我们学习的拆分字符串,字符串大小写转换……这些不都改变了字符串本身吗?这只是我们看到的表面现象。所有涉及到可能修改字符串内容的操作都是创建一个新对象,改变的是新对象 。这样我们每一次涉及修改字符串的操作都是创建一个新的对象。

为什么 String 要设计成不可变的?(不可变对象的好处是什么?) (目前简单了解)

1. 方便实现字符串对象池. 如果 String 可变, 那么对象池就需要考虑写时拷贝的问题了。

2. 不可变对象是线程安全的。 

3. 不可变对象更方便缓存 hash code, 作为 key 时可以更高效的保存到 HashMap 中. 那如果想要修改字符串中内容,该如何操作呢?

字符串修改

注意:尽量避免直接对String类型对象进行修改,因为String类是不能修改的,所有的修改都会创建新对象,效率非常低下。例如:

public class Test {

public static void main(String[] args) {

String s = "hello";

System.out.println(s);

s += " world";

System.out.println(s);

}

}

那么怎样才能使效率变得更高呢?

借助StringBuffer 和 StringBuilder。下面就来介绍一下。

StringBuilder和StringBuffer

由于String的不可更改特性,为了方便字符串的修改,Java中又提供StringBuilder和StringBuffer类。这两个类大部分功能是相同的。

看到这里,可能有小伙伴要问了:String不是不可修改吗?怎么又说为了方便修改提供了这两个类呢?这不就前后矛盾了吗?

不不不,并不矛盾。正是因为String不可修改,所以当我们想要修改String时,做不到。那么java开发人员也想到了这种情况,因此就设计出了StringBuffer和StringBuilder这两个类,来让我们想要修改时,可以做到。做法是通过StringBuffer和StringBuilder,修改传入的字符串,再把修改后的结果转换为字符串。这就是字符串的修改方式。

这里介绍 StringBuffer常用的一些方法。

StringBuff append(String str)  在尾部追加,相当于String的+=,可以追加:boolean、char、char[]、 double、float、int、long、Object、String、StringBuff的变量。

public class Test {

public static void main(String[] args) {

StringBuffer sb = new StringBuffer("Hello");

sb.append(" world");

System.out.println(sb);

}

}

char charAt(int index) 获取index位置的字符。

public class Test {

public static void main(String[] args) {

StringBuffer sb = new StringBuffer("Hello");

System.out.println(sb.charAt(0));

}

}

int length() 获取字符串的长度 。

public class Test {

public static void main(String[] args) {

StringBuffer sb = new StringBuffer("Hello");

System.out.println(sb.length());

}

}

int capacity()    获取底层保存字符串空间总的大小 。 

这里的底层是指用 c/c++ 保存时的大小。不必关心,

void ensureCapacity(int mininmumCapacity)     扩容。

public class Test {

public static void main(String[] args) {

StringBuffer sb = new StringBuffer("Hello");

// 把空间扩充到原来的两倍

sb.ensureCapacity(sb.length()*2);

}

}

void setCharAt(int index, char ch)   将index位置的字符设置为ch 。

public class Test {

public static void main(String[] args) {

StringBuffer sb = new StringBuffer("Hello");

sb.setCharAt(4, 'O');

System.out.println(sb);

}

}

 int indexOf(String str)    返回str第一次出现的位置。

public class Test {

public static void main(String[] args) {

StringBuffer sb = new StringBuffer("Hello");

// 这里要是字符串

System.out.println(sb.indexOf("o"));

}

}

int indexOf(String str, int fromIndex)      从fromIndex位置开始查找str第一次出现的位置 。

public class Test {

public static void main(String[] args) {

StringBuffer sb = new StringBuffer("Hello");

System.out.println(sb.indexOf("o", 2));

}

}

StringBuff insert(int offset, String str)         在offset位置之前插入:八种基类类型 & String类型 & Object类型数据 。

public class Test {

public static void main(String[] args) {

StringBuffer sb = new StringBuffer("Hello");

// 在4位置之前,插入world

System.out.println(sb.insert(4, " world"));

}

}

StringBuffer deleteCharAt(int index)     删除index位置字符。

public class Test {

public static void main(String[] args) {

StringBuffer sb = new StringBuffer("Hello");

System.out.println(sb.deleteCharAt(0));

}

}

StringBuffer delete(int start, int end)     删除[start, end)区间内的字符。

public class Test {

public static void main(String[] args) {

StringBuffer sb = new StringBuffer("Hello");

// 删除的范围是[0,4)

System.out.println(sb.delete(0, 4));

}

}

StringBuffer replace(int start, int end, String str)    将[start, end)位置的字符替换为str。

public class Test {

public static void main(String[] args) {

StringBuffer sb = new StringBuffer("Hello");

// 把[0,4)内的字符串内容替换成空,相当于删除操作

System.out.println(sb.replace(0, 4, ""));

}

}

String substring(int start)          从start开始一直到末尾的字符以String的方式返回。

public class Test {

public static void main(String[] args) {

StringBuffer sb = new StringBuffer("Hello");

System.out.println(sb.substring(0));

}

}

String substring(int start,int end)                 将[start, end)范围内的字符以String的方式返回。

public class Test {

public static void main(String[] args) {

StringBuffer sb = new StringBuffer("Hello");

// 返回的是[0,4)之间的字符串

System.out.println(sb.substring(0, 4));

}

}

StringBuffer reverse()         反转字符串。

public class Test {

public static void main(String[] args) {

StringBuffer sb = new StringBuffer("Hello");

System.out.println(sb.reverse());

}

}

String toString()                 将所有字符按照String的方式返回。 

public class Test {

public static void main(String[] args) {

StringBuffer sb = new StringBuffer("Hello");

System.out.println(sb.toString());

}

}

StringBuilder 的用法也和上面的类似。

StringBuffer 、StringBuilder 和String的区别:显而易见,前面两个是可以直接修改的,而String不行,只能通过创建出新的对象。而这个修改可变不可变主要是看是否可以在原对象上进行修改。其次,两者的方法也有些差异。

刷题练习

387.字符串中第一个唯一字符 

给定一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1 。

示例 1:

输入: s = "leetcode"

输出: 0

示例 2:

输入: s = "loveleetcode"

输出: 2

示例 3:

输入: s = "aabb"

输出: -1

提示:

1 <= s.length <= 105s 只包含小写字母

 

思路一:直接遍历取出字符串下标对应的字符,再拿这个字符去遍历整个字符串,遇到相等的字符,计数器就加1,当走完一轮之后,就开始看看计数器是否为1,如果为1,那就说明只有自己一个想同,符合,返回这个下标就行了,如果不为1,就说明还有除了自身之外,还有与其相等字符,就继续遍历,直至遍历完成。如果还没有找到就返回-1。

代码:

class Solution {

public int firstUniqChar(String s) {

for (int i = 0; i < s.length(); i++) {

char c = s.charAt(i);

int count = 0;

for (int j = 0; j < s.length(); j++) {

if (c == s.charAt(j)) {

count++;

}

}

if (count == 1) {

return i;

}

}

return -1;

}

}

这个方法看似可行,实际上耗费的时间非常多。当唯一的字符一个都没有时,它就要遍历n*n次(n为字符串的长度) ,而如果给出的字符串长度非常长,那么这个就肯定会超时。

思路二:既然是找这个出现唯一一次的字符,那么我们就可以采用计数数组来计数。当这个字符出现时,对应的下标就加1,遍历完这个字符串后,就开始第二次遍历这个数组,其中下标对应的数组值为1,也就是这个字符只出现了一次,符合要求,直接返回就行,如果没找到就返回-1。这个方法最坏的情况就是:遍历了完完整整的两次字符串,也就是2*n。

注意:第二次去遍历这个数组时,不能按照下标的顺序来遍历,得按照字符串中每个字符的顺序来遍历。

代码:

<code>class Solution {

public int firstUniqChar(String s) {

// 根据提示可知只会出现26个字母

int[] count = new int[26];

for (int i = 0; i < s.length(); i++) {

char c = s.charAt(i);

count[c-97]++; // c-'a'也是可以的,很方便我们写代码

}

for (int i = 0; i < s.length(); i++) {

char c = s.charAt(i);

if (count[c-97] == 1) {

return i;

}

}

return -1;

}

}

HJ1 字符串最后一个单词的长度 

描述

计算字符串最后一个单词的长度,单词以空格隔开,字符串长度小于5000。(注:字符串末尾不以空格为结尾)

输入描述:

输入一行,代表要计算的字符串,非空,长度小于5000。

输出描述:

输出一个整数,表示输入字符串最后一个单词的长度。

示例1

输入:

hello nowcoder

输出:

8

说明:

最后一个单词为nowcoder,长度为8

思路一: 这个题目就是在于确定其最后一个单词的位置。我们可以通过最后一个空格来来确定。确定最后一个字符串的位置之后,就可以把这个字符串从那个位置开始截取,最后再求其长度即可。

代码:

import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息

public class Main {

public static void main(String[] args) {

Scanner in = new Scanner(System.in);

String str = in.nextLine();

// 找到字符串中最后一个空格的位置

int x = str.lastIndexOf(" ");

// 从空格的后一个位置开始截取原字符串

str = str.substring(x+1);

// 直接输出该字符串的长度

System.out.println(str.length());

}

}

思路二:既然要找最后一个单词,那我就直接把最后一个单词放到最前面来就行了。直接把原字符串逆序,再遍历找到空格位置即可,中间的长度就是最后一个单词的长度。

但是有小伙伴可能要问了:JavaAPI中没有提供逆置字符串的方法呀,难道让我自己实现一个吗?

当然不是,java虽然没有为String提供,但是为StringBuffer提供了,并且我们刚刚也学过了。 

先把String转换为StringBuffer,再用reverse方法逆置(最后再把StringBuffer转换为String即可,可以不用转换,直接求)。 

import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息

public class Main {

public static void main(String[] args) {

Scanner in = new Scanner(System.in);

String str = in.nextLine();

StringBuffer sb = new StringBuffer(str);

StringBuffer sb2 = sb.reverse();

int count = 0;

for (int i = 0; i<sb2.length(); i++) {

if (sb2.charAt(i) == ' ') {

break;

}

count++;

}

System.out.println(count);

}

}

 125.验证回文串

如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。

字母和数字都属于字母数字字符。

给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false 

示例 1:

输入: s = "A man, a plan, a canal: Panama"

输出:true

解释:"amanaplanacanalpanama" 是回文串。

示例 2:

输入:s = "race a car"

输出:false

解释:"raceacar" 不是回文串。

示例 3:

输入:s = " "

输出:true

解释:在移除非字母数字字符之后,s 是一个空字符串 "" 。

由于空字符串正着反着读都一样,所以是回文串。

思路: 首先,用库方法,将大写字母转换成小写字母。接着遍历来比较看二者是否一致,如果不一致就直接返回;如果一致的话,就继续直至遍历两者的下标相遇即可,返回true。

代码:

class Solution {

public boolean isPalindrome(String s) {

s = s.toLowerCase(); // 大写转小写

int i = 0;

int j = s.length()-1;

while(i < j) {

// 找到数字字母字符

while (i < j && !isNumberCharacter(s.charAt(i))) {

i++;

}

// 找到数字字母字符

while (i < j && !isNumberCharacter(s.charAt(j))) {

j--;

}

if (s.charAt(i) != s.charAt(j)) {

return false;

}

i++;

j--;

}

return true;

}

public boolean isNumberCharacter(char c) {

if (c >= '0' && c <= '9' || c >= 'a' && c <= 'z') {

return true;

}

return false;

}

}

 好啦!本期 初始Java篇(JavaSE基础语法)(8)认识String类(下)的学习之旅就到此结束了!相信通过这两篇文章的学习,你对Java中String类的了解将会更进一步!我们下一期再一起学习吧!



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。