Java中的CharSequence VS String?


Answers:


343

字符串是CharSequences,因此您可以只使用String而不用担心。Android只是通过允许您还指定其他CharSequence对象(例如StringBuffers)来尝试提供帮助。


94
除非Android在回调中向我传递CharSequence,并且我需要一个String-调用charSeq.toString()。
Martin Konicek

100
但是请记住来自CharSequencejavadoc的警告:此接口不会完善equalsand hashCode方法的常规协定。因此,比较两个实现的对象的结果CharSequence通常是undefined。每个对象都可以由不同的类实现,并且不能保证每个类都能够测试其实例与另一个实例的相等性。因此,将任意CharSequence实例用作集合中的元素或用作映射中的键是不合适的。
Trevor Robinson

3
@Pacerier:我认为这更多是实际的限制。CharSequence是JDK 1.4的一种改进,目的是向包含字符序列的对象引入有限用途的通用接口。这些对象中的某些包含其他状态,因此将其定义Object.equals为“包含相同的字符序列” 可能没有意义。NIO CharBuffer,例如,仅公开其字符之间positionlimit作为CharSequence,尽管可能持有许多其他字符。
Trevor Robinson 2014年

6
@TrevorRobinson,这样的设计缺陷是有equals/ hashCodeObject摆在首位....
Pacerier

4
@Pacerier:恕我直言,Object或中都没有设计错误CharSequence,不需要接口来实现实现之间的平等。不需要两个Collection提供Collection接口之间的相等性,但是如果愿意的话,它们可以。恕我直言,CharSequence应仅限于输入,并较少用于返回类型。
Brett Ryan

58

CharSequence=接口
String=具体实施

你说:

从一个转换为另一个

没有从转换String

  • 每个String对象都是一个CharSequence
  • 每个人CharSequence都能产生一个String。致电CharSequence::toString。如果CharSequence碰巧是一个String,则该方法返回对其自身对象的引用。

换句话说,每个String都是a CharSequence,但不是每个CharSequence都是a String

编程接口

在Android中编程时,大多数文本值应在CharSequence中使用。

这是为什么?在String上使用CharSequence有什么好处?主要影响是什么?

通常,对接口进行编程比对具体类进行编程要好。这产生了灵活性,因此我们可以在不破坏其他代码的情况下在特定接口的具体实现之间进行切换。

当开发供各种程序员在各种情况下使用的API时,编写代码以提供和采用最通用的接口。这使调用程序的程序员可以自由使用该接口的各种实现,无论哪种实现最适合其特定的上下文。

例如,查看Java Collections Framework。如果您的API给或采取对象的有序集合,如使用声明你的方法List,而不是ArrayListLinkedList或任何其他第三方实现List

当编写一个仅在一个特定位置仅由您的代码使用的快捷方法时,与编写在多个位置中使用的API相对,您不必费心使用更通用的接口而不是特定的具体方法类。但是即使那样,使用您可以使用的最通用的界面也确实有害。

使用它们时,主要区别是什么?预期会出现什么问题,

  • 使用,String您知道只有一条文本完全在内存中,并且是不可变的。
  • 使用CharSequence,您可能不知道具体实现的特定功能。

CharSequence对象可能代表大量文本,因此具有内存含义。或者,可能是许多单独跟踪的文本块在您调用时需要缝合在一起toString,因此存在性能问题。该实现甚至可能正在从远程服务中检索文本,因此具有延迟隐患。

并从一个转换为另一个?

您通常不会来回转换。A String 一个CharSequence。如果您的方法声明采用a CharSequence,则调用程序的程序员可能会传递一个String对象,也可能传递诸如a StringBuffer或的其他内容StringBuilder。您方法的代码将仅使用传递的任何内容,并调用任何CharSequence方法。

最接近转换的是,如果代码收到,CharSequence并且您知道需要String。也许您正在与写入String类而不是写入接口的旧代码CharSequence交互。也许您的代码将与文本紧密协作,例如重复循环或进行其他分析。在这种情况下,您只希望对性能造成任何影响,因此请toString提前致电。然后使用您知道的完全在内存中的单个文本继续进行工作。

扭曲的历史

注意对已接受答案的评论。该CharSequence接口已改型为现有的类结构,因此存在一些重要的细微之处(equals()hashCode())。请注意在类/接口上标记的Java的各种版本(1、2、4和5),这些年来有些翻腾。理想情况下CharSequence,从一开始就应该存在,但这就是生活。

下面的类图可以帮助您了解Java 7/8中字符串类型的概况。我不确定所有这些内容是否都存在于Android中,但是整体上下文可能仍然对您有用。

各种与字符串相关的类和接口的示意图


2
您是否自己制作了此图?想知道是否有针对不同数据结构的这些图的目录。
user171943'1

4
@ user171943由我编写,使用OmniGroup的OmniGraffle应用程序手工制作。
罗勒·布尔克

37

我相信最好使用CharSequence。原因是String实现了CharSequence,因此您可以将String传递给CharSequence,但是您不能将CharSequence传递给String,因为CharSequence不实现String。另外,在Android中,该EditText.getText()方法返回一个Editable,该方法也实现了CharSequence,可以轻松地传递给一个,而不容易传递给String。CharSequence处理所有操作!


7
你可以做charSequence.toString()
豪尔赫·富恩特斯·冈萨雷斯2013年

1
@jorge:除非序列可变(或者出于任何原因需要复制字符以便生成不可变的字符串),否则效率相对较低。
劳伦斯·多尔

很好的解释..!
majurageerthan

23

通常,使用接口可使您以最小的附带损害来改变实施方式。尽管java.lang.String非常流行,但在某些情况下可能需要使用另一种实现。通过围绕CharSequences而不是Strings构建API,代码使人们有机会做到这一点。


8

这几乎可以肯定是性能原因。例如,假设有一个解析器经过一个包含字符串的500k ByteBuffer。

有三种返回字符串内容的方法:

  1. 在解析时构建一个String [],一次构建一个字符。这将花费大量时间。我们可以使用==代替.equals来比较缓存的引用。

  2. 在解析时构建具有偏移量的int [],然后在发生get()时动态构建String。每个String将是一个新对象,因此不会缓存返回值并使用==

  3. 在解析时构建一个CharSequence []。由于没有存储任何新数据(字节缓冲区中的偏移量除外),因此解析比#1低得多。在获取时间,我们不需要构建String,因此get性能等于#1(比#2更好),因为我们只返回对现有对象的引用。

除了使用CharSequence获得的处理优势外,还通过不复制数据来减少内存占用。例如,如果您有一个包含3个文本段落的缓冲区,并且想要返回全部3个或单个段落,则需要4个字符串来表示此内容。使用CharSequence时,您只需要1个数据缓冲区和4个CharSequence实现实例来跟踪起始和长度。


6
参考PLZ。听起来就像是随机猜测发生了什么。我也觉得你的论点无效。首先可能只是将500k字节缓冲区存储为字符串,然后只返回子字符串,这非常疯狂,而且更为常见。
kritzikratzi

6
@kritzikratzi-从JDK7开始,String上的子字符串不再共享底层数组,并且不是“疯狂快速”。子字符串的长度需要O(N)时间,并且每次调用它都会生成基础字符的副本(因此会产生大量垃圾)。
BeeOnRope13年

@kritzikratzi我相信更改的原因是,如果不进行复制,则将在所有子字符串的生命周期内保留原始String。鉴于子字符串通常仅是原始字符串的一小部分,并且可以无限期地持续使用,具体取决于它们的使用方式,如果子字符串的使用时间比原始字符串长得多,这通常会导致更多的垃圾。一个有趣的替代方法可能是根据子字符串与父字符串大小的比率确定是否要复制,但是您必须为此滚动自己的CharSequence实现。
JAB 2014年

1
也许我错过了重点,但是这个答案是胡说八道。A CharSequence是一个接口 –根据定义,它没有您讨论的实现细节,因为它没有自己的实现。A String是实现接口的几种具体类之一CharSequence。所以a String 一个CharSequence。您可以比较Stringvs StringBuffervs的性能细节StringBuilder,但不能比较CharSequence。编写“使用CharSequence获得的处理收益”是没有意义的。
罗勒·布尔克

7

在实际的Android代码中确实出现的一个问题是,将它们与CharSequence.equals进行比较是有效的,但不一定能按预期工作。

EditText t = (EditText )getView(R.id.myEditText); // Contains "OK"
Boolean isFalse = t.getText().equals("OK"); // will always return false.

比较应由

("OK").contentEquals(t.GetText()); 

5

字符序列

A CharSequence是接口,而不是实际的类。接口只是类在实现接口时必须包含的一组规则(方法)。在Android中,a CharSequence是各种类型的文本字符串的保护伞。这是一些常见的:

(您可以在此处详细了解它们之间的区别。)

如果您有一个CharSequence对象,那么它实际上是实现的类之一的对象CharSequence。例如:

CharSequence myString = "hello";
CharSequence mySpannableStringBuilder = new SpannableStringBuilder();

拥有像这样的通用伞类型的好处CharSequence是您可以用一种方法处理多种类型。例如,如果我有一个将a CharSequence作为参数的方法,则可以传入a String或a SpannableStringBuilder,它将处理其中一个。

public int getLength(CharSequence text) {
    return text.length();
}

您可以说a String只是一种CharSequence。但是,与不同CharSequence,它是一个实际的类,因此您可以从中创建对象。因此,您可以这样做:

String myString = new String();

但您不能这样做:

CharSequence myCharSequence = new CharSequence(); // error: 'CharSequence is abstract; cannot be instantiated

由于CharSequence只是String符合条件的规则列表,因此您可以执行以下操作:

CharSequence myString = new String();

这意味着,只要方法要求CharSequence,就可以给它一个String

String myString = "hello";
getLength(myString); // OK

// ...

public int getLength(CharSequence text) {
    return text.length();
}

但是,事实并非如此。如果该方法采用String参数,则无法传递通常只被称为a的参数CharSequence,因为它实际上可能是a SpannableString或某种其他类型的CharSequence

CharSequence myString = "hello";
getLength(myString); // error

// ...

public int getLength(String text) {
    return text.length();
}

0

CharSequence是一个接口并String实现它。您可以实例化a,String但是CharSequence因为它是一个接口,所以您不能这样做。您可以CharSequence在Java官方网站上找到其他实现。


-4

CharSequence是实现String的char值的可读序列。它有4种方法

  1. charAt(int索引)
  2. 长度()
  3. subSequence(整数开始,整数结束)
  4. toString()

请参阅文档CharSequence文档


6
CharSequence没有实现String。相反,这是正确的。
2013年

1
请删除这个废话答案
J. Doe
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.