如何检查一个字符串中是否包含单个字符?

281

在Java中,有没有一种方法可以检查以下条件:

“单个字符是否出现在字符串x中”

而不需要使用循环?


6
你避免使用循环的特殊原因是什么? - shsteimer
3
你不能进行不使用循环的字符的普通搜索。查阅图灵机的工作原理。 - Salvador Valencia
9
我们应该假设@barfoon不希望循环出现在他们的代码中。显然,机器会在某个地方进行循环,否则这个问题就没有意义。 - WW.
我会说Java的字符串操作相当有限。 - ACV
@barfoon,根据“单个字符”的定义以及用户访问该字符的方式,可能会有不同的使用模式。我已经添加了一个答案 - Thiyanesh
16个回答

357
您可以使用string.indexOf('a')
如果字符a存在于string中:

它会返回此对象所表示的字符序列中第一次出现该字符的索引,如果未找到该字符,则返回-1。


12
但是在调用背后总会有一个循环,因为否则你找不到符号。 - vava
7
indexOf() 内部使用循环。 - mmcdole
26
Barfoon并不是这样问的。Barfoon希望避免在自己的代码中使用循环。自然地,API需要进行循环,毕竟String是一个包含许多有用方法的字符数组封装类。 - mP.
6
这些答案为什么能获得这么多的赞?使用 indexOf() 函数的解决方案在内部使用循环。没有任何答案给出正确的解决方案,如果有人敢于提出一个新问题,人们会宣布它为“重复”。真的令人失望 ;( - Prashant Prabhakar Singh
9
@PrashantPrabhakarSingh,我不认为这可以在没有循环的情况下完成。字符串基本上是一组字符。如果它是一组(集合、数组等),那么无论是内部还是外部于本地代码,我都认为你需要循环来查找“组”中的某些内容。我认为“不使用循环?”更像是“不编写自己的循环?”。 - Tyler
显示剩余4条评论

163
  • String.contains():检查字符串是否包含指定的字符序列
  • String.indexOf():返回字符串中指定字符或子字符串的第一个出现位置的索引(此方法有4种变体)

19
一个字符不是一个CharSequence,因此它不能传递给String.contains(CharSequence)函数。 - mP.
44
要使用String.contains()来查询单个字符c,请这样做:String.contains(Character.toString(c)) - friederbluemle
11
如果您喜欢简洁的代码,可以这样写:String.contains(""+c) - Felix Neumeyer

38

我不确定原帖作者确切在问什么。由于indexOf(...)和contains(...)都 可能 在内部使用循环,也许他想知道是否有可能在没有循环的情况下完成这个任务?我可以想到两种方法,其中一种当然是递归:

public boolean containsChar(String s, char search) {
    if (s.length() == 0)
        return false;
    else
        return s.charAt(0) == search || containsChar(s.substring(1), search);
}

另一个解决方案不太优雅,但更完整...

/**
 * Works for strings of up to 5 characters
 */
public boolean containsChar(String s, char search) {
    if (s.length() > 5) throw IllegalArgumentException();

    try {
        if (s.charAt(0) == search) return true;
        if (s.charAt(1) == search) return true;
        if (s.charAt(2) == search) return true;
        if (s.charAt(3) == search) return true;
        if (s.charAt(4) == search) return true;
    } catch (IndexOutOfBoundsException e) {
        // this should never happen...
        return false;
    }
    return false;
}

当然,随着您需要支持越来越长的字符串,行数也会增加。但是根本没有循环/递归。如果您担心length()使用了循环,则甚至可以删除长度检查。


14
如果您将递归定义为一种非循环过程,那么您就是个极客 :D 因为您很有创造力。 - guerda
1
对于硬编码长度为5,一切都很好。否则需要进行一个循环来搜索字符。不是为了挑剔,但这个证明可以通过图灵机的定义得到。这是计算设备的基础。 - Salvador Valencia
4
如果我错了,请纠正我,我觉得归根结底,递归是一个伪装成循环的循环,是吗?在某些情况下,递归可能会导致比普通循环更多的内存消耗。 - PasinduJay

14

您可以使用String类中的2种方法。

  • String.contains()检查字符串是否包含指定的字符值序列
  • String.indexOf()返回字符串中指定字符或子字符串的第一次出现的索引,如果未找到该字符则返回-1(此方法有4个变体)

方法1:

String myString = "foobar";
if (myString.contains("x") {
    // Do something.
}

方法2:

String myString = "foobar";
if (myString.indexOf("x") >= 0 {
    // Do something.
}

链接来自:Zach Scrivena


12
String temp = "abcdefghi";
if(temp.indexOf("b")!=-1)
{
   System.out.println("there is 'b' in temp string");
}
else
{
   System.out.println("there is no 'b' in temp string");
}

1
这不是完全重复了已接受答案吗?我们感谢您的努力,但您应该尝试找到一些未回答的问题并回答它们。 - Shekhar_Pro

5

如果你需要经常检查同一个字符串,可以预先计算字符出现的次数。以下是一种使用长整型数组中包含位数组的实现:

public class FastCharacterInStringChecker implements Serializable {
private static final long serialVersionUID = 1L;

private final long[] l = new long[1024]; // 65536 / 64 = 1024

public FastCharacterInStringChecker(final String string) {
    for (final char c: string.toCharArray()) {
        final int index = c >> 6;
        final int value = c - (index << 6);
        l[index] |= 1L << value;
    }
}

public boolean contains(final char c) {
    final int index = c >> 6; // c / 64
    final int value = c - (index << 6); // c - (index * 64)
    return (l[index] & (1L << value)) != 0;
}}

1
我在一个类似的问题上尝试了你的解决方案。我的最佳解决方案是针对字符串1长度为63k和字符串2长度为95k的情况下超过1500毫秒。而你的解决方案可以在3-5毫秒内输出结果。你能否编辑你的解决方案并加入一些解释呢?拜托了。 - Viorel Florian

4

要检查字符串中是否不存在某个字符,您至少需要查看字符串中的每个字符。因此,即使您不明确使用循环,它的效率也是相同的。话虽如此,您可以尝试使用str.contains(""+char)。


同意。在某个时候,某人需要构建一个循环来完成这个任务。幸运的是,Java API可以做到这一点,否则我们的代码会非常混乱! - Fortyrunner

3
下面的内容是您在寻找的吗?
int index = string.indexOf(character);
return index != -1;

为什么你要加上 && string.lastIndexOf(character) != index - GreenAsJade

3

String.contains(String)或者String.indexOf(String) - 建议使用


Explanation: 该句话为技术相关内容,建议使用String类型自带的contains和indexOf方法,无需进一步解释。
"abc".contains("Z"); // false - correct
"zzzz".contains("Z"); // false - correct
"Z".contains("Z"); // true - correct
"and".contains(""); // true - correct
"and".contains(""); // false - correct
"and".indexOf(""); // 0 - correct
"and".indexOf(""); // -1 - correct

String.indexOf(int) 和经过深思熟虑的 String.indexOf(char),其中 char 转为 int 时需要进行扩展。

"and".indexOf("".charAt(0)); // 0 though incorrect usage has correct output due to portion of correct data
"and".indexOf("".charAt(0)); // 0 -- incorrect usage and ambiguous result
"and".indexOf("".codePointAt(0)); // -1 -- correct usage and correct output

Java世界中关于字符的讨论存在歧义

是否可以认为charCharacter的值是单个字符?

不可以。在Unicode字符的上下文中,charCharacter有时可以 作为单个字符的一部分,逻辑上不应被视为完整的单个字符

如果不行,那么从逻辑上应该考虑什么才能算单个字符?

任何支持Unicode字符编码的系统都应该将Unicode的代码点视为单个字符。

因此,Java应该明确而清晰地做到这一点,而不是向用户公开太多的内部实现细节。

String类在抽象层面上做得不好(虽然需要混淆性强的封装理解才能理解抽象并因此形成了一个反模式)。

这与普通char的使用有什么不同?

char只能映射到基本多语言平面中的字符。

只有codePoint-int才能涵盖Unicode字符的完整范围。

为什么会有这种差异?

char在内部被视为16位无符号值,使用UTF-16内部表示时,不能仅用2字节表示所有的Unicode字符。有时,需要将一个16位范围内的值与另一个16位值组合才能正确定义字符。

不过,在使用indexOfcharAtlength等方法时,不要过于冗长。真诚地希望Java能添加新的UnicodeStringUnicodeCharacter类,并具有清晰定义的抽象。

为什么倾向于使用contains而不是indexOf(int)

  1. 实际上,许多代码流在Java中将逻辑字符视为char
  2. 在Unicode上下文中,char不足够
  3. 虽然indexOf接受一个int,但charint转换掩盖了这一点,用户可能会做类似于str.indexOf(someotherstr.charAt(0))的操作(除非用户了解确切的上下文)。
  4. 因此,把一切都视为CharSequence(也就是String)更好。
    public static void main(String[] args) {
        System.out.println("and".indexOf("".charAt(0))); // 0 though incorrect usage has correct output due to portion of correct data
        System.out.println("and".indexOf("".charAt(0))); // 0 -- incorrect usage and ambiguous result
        System.out.println("and".indexOf("".codePointAt(0))); // -1 -- correct usage and correct output
        System.out.println("and".contains("")); // true - correct
        System.out.println("and".contains("")); // false - correct
    }

语义

  1. char 可以处理大多数实际使用情况。但在编程环境中使用 codepoints 以便未来扩展性更好。
  2. codepoint 应该可以处理几乎所有与编码相关的技术用例。
  3. 然而,Grapheme Clusters 超出了 codepoint 抽象级别的范围。
  4. 如果 int 太昂贵(加倍),存储层可以选择 char 接口。除非存储成本是唯一的指标,否则仍然最好使用 codepoint。此外,最好将存储视为 byte 并将语义委托给围绕存储构建的业务逻辑。
  5. 语义可以在多个级别上抽象。 codepoint 应该成为最低级别的接口,其他语义可以在运行时环境中围绕 codepoint 构建。

2
那是一个非常开心的回答 :) - dreamcrash
我读过的最好的答案,做得很好。 - Ray Toal

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接