Javase.String 类

String 类

  • 【本节目标】
  • 1. String类的重要性
  • 2. 常用方法
    • 2.1 字符串构造
    • 2.2 String对象的比较
    • 2.3 字符串查找
    • 2.4 转化
    • 2.5 字符串替换
    • 2.7 字符串截取
    • 2.8 其他操作方法
    • 2.9 字符串的不可变性
    • 2.10 字符串修改
  • 3. StringBuilder和StringBuffer
    • 3.2 面试题:
  • 4. String类oj
    • 4.1第一个只出现一次的字符
    • 4.2最后一个单词的长度
    • 4.3检测字符串是否为回文

【本节目标】

  1. 认识String类
  2. 了解String类的基本用法
  3. 熟练掌握String类的常见操作
  4. 认识StringBuffer和StringBuilder

1. String类的重要性

在C语言中已经涉及到字符串了,但是在C语言中要表示字符串只能使用字符数组或者字符指针,可以使用标准库提供的字符串系列函数完成大部分操作,但是这种将数据和操作数据方法分离开的方式不符合面相对象的思想,而字符串应用又非常广泛,因此Java语言专门提供了String类。

2. 常用方法

2.1 字符串构造

String类提供的构造方式非常多,常用的就以下三种:

public static void main(String[] args) {
    //使用常量串构造
    String s1 = "hello world";
    System.out.println(s1);

    //直接new String 对象
    String s2 = new String("hello world");
    System.out.println(s2);

    //使用字符数组进行构造
    char[] array = {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'};
    String s3 = new String(array);
    System.out.println(s3);
}

其他方法需要用到时,大家参考Java在线文档:String文档

【注意】

  1. String是引用类型,内部并不存储字符串本身,在String类的实现源码中,String类实例变量如下:
    在这里插入图片描述
public static void main(String[] args) {
    //s1和s2引用的不是同一对象
    String s1 = new String("hello");
    String s2 = new String("world");
    //s1和s3引用的是同一对象
    String s3 = s1;

    System.out.println(s1.length());//获取字符串长度 -> 输出5
    System.out.println(s1.isEmpty());//如果字符串长度为0,返回true,否则返回false
}

在这里插入图片描述

  1. 在Java中“”引起来的也是String类型对象。
public static void main(String[] args) {
    //打印"hello"字符串(String对象)的长度
    System.out.println("hello".length());
}

2.2 String对象的比较

字符串的比较是常见操作之一,比如:字符串排序。Java中总共提供了4中方式:

  1. ==比较是否引用同一个对象
    注意:对于内置类型,比较的是变量中的值;对于引用类型比较的是引用中的地址。
public class Test {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        int c = 10;

        //对于基本数据类型,==比较的是两个变量中存储的值是否相同
        System.out.println(a == b);//false
        System.out.println(a == c);//true

        //对于引用类型变量,==比较两个引用变量引用的时候为同一对象
        String s1 = new String("hello");
        String s2 = new String("hello");
        String s3 = new String("world");
        String s4 = s1;

        System.out.println(s1 == s2);//false
        System.out.println(s2 == s3);//false
        System.out.println(s1 == s4);//true
    }
}
  1. boolean equals(Object anObject) 方法:按照字典序比较

字典序:字符大小的顺序

String类重写了父类Object中equals方法,Object中equals默认按照==比较,String重写equals方法后,按照如下规则进行比较,比如: s1.equals(s2)

//库方法
public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}
//Test.java
public static void main(String[] args) {
    String s1 = new String("hello");
    String s2 = new String("hello");
    String s3 = new String("Hello");

    //s1、s2、s3引用的是三个不同的对象,因此==比较的结果全部是false
    System.out.println(s1 == s2);//false
    System.out.println(s1 == s3);//false

    /**
     * equals比较:String对象中逐个字符
     * 虽然s1和s2引用的不是同一个对象,但是两个对象中放置的内容相同,因此输出的是true
     * s1和s3引用的不是同一个对象,而且两个对象中的内容也不同,因此输出false
     */
    System.out.println(s1.equals(s2));//true
    System.out.println(s1.equals(s3));//false
}
  1. int compareTo(String s) 方法: 按照字典序进行比较

与equals不同的是,equals返回的是boolean类型,而compareTo返回的是int类型,具体比较方式:

  • 先按照字典序大小比较,如果出现不等的字符,直接返回两个字符大小的差值
  • 如果前k个字符相等(k为两个字符长度最小值),返回值两个字符串长度差值
public class Test {
    public static void main(String[] args) {
        String s1 = new String("abc");
        String s2 = new String("ac");
        String s3 = new String("abc");
        String s4 = new String("abcs");

        System.out.println(s1.compareTo(s2));//不同输出字符值为-1
        System.out.println(s1.compareTo(s3));//相同输出0
        System.out.println(s1.compareTo(s4));//前k个字符完全相同,输出长度差值-1
    }
}
  1. int compareToIgnoreCase(String str) 方法:与compareTo方式相同,但是忽略大小写比较
public class Test {
    public static void main(String[] args) {
        String s1 = new String("abc");
        String s2 = new String("ac");
        String s3 = new String("ABc");//忽略大小写 -> abc
        String s4 = new String("abcs");

        System.out.println(s1.compareToIgnoreCase(s2));//不同输出字符差值为-1
        System.out.println(s1.compareToIgnoreCase(s3));//相同输出0
        System.out.println(s1.compareToIgnoreCase(s4));//前k个字符完全相同,输出长度差值-1
    }
}

2.3 字符串查找

字符串查找也是字符串中非常常见的操作,String类提供的常用查找的方法:

方法功能
char charAt(int index)返回index位置上字符,如果index为负数或者越界,抛出IndexOutOfBoundsException异常
int indexOf(int ch)返回ch第一次出现的位置,没有返回-1
int indexOf(int ch, int fromIndex)从fromIndex位置开始找ch第一次出现的位置,没有返回-1
int indexOf(String str)返回str第一次出现的位置,没有返回-1
int indexOf(String str, int fromIndex)从fromIndex位置开始找str第一次出现的位置,没有返回-1
int lastIndexOf(int ch)从后往前找,返回ch第一次出现的位置,没有返回-1
int lastIndexOf(int ch, int fromIndex)从fromIndex位置开始找,从后往前找ch第一次出现的位置,没有返回-1
int lastIndexOf(String str)从后往前找,返回str第一次出现的位置,没有返回-1
int lastIndexOf(String str, intfromIndex)从fromIndex位置开始找,从后往前找str第一次出现的位置,没有返回-1
public class Test {
    public static void main(String[] args) {
        String s = "ababcabcd";

        //char charAt(int index):
        System.out.println(s.charAt(3));//b

        //int indexOf(int ch):
        System.out.println(s.indexOf('c'));//4
        //int indexOf(int ch, int fromIndex):
        System.out.println(s.indexOf('c', 3));//4
        //int indexOf(String str):
        System.out.println(s.indexOf("abc"));//2
        //int indexOf(String str, int fromIndex):
        System.out.println(s.indexOf("abc", 3));//5

        //int lastIndexOf(int ch):
        System.out.println(s.lastIndexOf('c'));//7
        //int lastIndexOf(int ch, int fromIndex):
        System.out.println(s.lastIndexOf('c', 3));//-1
        //int lastIndexOf(String str):
        System.out.println(s.lastIndexOf("abc"));//5
        //int lastIndexOf(String str, int fromIndex):
        System.out.println(s.lastIndexOf("abc", 3));//2

    }
}

注意:上述方法都是实例方法。

2.4 转化

  1. 数值和字符串转化
//Test.java
class Student {
    public String name;
    public int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
//Test.java
public class Test {
    public static void main(String[] args) {
        //1.数字转字符串
        String s1 = String.valueOf(1234);
        String s2 = String.valueOf(12.34);
        String s3 = String.valueOf(true);
        String s4 = String.valueOf(new Student("张三", 19));

        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s3);
        System.out.println(s4);

        System.out.println("===========================");

        //2. 字符串转数字
        //注意:Integer、Double等是Java中的包装类,后面会详细介绍
        int data1 = Integer.parseInt("1234");
        double data2 = Double.parseDouble("12.34");

        System.out.println(data1);
        System.out.println(data2);
        System.out.println(data1 + data2);
    }
}
  1. 大小写转换
public class Test {
    public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "HELLO";

        //1. 小写转大写
        System.out.println(s1.toUpperCase());
        //2. 大写转小写
        System.out.println(s2.toLowerCase());
    }
}

/*
执行结果:
HELLO
hello
 */
  1. 字符串转数组
public class Test {
    public static void main(String[] args) {
        String s = "hello";
        //1. 字符串转数组
        char[] ch = s.toCharArray();//{'h', 'e', 'l', 'l', 'o'};
        for (int i = 0; i < ch.length; i++) {
            System.out.print(ch[i] + " ");
        }
        System.out.println();
        //2. 数组转字符串
        String s2 = new String(ch);
        System.out.println(s2);
    }
}

/*
执行结果:
h e l l o
hello
 */
  1. 格式化
public class Test {
    public static void main(String[] args) {
        String s = String.format("%d-%d-%d", 2024, 6, 20);
        System.out.println(s);
    }
}

/*
执行结果:
2024-6-20
 */

2.5 字符串替换

使用一个指定的新的字符串替换掉已有的字符串数据,可用的方法如下:

方法功能
String replaceAll(String regex, String replacement)替换所有的指定内容
String replaceFirst(String regex, String replacement)替换收个内容
  • 代码示例: 字符串的替换处理
//Test.java
public static void main(String[] args) {
    String str = "hello world hello mom";
    String[] ret = str.split(" ");//按照空格拆分
    for(String s : ret) {
        System.out.println(s);
    }
}

/*
1.执行结果:
hello
world
hello
mom
*/
  • 代码示例: 字符串的部分拆分
public static void main(String[] args) {
    String str = "hello world hello mom";
    String[] ret = str.split(" ", 2);
    for(String s : ret) {
        System.out.println(s);
    }
}

/*2.执行结果:
hello
world hello mom
*/

拆分是特别常用的操作. 一定要重点掌握. 另外有些特殊字符作为分割符可能无法正确切分, 需要加上转义

  • 代码示例: 拆分IP地址
public static void main(String[] args) {
    String str = "192.168.1.1" ;
    String[] ret = str.split("\\.") ;
    for(String s : ret) {
        System.out.println(s);
    }
}

/*
3.执行结果:
192
168
1
1
*/
注意事项:
1. 字符"|"、"*"、"+"、"."都得加上转义字符,前面加上"\\"。
2. 而如果是"\",那么就得写成"\\\\"。
3. 如果一个字符串中有多个分隔符,可以用"|"作为连字符。
  • 代码示例: 多次拆分
public static void main(String[] args) {
    String str = "name=zhangsan&age=18" ;
    String[] ret = str.split("&") ;
    for (int i = 0; i < ret.length; i++) {
        String[] temp = ret[i].split("=") ;
        System.out.println(temp[0]+" = "+temp[1]);
    }
}

/*
4.执行结果:
name = zhangsan
age = 18
*/

这种代码在以后的开发之中会经常出现

2.7 字符串截取

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

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

代码示例: 观察字符串截取

public class Test {
    public static void main(String[] args) {
        String str = "helloworld";
         System.out.println(str);
        //String substring(int beginIndex):
        System.out.println(str.substring(3));
        //String substring(int beginIndex, int endIndex):
        System.out.println(str.substring(0, 5));//[0,5)
    }
}

/*
执行结果:
helloworld
loworld
hello
 */

注意事项:

  1. 索引从0开始
  2. 注意前闭 后开区间的写法,substring(0, 5) 表示包含 0 号下标的字符, 不包含 5 号下标

2.8 其他操作方法

方法功能
String trim()去掉字符串中的左右空格,保留中间空格
String toUpperCase()字符串转大写
String toLowerCase()字符串转小写
  • 代码示例: 观察trim()方法的使用
public static void main(String[] args) {
    String str = " hello world  ";
    System.out.println("[" + str + "]");
    System.out.println("[" + str.trim() + "]");
}

/*
1.执行结果:
[ hello world  ]
[hello world]
 */

trim 会去掉字符串开头和结尾的空白字符(空格, 换行, 制表符等)。

  • 代码示例: 大小写转换
public static void main(String[] args) {
    String str = " hello%$$%@#$%WORLD 哈哈哈 " ;
    System.out.println(str.toUpperCase());
    System.out.println(str.toLowerCase());
}

/*
2.执行结果:
 HELLO%$$%@#$%WORLD 哈哈哈
 hello%$$%@#$%world 哈哈哈
 */

这两个函数只转换字母。

2.9 字符串的不可变性

String是一种不可变对象. 字符串中的内容是不可改变。字符串不可被修改,是因为:

  1. String类在设计时就是不可改变的,String类实现描述中已经说明了
    以下来自JDK1.8中String类的部分实现:
    在这里插入图片描述
    在这里插入图片描述
    String类中的字符实际保存在内部维护的value字符数组中,该图还可以看出:

    • String类被final修饰,表明该类不能被继承
    • value被修饰被final修饰,表明value自身的值不能改变,即不能引用其它字符数组,但是其引用空间中的内容可以修改。
  2. 所有涉及到可能修改字符串内容的操作都是创建一个新对象,改变的是新对象
    比如 replace 方法:

在这里插入图片描述
【纠正】 网上有些人说:字符串不可变是因为其内部保存字符的数组被final修饰了,因此不能改变。

这种说法是错误的,不是因为String类自身,或者其内部value被final修饰而不能被修改。

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

public class Test {
    public static void main1(String[] args) {
        String str = "abc";
        System.out.println(str.replace("a", "qq"));
    }

    public static void main(String[] args) {
        final int[] array = {1, 2, 3, 4, 5};
        array[0] = 100;
        System.out.println(Arrays.toString(array));

        //编译报错:java: 无法为最终变量array分配值
        //array = new int[]{6, 7, 8};
    }
}

为什么 String 要设计成不可变的?(不可变对象的好处是什么?)

  1. 方便实现字符串对象池. 如果 String 可变, 那么对象池就需要考虑写时拷贝的问题了
  2. 不可变对象是线程安全的
  3. 不可变对象更方便缓存 hash code, 作为 key 时可以更高效的保存到 HashMap 中

2.10 字符串修改

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

public static void main1(String[] args) {
    String s = "hello";
    s += " world";
    System.out.println(s);//hello world
}

但是这种方式不推荐使用,因为其效率非常低,中间创建了好多临时对象。

在这里插入图片描述

public class Test {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();//毫秒
        String s = "";
        for(int i = 0; i < 10000; ++i){
            s += i;
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);//248


        start = System.currentTimeMillis();
        StringBuffer sbf = new StringBuffer("");
        for(int i = 0; i < 10000; ++i){
            sbf.append(i);
        }
        end = System.currentTimeMillis();
        System.out.println(end - start);//1


        start = System.currentTimeMillis();
        StringBuilder sbd = new StringBuilder();
        for(int i = 0; i < 10000; ++i){
            sbd.append(i);
        }
        end = System.currentTimeMillis();
        System.out.println(end - start);//0
    }
}

/*
执行结果:
248
1
0
*/

可以看待在对String类进行修改时,效率是非常慢的,因此:尽量避免对String的直接需要,如果要修改建议尽量使用StringBuffer或者StringBuilder。

借助StringBuffer 和 StringBuilder

3. StringBuilder和StringBuffer

由于String的不可更改特性,为了方便字符串的修改,Java中又提供StringBuilder和StringBuffer类。这两个类大部分功能是相同的,这里介绍 StringBuilder常用的一些方法,其它需要用到了大家可参阅StringBuilder官方文档

方法功能
StringBuff append(String str)在尾部追加,相当于String的+=,可以追加:boolean、char、char[]、double、float、int、long、Object、String、StringBuff的变量
char charAt(int index)获取index位置的字符
int length()获取字符串的长度
int capacity()获取底层保存字符串空间总的大小
void ensureCapacity(int mininmumCapacity)扩容
void setCharAt(int index, char ch)将index位置的字符设置为ch
int indexOf(String str)返回str第一次出现的位置
int indexOf(String str, int fromIndex)从fromIndex位置开始查找str第一次出现的位置
int lastIndexOf(String str)返回最后一次出现str的位置
int lastIndexOf(String str, int fromIndex)从fromIndex位置开始找str最后一次出现的位置
StringBuff insert(int offset, String str)在offset位置插入:八种基类类型 & String类型 & Object类型数据
StringBuffer deleteCharAt(int index)删除index位置字符
StringBuffer delete(int start, int end)删除[start, end)区间内的字符
StringBuffer replace(int start, int end, String str)将[start, end)位置的字符替换为str
String substring(int start)从start开始一直到末尾的字符以String的方式返回
String substring(int start,int end)将[start, end)范围内的字符以String的方式返回
StringBuffer reverse()反转字符串
String toString()将所有字符按照String的方式返回
public static void main(String[] args) {
    StringBuilder sb1 = new StringBuilder("hello");
    StringBuilder sb2 = sb1;

    //追加:即尾插 ->字符、字符串、整型数字
    sb1.append(' ');//hello
    sb1.append("world");//hello world
    sb1.append(123);//hello world123
    System.out.println(sb1);//hello world123
    System.out.println(sb1 == sb2);//true
    System.out.println("===========================");


    System.out.println(sb1.charAt(0));//获取0号位上的字符 h
    System.out.println(sb1.length());//获取字符串的有效长度14
    System.out.println(sb1.capacity());//获取底层数组的总大小
    System.out.println("===========================");

    sb1.setCharAt(0, 'H');//设置任意位置的字符 Hello world123
    sb1.insert(0, "Hello world!!!");//Hello world!!!Hello world123
    System.out.println(sb1);
    System.out.println(sb1.indexOf("Hello"));//获取Hello第一次出现的位置
    System.out.println(sb1.lastIndexOf("hello"));//获取hello最后一次出现的位置
    System.out.println("===========================");


    sb1.deleteCharAt(0);//删除首字符
    sb1.delete(0,5);//删除[0, 5)范围内的字符
    String str = sb1.substring(0, 5);//截取[0, 5)区间中的字符以String的方式返回
    System.out.println(str);
    System.out.println("===========================");


    sb1.reverse();//字符串逆转
    str = sb1.toString();//将StringBuffer以String的方式返回
    System.out.println(str);
}

从上述例子可以看出:String和StringBuilder最大的区别在于String的内容无法修改,而StringBuilder的内容可以修改。频繁修改字符串的情况考虑使用StringBuilder

注意:String和StringBuilder不能直接转换。如果想要互相转换,可以采用如下原则:

  • String变成StringBuilder:利用StringBuilder的构造方法或者append()方法
public static void main(String[] args) {
    String str = "hello";
    StringBuilder sb = new StringBuilder(str);//使用构造方法
    //或者
    StringBuilder sb2 = new StringBuilder();
    System.out.println(sb2.append(str));//使用append方法
}
  • StringBuilder变成String:调用toString()方法。
public static void main(String[] args) {
    StringBuilder sb = new StringBuilder("hello");
    String str = sb.toString();
    System.out.println(str);
}

3.2 面试题:

  1. String、StringBuffer、StringBuilder的区别
  • String的内容不可修改,StringBuffer和StringBuilder的内容可以修改
  • StringBuffer和StringBuilder的大部分功能是相似的
  • StringBuffer采用同步处理,属于线程安全操作,而StringBuilder未采用同步处理,属于线程不安全操作
  1. 以下总共创建了多少个String对象 【前提不考虑常量池之前是否存在】
public class Test {
    public static void main(String[] args) {
        String str = new String("ab");//会创建多少个对象
        String str1 = new String("a") + new String("b");//会创建多少个对象
    }
}

在Java中,使用new关键字创建一个对象时,就会在堆内存中分配一个新的对象实例。现在,来分析一下代码,看看分别创建了多少个对象。

对于第一行代码:

String str = new String("ab");

这里会创建两个对象:

  • 一个是在字符串常量池中创建的字符串字面量"ab"(如果池中尚未存在该字面量)。但请注意,如果之前已经有相同的字符串字面量存在于常量池中,则不会创建新的字面量对象。

  • 另一个是通过 new String(“ab”) 在堆上创建的一个新的String对象,该对象的内容与字符串常量池中的"ab"相同,但是是堆上的一个独立副本。

所以,这行代码至少会创建一个堆上的String对象,可能还会在字符串常量池中创建一个对象(如果之前不存在的话)。

对于第二行代码:

String str1 = new String("a") + new String("b");

这里发生的事情稍微复杂一些:

  • 首先,字符串字面量"a"和"b"会被放入字符串常量池(如果它们之前不存在的话)。

  • 然后,通过 new String(“a”)和new String(“b”) 在堆上分别创建两个新的String对象。

当执行字符串连接操作时(使用+运算符),Java会创建一个新的StringBuilder对象(在某些情况下可能是StringBuffer,但在大多数情况下是StringBuilder),并使用其append方法来连接两个字符串。然后,通过调用StringBuilder的toString方法来创建一个表示连接结果的新String对象。

  • 因此,这行代码至少会创建以下对象:

两个在堆上的String对象(通过new String(“a”)和new String(“b”)创建)。

  1. 一个StringBuilder对象(或类似的可变字符序列对象)。
  2. 一个表示连接结果的String对象(通过StringBuilder的toString方法创建)。
  • 另外,如果字符串常量池中之前没有"a"和"b"这两个字面量,那么还会在池中创建这两个对象。

综上所述,str的初始化至少会创建一个对象(堆上的String),可能还会在字符串常量池中创建一个;而str1的初始化则会创建至少四个对象(两个堆上的String,一个StringBuilder,以及一个表示连接结果的String),同样,如果字符串常量池中之前没有相关字面量,还会在池中创建。不过,请注意,JVM的具体实现和版本可能会影响这些细节。

4. String类oj

4.1第一个只出现一次的字符

第一个只出现一次的字符

描述:

	给定一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1 
	
	示例 1:
	输入: s = "leetcode"
	输出: 0
	
	示例 2:
	输入: s = "loveleetcode"
	输出: 2
	
	示例 3:
	输入: s = "aabb"
	输出: -1

代码展示:

class Solution {
    public static int firstUniqChar(String s) {
        int[] count = new int[26];//这个数组拿来计数:字符出现的次数
        for(int i = 0; i < s.length(); i++) {
            //从字符串中拿出字符:
            char ch = s.charAt(i);//length
            //放在计数的数组中
            count[ch - 'a'] ++;
        }
        //计数完重新遍历一遍字符串,找到它的第一个不重复的字符
        for(int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            if(count[ch - 'a'] == 1) {
                return i;
            }
        }
        return -1;
    }
}

public class Test {
    public static void main(String[] args) {
        int ret = Solution.firstUniqChar("loveleetcode");
        System.out.println(ret);
    }
}

解析:

  • 初始化计数数组:首先,代码创建了一个大小为26的整数数组count,用于记录字符串中每个小写字母出现的次数。因为英文字母有26个,所以数组大小是26。
  • 统计字符出现次数:接着,代码遍历输入字符串s,对每个字符进行计数。字符的计数位置是通过将字符转换为0-25之间的索引来确定的(通过ch - ‘a’)。例如,字符’a’对应索引0,字符’b’对应索引1,以此类推。
  • 寻找第一个只出现一次的字符:完成计数后,代码再次遍历字符串s。这次遍历的目的是找到第一个在count数组中计数为1的字符,即只出现一次的字符。一旦找到这样的字符,就返回它在字符串中的位置(索引)。
  • 没有只出现一次的字符:如果字符串中没有只出现一次的字符,函数将返回-1。

简而言之,这段代码通过两次遍历字符串:第一次统计字符出现次数,第二次找到第一个只出现一次的字符。

图文:
在这里插入图片描述

4.2最后一个单词的长度

最后一个单词的长度
描述:

计算字符串最后一个单词的长度,单词以空格隔开,字符串长度小于5000。(注:字符串末尾不以空格为结尾)
输入描述:
输入一行,代表要计算的字符串,非空,长度小于5000。

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

示例:

输入:hello nowcoder
输出:8
说明:最后一个单词为nowcoder,长度为8 

代码展示:

  • 方法1:
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    //方法1:使用split方法 -> 切割字符串
    public static void main1(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String s = scanner.nextLine();
        //使用split切割字符串,装在新的数组中
        String[] ss = s.split(" ");
        //取出最后一个索引的内容
        int len = ss[ss.length - 1].length();
        System.out.println(len);
    }
}

解析:

  • 创建Scanner对象:首先,代码创建了一个Scanner对象来读取用户的输入。
    读取一行文本:使用scanner.nextLine()方法读取用户输入的一行文本,并将其存储在字符串s中。
  • 分割字符串:使用s.split(" ")方法将字符串s按照空格分割成多个子字符串,并将这些子字符串存储在一个字符串数组ss中。
  • 获取最后一个单词的长度:通过ss[ss.length - 1]获取数组ss中的最后一个元素(即最后一个单词),然后使用.length()方法计算这个单词的长度,并将长度存储在变量len中。
  • 打印长度:最后,代码使用System.out.println(len)打印出最后一个单词的长度。

简而言之,这段代码就是读取一行文本,找出其中的最后一个单词,并输出这个单词的长度。

  • 方法2:
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Test {
    //方法2:使用lastIndexOf() 和 substring()
    public static void main(String[] args) {
        //lastIndexOf() : 从字符串的后面开始找: " ",找到就返回索引,否则就返回-1
        //substring(int beginIndex) : 从beginIndex开始发生截断
        Scanner scanner = new Scanner(System.in);
        String s = scanner.nextLine();
        //找最后一个空格后面的字符串
        int index = s.lastIndexOf(" ");
        //从index索引开始发生截断 -> 得到index+1后面的字符串
        int len = s.substring(index + 1).length();
        System.out.println(len);
    }
}

解析:

  • 读取输入:首先,通过Scanner对象读取用户输入的一行文本。
  • 查找最后一个空格:使用lastIndexOf(" ")方法从输入的文本中查找最后一个空格的位置,并返回其索引。如果没有找到空格,该方法会返回-1,但在这个上下文中,我们假设输入中至少有一个空格。
  • 截取字符串:使用substring(index + 1)方法从最后一个空格之后开始截取字符串。index + 1确保了我们从空格之后的第一个字符开始截取。
  • 计算长度并输出:计算截取后的字符串的长度,并使用System.out.println输出这个长度。

简而言之,这段代码读取一行文本,找到最后一个空格后的字符串部分,并输出其长度。

4.3检测字符串是否为回文

检测字符串是否为回文
描述:

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

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

给你一个字符串 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 是一个空字符串 "" 。
由于空字符串正着反着读都一样,所以是回文串。

代码展示:

class Solution {
    public boolean isPalindrome(String s) {
        //所有大写字符转换为小写字符:
        s = s.toLowerCase();
        //开始判断:定义两个索引:一个从左边开始, 一个从右边开始,进行对比
        int left = 0;
        int right = s.length() - 1;
        //开始对比:
        while(left < right) {
            //遇到非字母和非数字就跳过,不进行比较
            while(left < right && !isNumberAndCharacter(s.charAt(left))) {
                left++;
            }
            while(left < right && !isNumberAndCharacter(s.charAt(right))) {
                right--;
            }
            //走到这,证明字符都是字母,可以进行对比
            if(s.charAt(left) == s.charAt(right)) {
                left++;
                right--;
                //对比完,就往下走
            }else {
                //不相同,说明不是回文串
                return false;
            }
        }
        //走到这,证明是回文串
        return true;
    }

    //字母和数字都属于字母数字字符。 -> 假如是非字母和非数字就跳过,不进行比较
    private boolean isNumberAndCharacter(char ch) {
        //用isDigit() : 判断字符是否为数字
        //用isLetter() : 判断字符是否为字母
        if(Character.isDigit(ch) || Character.isLetter(ch)) {
            return true;
        }else {
            return false;
        }
    }
}
public class Test {
    public static void main(String[] args) {
        Solution solution = new Solution();
        System.out.println(solution.isPalindrome("A man, a plan, a canal: Panama"));
    }
}

解析:
代码定义了一个Solution类,其中包含两个方法:isPalindrome和isNumberAndCharacter。这个类的主要功能是检查一个字符串是否是一个“回文串”,即正读和反读都相同的字符串

  1. isPalindrome方法:

    • 首先,该方法将输入字符串s转化为小写,以确保大小写不影响回文的判断。

    • 定义了两个指针,left从字符串的开始位置出发,right从字符串的末尾位置出发。

    • 使用一个while循环,当left小于right时执行循环体内的代码。
      在循环体内,代码会跳过left指针位置上的非字母和非数字字符,直到找到第一个字母或数字字符。

      • 同样,代码也会跳过right指针位置上的非字母和非数字字符,直到找到第一个字母或数字字符。
      • 接着,比较left和right指针位置上的字符是否相同。如果相同,则将两个指针向中间移动;如果不同,则直接返回false,表示该字符串不是回文串。
    • 如果left指针超过了right指针,说明已经检查完整个字符串,且字符串是回文的,所以返回true。

  2. isNumberAndCharacter方法:

    • 这个辅助方法用于检查一个字符是否是字母或数字。
    • 使用Character.isDigit(ch)检查字符ch是否为数字。
    • 使用Character.isLetter(ch)检查字符ch是否为字母。
    • 如果字符是数字或字母,则返回true;否则返回false。

简而言之,这段代码通过双指针的方法,从字符串的两端向中间遍历,同时跳过非字母和非数字的字符,来判断一个字符串是否是回文串。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/727639.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

密钥管理简介

首先我们要知道什么是密钥管理&#xff1f; 密钥管理是一种涉及生成、存储、使用和更新密钥的过程。 密钥的种类 我们知道&#xff0c;对称密码主要包括分组密码和序列密码。但有时也可以将杂凑函数和消息认证码划分为这一类&#xff0c;将它们的密钥称为对称密钥&#xff1b;…

T200S4高清4路SDI采集卡

产品简介&#xff1a; 同三维T200S4 4路高清SDI采集卡&#xff0c;可以同时采集4路SDI高清信号&#xff0c;卡上有4个SDI接口1个SDI环出转接口&#xff0c;配件有&#xff1a; 1个转SDI转接线&#xff0c;PCI-E2.0 X4&#xff0c;分辨率最高可以达到1080P/60HZ,带SDK开发包&am…

Redis分片集群搭建

主从模式可以解决高可用、高并发读的问题。但依然有两个问题没有解决&#xff1a; 海量数据存储高并发写 要解决这两个问题就需要用到分片集群了。分片的意思&#xff0c;就是把数据拆分存储到不同节点&#xff0c;这样整个集群的存储数据量就更大了。 Redis分片集群的结构如…

酸性设计震撼登场,让你眼前一亮!

说起酸性&#xff08;ACID&#xff09;&#xff0c;你会想到什么&#xff1f;”我们通常会想到酸味&#xff0c;酸设计的视觉魅力是通过图形、颜色、排版给人复古、迷幻、黑暗、叛逆的感觉&#xff0c;反复几何图形和高饱和的颜色&#xff0c;使设计非常时尚&#xff0c;非常适…

AI 情感聊天机器人之旅 —— 相关论文调研

开放域闲聊场景 Prompted LLMs as Chatbot Modules for Long Open-domain Conversation 发布日期&#xff1a;2023-05-01 简要介绍&#xff1a;作者提出了 MPC&#xff08;模块化提示聊天机器人&#xff09;&#xff0c;这是一种无需微调即可创建高质量对话代理的新方法&…

Linux计划任务与日志

计划任务 主要用于完成一些周期性任务及定时任务&#xff0c;Windows中也有该功能&#xff1a; 单次调度执行 yum install -y at安装at工具&#xff0c;systemctl start atd启动服务&#xff0c;使用方法为at 选项 时间 执行内容时间可以自由设置&#xff0c;开启的栏目中输…

【网络安全产品】---网闸

了解了不少安全产品&#xff0c;但是对网闸的理解一直比较模糊&#xff0c;今天 what 网闸是安全隔离与信息交换系统的简称&#xff0c;使得在不影响数据正常通信的前提下&#xff0c;让络在不连通的情况下数据的安全交换和资源共享&#xff0c;对不同安全域/网络之间实现真正…

【可控图像生成系列论文(二)】MimicBrush 港大、阿里、蚂蚁集团合作论文解读2

【可控图像生成系列论文&#xff08;一&#xff09;】简要介绍了论文的整体流程和方法&#xff0c;本文则将就整体方法、模型结构、训练数据和纹理迁移进行详细介绍。 1.整体方法 MimicBrush 的整体框架如下图所示。为了实现模仿编辑&#xff0c;作者设计了一种具有双扩散模型…

【Python】一文向您详细解析内置装饰器 @lru_cache

【Python】一文向您详细解析内置装饰器 lru_cache 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1a;985高校的普通本硕&a…

QT-QPainter实现一个可切换的开关控件

1、效果 2、核心代码 #ifndef SWITCH_H #define SWITCH_H #include <QWidget> #include <QTimer>

GitLab项目组相关操作(创建项目组Group、创建项目组的项目、为项目添加成员并赋予权限)

天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。 君不见,黄河之水天上来,奔流到海不复回。 君不见,高堂明镜悲白发,朝如青丝暮成雪。 ——《将…

信息学奥赛初赛天天练-30CSP-J2022完善程序-结构体构造函数初始化、auto关键字、连通块、洪水填充算法实战

PDF文档公众号回复关键字:20240620 2022 CSP-J 阅读程序2 完善程序 (单选题 &#xff0c;每小题3分&#xff0c;共30分) 2 (洪水填充) 现有用字符标记像素颜色的8 * 8图像。颜色填充操作描述如下&#xff1a;给定起始像素的位置和待填充的颜色&#xff0c;将起始像素和所有可…

【JavaEE】Spring Boot MyBatis详解(二)

一.解决数据库字段名和对象属性名冲突的问题. 产生这个问题的本质原因就是Java 属性名和数据库字段的命名规范不同. 这个问题的本质就是查询数据库返回了字段,但是不知道和Java对象的哪个属性相对应 1.注解的解决方法 注解的解决方式有三种: 方式一:给数据库字段起别名. 本质…

QT-QPainter实现一个动态充电的电池

1、效果 2、核心代码 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTimer>

全网最全postman接口测试教程和项目实战~从入门到精通

Postman实现接口测试内容大纲一览&#xff1a; 一、什么是接口&#xff1f;为什么需要接口&#xff1f; 接口指的是实体或者软件提供给外界的一种服务。 因为接口能使我们的实体或者软件的内部数据能够被外部进行修改。从而使得内部和外部实现数据交互。所以需要接口。 比如&…

番外篇 | 基于改进YOLOv5的安全帽佩戴检测 | 重参数化结构RepVGG + 空间对象注意力机制RCS-OSA模块

前言:Hello大家好,我是小哥谈。RCS-YOLO是一种目标检测算法,它是基于YOLOv3算法的改进版本。通过查看RCS-YOLO的整体架构可知,其中包括RCS-OSA模块。RCS-OSA模块在模型中用于堆叠RCS模块,以确保特征的复用并加强不同层之间的信息流动。本文针对安全帽佩戴的检测就是基于RC…

[leetcode hot 150]第十五题,三数之和

题目&#xff1a; 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答案中不可以包含重复…

RSA 加密算法的基础数论、基本原理与 Python 实现

Title: RSA 加密算法的基础数论、基本原理与 Python 实现 文章目录 前言I. 数学原理1. 整数环2. 单位元3. 欧拉定理 II. 算法原理1. 扩展欧几里得算法2. RSA 非对称加密算法 III. 算法实现1. 源代码2. 测试结果 总结参考文献 前言 1977 年美国 MIT 的三位数学家 Ronald L. Riv…

startActivity启动流程

从桌面点击应用图标开始到Activity创建并执行onCreate&#xff0c;activity的启动涉及到两个进程system_server(AMS所在进程)和Zygote(如果进程没有创建需要先创建) 下图是从点击图标开始执行startActivity&#xff0c;一直到ActivityTaskSupervisor&#xff0c;到ActivityTas…

QT自定义标题栏窗口其二:实现拖动及可拉伸效果 + 顶部全屏/侧边半屏

1、效果 2、核心代码 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent<