CharSequence VS String en Java?

421

Programando en Android, se esperan la mayoría de los valores de texto CharSequence.

¿Porqué es eso? ¿Cuál es el beneficio y cuáles son los principales impactos del uso CharSequenceexcesivo String?

¿Cuáles son las principales diferencias y qué problemas se esperan al usarlos y convertirlos de uno a otro?

e-satis
fuente
1
Se pueden encontrar mejores respuestas en la diferencia exacta entre CharSequence y String en Java
Suragch

Respuestas:

343

Las cadenas son secuencias de caracteres , por lo que puede usar cadenas y no preocuparse. Android simplemente está tratando de ser útil permitiéndole especificar también otros objetos CharSequence, como StringBuffers.

Zarkonnen
fuente
94
Excepto cuando Android me pasa un CharSequence en una devolución de llamada y necesito un String - llame a charSeq.toString ().
Martin Konicek
100
Pero tenga en cuenta esta advertencia del CharSequencejavadoc: esta interfaz no refina los contratos generales de los métodos equalsy hashCode. El resultado de comparar dos objetos que implementan CharSequencees, por lo tanto, en general, indefinido . Cada objeto puede ser implementado por una clase diferente, y no hay garantía de que cada clase sea capaz de probar la igualdad de sus instancias con las de la otra. Por lo tanto, es inapropiado usar CharSequenceinstancias arbitrarias como elementos en un conjunto o como claves en un mapa.
Trevor Robinson
3
@Pacerier: Creo que es más una limitación práctica. CharSequencefue una modificación en JDK 1.4 para introducir una interfaz común de propósito limitado a los objetos que contienen secuencias de caracteres. Algunos de esos objetos contienen otro estado, por lo que puede no tener sentido definir Object.equalscomo "contiene la misma secuencia de caracteres". NIO CharBuffer, por ejemplo, solo expone los caracteres entre su positiony limitcomo el CharSequence, a pesar de tener potencialmente muchos otros caracteres.
Trevor Robinson
66
@TrevorRobinson, así que el fallo de diseño es tener equals/ hashCodeen Objecten el primer lugar ....
Pacerier
44
@Pacerier: En mi humilde opinión No es un error de diseño, ya sea en Objecto CharSequence, no se requiere ninguna interfaz para proporcionar la cordura igualdad entre implementaciones. No Collectionse requieren dos s para proporcionar igualdad entre la Collectioninterfaz, pero pueden hacerlo si así lo desean. En mi humilde opinión, CharSequencedebe limitarse a las entradas y utilizar menos para tipos de retorno.
Brett Ryan
58

CharSequence= interfaz
String= implementación concreta

Tu dijiste:

convirtiendo de uno a otro

No hay conversión de String.

  • Cada Stringobjeto es unCharSequence .
  • Todos CharSequencepueden producir a String. LlamadaCharSequence::toString . Si el CharSequencepasa a ser unString , entonces el método devuelve una referencia a su propio objeto.

En otras palabras, cada String es un CharSequence, pero no todos CharSequenceson unString .

Programando a una interfaz

Programando en Android, la mayoría de los valores de texto se esperan en CharSequence.

¿Porqué es eso? ¿Cuál es el beneficio y cuáles son los principales impactos del uso de CharSequence sobre String?

En general, programar en una interfaz es mejor que programar en clases concretas. Esto proporciona flexibilidad, por lo que podemos cambiar entre implementaciones concretas de una interfaz particular sin romper otro código.

Al desarrollar una API para ser utilizada por varios programadores en diversas situaciones, escriba su código para dar y tomar las interfaces más generales posibles. Esto le da al programador que llama la libertad de usar diversas implementaciones de esa interfaz, la implementación que sea mejor para su contexto particular.

Por ejemplo, mire el Java Collections Framework . Si su API da o toma una colección ordenada de objetos, declarar sus métodos como el uso Listen lugar de ArrayList, LinkedListo cualquier otra aplicación de 3 ª parte List.

Al escribir un pequeño método rápido y sucio para ser usado solo por su código en un lugar específico, en lugar de escribir una API para usar en varios lugares, no necesita molestarse en usar la interfaz más general en lugar de un concreto específico clase. Pero incluso entonces, le duele usar la interfaz más general que pueda.

¿Cuáles son las principales diferencias y qué problemas se esperan al usarlos?

  • Con un Stringsabes que tienes una sola pieza de texto, completamente en memoria, y es inmutable.
  • Con un CharSequence, no sabe cuáles podrían ser las características particulares de la implementación concreta.

El CharSequenceobjeto puede representar una enorme porción de texto y, por lo tanto, tiene implicaciones de memoria. O puede haber muchos fragmentos de texto rastreados por separado que necesitarán unirse cuando llame toString, y por lo tanto tiene problemas de rendimiento. La implementación puede incluso recuperar texto de un servicio remoto y, por lo tanto, tiene implicaciones de latencia.

y convirtiendo de uno a otro?

Por lo general, no se convertirá de un lado a otro. A String es a CharSequence. Si su método declara que toma un CharSequence, el programador que llama puede pasar un Stringobjeto, o puede pasar algo más como un StringBuffero StringBuilder. El código de su método simplemente usará lo que se haya pasado, llamando a cualquiera de los CharSequencemétodos.

Lo más cerca que estaría de convertir es si su código recibe un CharSequencey sabe que necesita un String. Quizás esté interactuando con el código antiguo escrito en la Stringclase en lugar de hacerlo en la CharSequenceinterfaz. O tal vez su código trabajará intensamente con el texto, como hacer bucles repetidamente o analizar de otro modo. En ese caso, desea recibir cualquier posible golpe de rendimiento solo una vez, por lo que debe llamar toStringpor adelantado. Luego, continúe con su trabajo utilizando lo que sabe que es una sola pieza de texto completamente en la memoria.

Historia retorcida

Tenga en cuenta los comentarios realizados en la respuesta aceptada . La CharSequenceinterfaz se actualizó en las estructuras de clase existentes, por lo que hay algunas sutilezas importantes ( equals()& hashCode()). Observe las diversas versiones de Java (1, 2, 4 y 5) etiquetadas en las clases / interfaces, un poco de abandono a lo largo de los años. Lo ideal CharSequencesería haber estado en su lugar desde el principio, pero así es la vida.

Mi diagrama de clases a continuación puede ayudarlo a ver el panorama general de los tipos de cadenas en Java 7/8. No estoy seguro de si todos estos están presentes en Android, pero el contexto general aún puede resultarle útil.

diagrama de varias clases e interfaces relacionadas con cadenas

Albahaca Bourque
fuente
2
¿Hiciste este diagrama tú mismo? preguntándose si existe un catálogo de estos diagramas para diferentes estructuras de datos.
user171943
44
@ user171943 Creado por mí, hecho a mano con la aplicación OmniGraffle del OmniGroup.
Basil Bourque
37

Creo que es mejor usar CharSequence. La razón es que String implementa CharSequence, por lo tanto, puede pasar un String a un CharSequence, SIN EMBARGO, no puede pasar un CharSequence a un String, ya que CharSequence no implementa el String. TAMBIÉN, en Android, el EditText.getText()método devuelve un Editable, que también implementa CharSequence y se puede pasar fácilmente a uno, aunque no fácilmente a una Cadena. ¡CharSequence se encarga de todo!

Impostor
fuente
77
Puedes hacerlocharSequence.toString()
Jorge Fuentes González
1
@jorge: Excepto que será relativamente ineficiente si la secuencia es mutable (o por alguna razón requiere una copia de los caracteres para hacer una cadena inmutable).
Lawrence Dol
muy buena explicación ..!
majurageerthan
23

En general, el uso de una interfaz le permite variar la implementación con un daño colateral mínimo. Aunque java.lang.String son súper populares, es posible que en ciertos contextos uno quiera usar otra implementación. Al construir la API alrededor de CharSequences en lugar de Strings, el código le da a uno la oportunidad de hacerlo.

Itay Maman
fuente
8

Esto es casi seguro razones de rendimiento. Por ejemplo, imagine un analizador que pasa por un ByteBuffer de 500k que contiene cadenas.

Hay 3 enfoques para devolver el contenido de la cadena:

  1. Cree una Cadena [] en tiempo de análisis, un carácter a la vez. Esto llevará una cantidad de tiempo notable. Podemos usar == en lugar de .equals para comparar referencias en caché.

  2. Construya un int [] con desplazamientos en tiempo de análisis, luego construya String dinámicamente cuando ocurra un get (). Cada cadena será un objeto nuevo, por lo que no se almacenarán en caché los valores devueltos y se utilizará ==

  3. Cree una secuencia de caracteres [] en tiempo de análisis. Como no se almacenan datos nuevos (aparte de las compensaciones en el búfer de bytes), el análisis es mucho menor que el n. ° 1. En el momento oportuno, no necesitamos construir una Cadena, por lo que obtener un rendimiento es igual al # 1 (mucho mejor que el # 2), ya que solo estamos devolviendo una referencia a un objeto existente.

Además de las ganancias de procesamiento que obtiene usando CharSequence, también reduce la huella de memoria al no duplicar datos. Por ejemplo, si tiene un búfer que contiene 3 párrafos de texto y desea devolver los 3 o un solo párrafo, necesita 4 cadenas para representar esto. Usando CharSequence solo necesita 1 búfer con los datos y 4 instancias de una implementación de CharSequence que rastrea el inicio y la duración.

Phil Lello
fuente
66
referencias por favor. Suena como adivinar al azar lo que está sucediendo. Además, no encuentro su argumento válido. en primer lugar, uno simplemente puede almacenar el bytebuffer de 500k como una cadena y simplemente devolver subcadenas, lo cual es una locura rápida y mucho más común.
kritzikratzi
66
@kritzikratzi: a partir de JDK7, la subcadena en String ya no comparte la matriz subyacente y no es "locamente rápida". Lleva O (N) tiempo en la longitud de la subcadena y genera una copia de los caracteres subyacentes cada vez que lo llama (por lo tanto, mucha basura).
BeeOnRope
@kritzikratzi Creo que la razón del cambio es que, si no se hiciera la copia, la Cadena original se conservaría durante toda la vida de todas las subcadenas. Dado que las subcadenas generalmente son solo pequeñas partes del original y podrían durar indefinidamente dependiendo de cómo se usen, esto a menudo generaría aún más basura si las subcadenas estuvieran en uso durante mucho más tiempo que la cadena original. Una alternativa interesante podría ser determinar si copiar o no en función de la proporción de la subcadena al tamaño de la cadena principal, pero tendrías que rodar tu propia CharSequenceimplementación para eso.
JAB
1
Quizás he perdido el punto, pero esta respuesta no tiene sentido. A CharSequencees una interfaz : por definición, no tiene ninguno de los detalles de implementación que discute porque no tiene implementación propia. A Stringes una de varias clases concretas que implementa la CharSequenceinterfaz. Entonces a String es a CharSequence. Puede comparar los detalles de rendimiento de Stringvs StringBuffervs StringBuilder, pero no CharSequence. Escribir "ganancias de procesamiento que obtienes usando CharSequence" no tiene sentido.
Basil Bourque
7

Un problema que SÍ surge en el código práctico de Android es que compararlos con CharSequence.equals es válido pero no necesariamente funciona según lo previsto.

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

La comparación debe hacerse por

("OK").contentEquals(t.GetText()); 
Cripta
fuente
5

CharSequence

A CharSequencees una interfaz, no una clase real. Una interfaz es solo un conjunto de reglas (métodos) que una clase debe contener si implementa la interfaz. En Android, a CharSequencees un paraguas para varios tipos de cadenas de texto. Estos son algunos de los más comunes:

(Puede leer más sobre las diferencias entre estos aquí ).

Si tiene un CharSequenceobjeto, entonces en realidad es un objeto de una de las clases que implementa CharSequence. Por ejemplo:

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

El beneficio de tener un tipo general de paraguas CharSequencees que puede manejar múltiples tipos con un solo método. Por ejemplo, si tengo un método que toma a CharSequencecomo parámetro, podría pasar a Stringo a SpannableStringBuildery manejaría cualquiera de ellos.

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

Cuerda

Se podría decir que a Stringes solo un tipo de CharSequence. Sin embargo, a diferencia de CharSequence, es una clase real, por lo que puede hacer objetos a partir de ella. Entonces podrías hacer esto:

String myString = new String();

pero no puedes hacer esto:

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

Dado que CharSequencees solo una lista de reglas que se Stringajustan, puede hacer esto:

CharSequence myString = new String();

Eso significa que cada vez que un método solicita un CharSequence, está bien darle un String.

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

// ...

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

Sin embargo, lo contrario no es cierto. Si el método toma un Stringparámetro, no puede pasarle algo que generalmente se conoce como a CharSequence, porque en realidad podría ser un SpannableStringu otro tipo de CharSequence.

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

// ...

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

CharSequencees una interfaz y la Stringimplementa. Puede crear una instancia de Stringpero no puede hacerlo CharSequenceporque es una interfaz. Puede encontrar otras implementaciones CharSequenceen el sitio web oficial de Java.

Xiaogang
fuente
-4

CharSequence es una secuencia legible de valores char que implementa String. tiene 4 métodos

  1. charAt (int int)
  2. longitud()
  3. subSecuencia (int inicio, int final)
  4. Encadenar()

Consulte la documentación Documentación de CharSequence

Srinivas Thammanaboina
fuente
66
CharSequenceno implementa String. Sin embargo, lo contrario es cierto.
seh
1
Elimine esta respuesta sin sentido
J. Doe