CharSequence VS String в Java?


421

Программирование в Android, большинство текстовых значений ожидается в CharSequence.

Это почему? В чем выгода, и каковы основные последствия использования CharSequenceболее String?

Каковы основные различия и какие проблемы ожидаются при их использовании и преобразовании из одного в другое?


Ответы:


343

Строки - это CharSequence , поэтому вы можете просто использовать Strings и не беспокоиться. Android просто пытается быть полезным, позволяя вам также указывать другие объекты CharSequence, например StringBuffers.


94
За исключением случаев, когда Android передает мне CharSequence в обратном вызове, и мне нужен String - вызов charSeq.toString ().
Мартин Коничек

100
Но имейте в виду , это предостережение от CharSequenceJavadoc: Этот интерфейс не уточняют общие договоров equalsи hashCodeметодов. Поэтому результат сравнения двух реализуемых объектов, CharSequenceкак правило, не определен . Каждый объект может быть реализован отдельным классом, и нет никакой гарантии, что каждый класс будет способен проверять свои экземпляры на равенство с другими. Поэтому нецелесообразно использовать произвольные CharSequenceэкземпляры в качестве элементов в наборе или в качестве ключей на карте.
Тревор Робинсон

3
@Pacerier: я думаю, что это скорее практическое ограничение. CharSequenceбыл модифицирован в JDK 1.4 для введения общего интерфейса с ограниченными целями для объектов, содержащих последовательности символов. Некоторые из этих объектов содержат другое состояние, поэтому может не иметь смысла определять Object.equalsкак «содержит ту же последовательность символов». CharBufferНапример, NIO предоставляет только символы между своими positionи limitкак CharSequence, несмотря на то , что потенциально может содержать много других символов.
Тревор Робинсон

6
@TrevorRobinson, поэтому дизайн ошибка оказывает equals/ hashCodeна Objectв первую очередь ....
Pacerier

4
@Pacerier: ИМХО Существует не дизайн ошибки в любом Objectили CharSequence, интерфейс не требуется для обеспечения равенства здравомыслия между реализациями. Нет двух Collectionобязательных для обеспечения равенства между Collectionинтерфейсом, но они могут, если захотят. ИМХО CharSequenceдолжно быть ограничено входами и меньше использоваться для типов возврата.
Бретт Райан

58

CharSequence= интерфейс
String= конкретная реализация

Вы сказали:

преобразование из одного в другое

Там нет конвертации из String.

  • Каждый Stringобъект являетсяCharSequence .
  • Каждый CharSequenceможет изготовить String. Вызов CharSequence::toString. Если CharSequenceслучаем является a String, то метод возвращает ссылку на свой собственный объект.

Другими словами, каждый Stringявляется CharSequence, но не каждый CharSequenceявляется String.

Программирование на интерфейс

При программировании на Android большинство текстовых значений ожидаются в CharSequence.

Это почему? В чем выгода и каковы основные последствия использования CharSequence вместо String?

Как правило, программирование для интерфейса лучше, чем программирование для конкретных классов. Это обеспечивает гибкость, поэтому мы можем переключаться между конкретными реализациями определенного интерфейса, не нарушая другой код.

При разработке API который будет использоваться различными программистами в различных ситуациях, напишите свой код, чтобы предоставить и использовать самые общие возможные интерфейсы. Это дает вызывающему программисту свободу использовать различные реализации этого интерфейса, независимо от того, какая реализация лучше всего подходит для их конкретного контекста.

Например, посмотрите на Java Collections Framework . Если API дает или принимает упорядоченную коллекцию объектов, объявлять методы, используя Listвместо ArrayList, LinkedListили любой другой реализации третьей стороной в List.

При написании быстрого и грязного метода, который будет использоваться только вашим кодом в одном конкретном месте, в отличие от написания API, который будет использоваться в нескольких местах, вам не нужно беспокоиться об использовании более общего интерфейса, а не конкретного конкретного учебный класс. Но даже тогда было бы больно использовать самый общий интерфейс, какой только можно.

Каковы основные различия и какие проблемы ожидаются при их использовании,

  • С помощью Stringвы знаете, что у вас есть один фрагмент текста, полностью в памяти, и является неизменным.
  • С a CharSequenceвы не знаете, какими могут быть конкретные особенности конкретной реализации.

CharSequenceОбъект может представлять собой огромный кусок текста, и , следовательно , имеет значение памяти. Или может быть много фрагментов текста, отслеживаемых отдельно, которые нужно будет сшивать вместе, когда вы звоните toString, и поэтому возникают проблемы с производительностью. Реализация может даже извлекать текст из удаленной службы, и, следовательно, имеет значение задержки.

и конвертировать из одного в другой?

Как правило, вы не будете конвертировать туда и обратно. String ЯвляетсяCharSequence . Если ваш метод объявляет, что он принимает a CharSequence, вызывающий программист может передать Stringобъект или передать что-то еще, например, StringBufferили StringBuilder. Код вашего метода будет просто использовать все, что передано, вызывая любой из CharSequenceметодов.

Самое близкое, что вы получили бы к конвертации, это если ваш код получит, CharSequenceи вы знаете, что вам нужно String. Возможно, вы взаимодействуете со старым кодом, написанным для Stringкласса, а не для CharSequenceинтерфейса. Или, возможно, ваш код будет интенсивно работать с текстом, например, многократно повторять циклы или иным образом анализировать. В этом случае вы хотите получить любой возможный удар по производительности только один раз, так что вы звоните toStringпрямо. Затем приступайте к работе, используя то, что, как вы знаете, представляет собой один фрагмент текста полностью в памяти.

Искаженная история

Обратите внимание на комментарии к принятому ответу . CharSequenceИнтерфейс был переоборудован на существующих структуры класса, так что есть некоторые важные тонкости ( equals()& hashCode()). Обратите внимание на различные версии Java (1, 2, 4 и 5), помеченные на классах / интерфейсах - с течением времени они немного изменились. В идеале CharSequenceбыло бы на месте с самого начала, но такова жизнь.

Моя диаграмма классов ниже может помочь вам увидеть общую картину типов строк в Java 7/8. Я не уверен, что все это присутствует в Android, но общий контекст может оказаться полезным для вас.

схема различных классов и интерфейсов, связанных со строками


2
Вы сделали эту диаграмму самостоятельно? интересно, есть ли каталог этих диаграмм для разных структур данных.
user171943

4
@ user171943 Созданный мной, созданный вручную с помощью приложения OmniGraffle от OmniGroup.
Василий Бурк

37

Я считаю, что лучше всего использовать CharSequence. Причина в том, что String реализует CharSequence, поэтому вы можете передать String в CharSequence, ОДНАКО вы не можете передать CharSequence в String, так как CharSequence не реализует String. Кроме того, в Android EditText.getText()метод возвращает Editable, который также реализует CharSequence и может быть легко передан в один, а не просто в String. CharSequence обрабатывает все!


7
Вы можете сделатьcharSequence.toString()
Хорхе Фуэнтес Гонсалес

1
@jorge: за исключением того, что это будет относительно неэффективно, если последовательность является изменяемой (или по какой-либо причине требуется копия символов для преобразования в неизменяемую строку).
Лоуренс Дол

очень хорошее объяснение ..!
27

23

В целом использование интерфейса позволяет варьировать реализацию с минимальным побочным ущербом. Несмотря на то, что java.lang.String очень популярны, возможно, что в определенных контекстах вы захотите использовать другую реализацию. Путем построения API вокруг CharSequence, а не Strings, код дает возможность сделать это.


8

Это почти наверняка из соображений производительности. Например, представьте анализатор, который проходит через 500k ByteBuffer, содержащий строки.

Есть 3 подхода к возвращению содержимого строки:

  1. Создайте строку [] во время разбора, по одному символу за раз. Это займет заметное количество времени. Мы можем использовать == вместо .equals для сравнения кэшированных ссылок.

  2. Создайте int [] со смещениями во время разбора, затем динамически создайте String, когда произойдет get (). Каждая строка будет новым объектом, поэтому нет кэширования возвращаемых значений и использования ==

  3. Создайте CharSequence [] во время разбора. Поскольку никакие новые данные не сохраняются (кроме смещений в байтовом буфере), синтаксический анализ намного ниже, чем # 1. Во время получения нам не нужно создавать String, поэтому производительность get равна # 1 (намного лучше, чем # 2), поскольку мы только возвращаем ссылку на существующий объект.

В дополнение к преимуществам обработки, которые вы получаете с помощью CharSequence, вы также уменьшаете объем памяти, не дублируя данные. Например, если у вас есть буфер, содержащий 3 абзаца текста, и вы хотите вернуть все 3 или один абзац, вам нужно 4 строки для представления этого. При использовании CharSequence вам нужен только 1 буфер с данными и 4 экземпляра реализации CharSequence, которая отслеживает начало и длину.


6
ссылки плз. звучит как случайное предположение, что происходит. Также я не считаю ваш аргумент действительным. в первую очередь можно просто сохранить байтовый буфер 500 КБ в виде строки и просто вернуть подстроки, что очень быстро и гораздо более распространено.
kritzikratzi

6
@kritzikratzi - начиная с JDK7, подстрока в String больше не разделяет базовый массив и не является "сумасшедшей быстрой". Это занимает O (N) времени в длине подстроки и генерирует копию нижележащих символов каждый раз, когда вы ее вызываете (так много мусора).
BeeOnRope

@kritzikratzi Я полагаю, что причина изменения заключается в том, что, если бы копия не была сделана, исходная строка сохранялась бы в течение всего времени жизни всех подстрок. Учитывая, что подстроки обычно являются только небольшими частями оригинала и могут длиться бесконечно в зависимости от того, как они используются, это может привести к еще большему мусору, если подстроки использовались намного дольше, чем исходная строка. Интересный альтернативный вариант может заключаться в том, чтобы определить, следует ли копировать, основываясь на соотношении подстроки к размеру родительской строки, но для этого вам придется свернуть собственную CharSequenceреализацию.
JAB

1
Возможно, я упустил момент, но этот ответ - чепуха. A CharSequence- это интерфейс - по определению он не имеет ни одной детали реализации, которую вы обсуждаете, потому что у него нет собственной реализации. String - это один из нескольких конкретных классов, который реализует CharSequenceинтерфейс. Таким образом, String этоCharSequence . Вы можете сравнить детали производительности Stringпротив StringBufferпротив 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

CharSequence

A CharSequence- это интерфейс, а не реальный класс. Интерфейс - это просто набор правил (методов), которые должен содержать класс, если он реализует интерфейс. В Android CharSequenceесть зонтик для различных типов текстовых строк. Вот некоторые из распространенных:

  • String (неизменный текст без разметки)
  • StringBuilder (изменяемый текст без интервалов стиля)
  • SpannableString (неизменный текст с пролетами стиля)
  • SpannableStringBuilder (изменяемый текст со стилями)

(Вы можете прочитать больше о различиях между этими здесь .)

Если у вас есть CharSequenceобъект, то это фактически объект одного из реализующих классов CharSequence. Например:

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

Преимущество использования общего типа зонтика в CharSequenceтом, что вы можете обрабатывать несколько типов одним методом. Например, если у меня есть метод, который принимает a CharSequenceв качестве параметра, я мог бы передать a Stringили a, SpannableStringBuilderи он обработал бы любой из них.

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

строка

Можно сказать, что это Stringвсего лишь один из видов CharSequence. Однако, в отличие от этого CharSequence, это фактический класс, поэтому вы можете создавать объекты из него. Так что вы можете сделать это:

String myString = new String();

но вы не можете сделать это:

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

Так как CharSequenceэто просто список правил String, которые можно выполнить, вы можете сделать это:

CharSequence myString = new String();

Это означает, что всякий раз, когда метод запрашивает a CharSequence, можно дать ему a 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реализует его. Вы можете создать экземпляр, Stringно вы не могли этого сделать, CharSequenceпоскольку это интерфейс. Вы можете найти другие реализации CharSequenceна официальном веб-сайте Java.


-4

CharSequence - читаемая последовательность значений char, которая реализует String. у него 4 метода

  1. charAt (int index)
  2. длина ()
  3. подпоследовательность (int start, int end)
  4. нанизывать()

Пожалуйста, обратитесь к документации CharSequence документации


6
CharSequenceне реализует String. Обратное, правда, правда.
SEH

1
Пожалуйста, удалите этот бессмысленный ответ
Дж. Доу
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.