CharSequence VS String на Java?


421

Програмування в Android, більшість значень тексту очікується в CharSequence.

Чому так? Яка користь і які основні наслідки використання CharSequenceбільш String?

Які основні відмінності та які проблеми очікуються під час їх використання та перетворення з одного на інший?


1
Кращі відповіді можна знайти в Точній різниці між CharSequence і String in java
Сурагч

Відповіді:


343

Струни - це CharSequences , тому ви можете просто використовувати 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інтерфейсом не потрібно двох s , але вони можуть, якщо захочуть. ІМХО CharSequenceслід обмежувати введеннями та використовувати менше для повернення типів.
Бретт Райан

58

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

Ти сказав:

перетворення від одного до іншого

Перетворення з String.

  • Кожен Stringоб’єкт - це CharSequence.
  • Кожен CharSequenceможе виробляти String. Дзвінок CharSequence::toString. Якщо це CharSequenceстанеться a String, метод повертає посилання на власний об'єкт.

Іншими словами, кожен Stringє a CharSequence, але не кожен CharSequenceє a String.

Програмування на інтерфейс

Програмування в Android, більшість текстових значень очікується в CharSequence.

Чому так? Яка вигода та які основні наслідки використання CharSequence над String?

Взагалі програмування на інтерфейс краще, ніж програмування на конкретні класи. Це дає гнучкість, тому ми можемо перемикатися між конкретними реалізаціями певного інтерфейсу, не порушуючи іншого коду.

Розробляючи API, який використовуватимуться різними програмістами в різних ситуаціях, напишіть свій код, щоб надати та взяти найбільш загальні можливі інтерфейси. Це дає програмісту, що викликає, свободу використовувати різні реалізації цього інтерфейсу, залежно від того, яка реалізація найкраща для їх конкретного контексту.

Наприклад, подивіться на Framework Collections Java . Якщо API дає або приймає впорядковану колекцію об'єктів, оголошувати методи, використовуючи Listзамість ArrayList, LinkedListабо будь-який інший реалізації третьою стороною в List.

Коли ви пишете маленький швидкий і брудний метод, який буде використовуватися лише вашим кодом в одному конкретному місці, на відміну від написання API, який буде використовуватися в декількох місцях, вам не потрібно заважати використовувати більш загальний інтерфейс, а не конкретний конкретний клас. Але навіть тоді не шкода використовувати найзагальніший інтерфейс, який ви можете.

Які основні відмінності та які проблеми очікуються під час їх використання,

  • Якщо Stringви знаєте, у вас є один фрагмент тексту, повністю в пам'яті, і він незмінний.
  • З а CharSequence, ви не знаєте, якими можуть бути конкретні особливості конкретної реалізації.

CharSequenceОб'єкт може являти собою величезний шматок тексту, і , отже , має значення пам'яті. Або може бути багато фрагментів тексту, які відслідковуються окремо, які потрібно буде з'єднати під час дзвінка toString, а отже, є проблеми з продуктивністю. Реалізація може навіть витягувати текст із віддаленої служби, а тому має наслідки затримки.

і перетворення від одного до іншого?

Як правило, ви не перетворюєте туди-сюди. А String - це CharSequence. Якщо ваш метод оголошує, що він займає а CharSequence, викликаючий програміст може передавати Stringоб'єкт, або може передавати щось інше, наприклад, StringBufferабо StringBuilder. Код вашого методу просто використовуватиме все, що передається, викликаючи будь-який із CharSequenceметодів.

Найближче до конвертації ви отримаєте, якщо ваш код отримує CharSequenceі ви знаєте, що вам потрібно String. Можливо, ви поєднуєте старий код, записаний у Stringклас, а не записаний в CharSequenceінтерфейс. Або, можливо, ваш код буде інтенсивно працювати з текстом, наприклад, повторно циклічно чи аналізуючи. У цьому випадку ви хочете виконати будь-який можливий удар на виставу лише один раз, тому ви зателефонуєте toStringна фронт. Потім продовжуйте свою роботу, використовуючи те, що ви знаєте, як єдиний фрагмент тексту повністю в пам'яті.

Закручена історія

Зверніть увагу на коментарі, прийняті до прийнятого відповіді . CharSequenceІнтерфейс був переобладнаний на існуючих структури класу, так що є деякі важливі тонкощі ( equals()& hashCode()). Зауважте, що різні версії Java (1, 2, 4 і 5), позначені на класах / інтерфейсах, досить непомітні за ці роки. Ідеально CharSequenceбуло б на місці з самого початку, але таке життя.

Приведена нижче схема мого класу може допомогти вам побачити велику картину типів рядків на Java 7/8. Я не впевнений, чи всі вони є в Android, але загальний контекст може все-таки виявитися корисним для вас.

схема різних класів та інтерфейсів, пов'язаних із рядками


2
Ви склали цю схему самостійно? цікаво, чи існує каталог цих діаграм для різних структур даних.
користувач171943

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: Крім того, це буде відносно неефективно, якщо послідовність є змінною (або з будь-якої причини потрібна копія символів для того, щоб внести в незмінний рядок).
Лоуренс Дол

дуже приємне пояснення ..!
majurageerthan

23

Взагалі використання інтерфейсу дозволяє варіювати реалізацію з мінімальними заставою. Хоча java.lang.String дуже популярний, можливо, у певних контекстах можна використовувати іншу реалізацію. Створюючи API навколо CharSequences, а не Strings, код дає можливість зробити це.


8

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

Існує 3 підходи до повернення рядкового вмісту:

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

  2. Побудуйте int [] із зрушеннями під час розбору, а потім динамічно будуйте String, коли відбувається get (). Кожен рядок буде новим об'єктом, тому не кешуйте повернені значення та використовуйте ==

  3. Побудуйте CharSequence [] під час розбору. Оскільки ніяких нових даних не зберігається (крім зсувів у байтовому буфері), синтаксичний аналіз значно нижчий, ніж №1. У той час нам не потрібно будувати String, тому результативність роботи дорівнює №1 (набагато краще, ніж №2), оскільки ми повертаємо лише посилання на існуючий об’єкт.

Окрім прибутків від обробки, які ви отримуєте за допомогою CharSequence, ви також зменшуєте слід пам'яті, не дублюючи дані. Наприклад, якщо у вас є буфер, що містить 3 абзаци тексту, і ви хочете повернути або всі 3, або один абзац, вам потрібні 4 рядки для представлення цього. Використовуючи CharSequence, вам потрібен лише 1 буфер із даними та 4 екземпляри реалізації CharSequence, які відстежують початок та довжину.


6
посилання пл. звучить як випадкове відгадування того, що відбувається. також я не вважаю ваш аргумент дійсним. можна просто зберегти байт-буфер 500k у якості рядка в першу чергу і просто повернути підрядки, що шалено швидко, і набагато більш поширене.
kritzikratzi

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

@kritzikratzi Я вважаю, що причина зміни полягає в тому, що, якби копія не була зроблена, оригінальний рядок зберігався б протягом усього періоду. Зважаючи на те, що підрядки, як правило, є лише невеликими частинами оригіналу і можуть тривати нескінченно залежно від способу їх використання, це часто призводить до ще більше сміття, якби підрядки використовувалися набагато довше, ніж початковий рядок. Цікавим альтом може бути визначення того, потрібно чи ні копіювати на основі співвідношення підрядок до розміру батьківського рядка, але для цього вам доведеться прокрутити власну CharSequenceреалізацію.
JAB

1
Можливо, я пропустив думку, але ця відповідь - це нісенітниця. A CharSequence- це інтерфейс - за визначенням він не має жодної деталі реалізації, яку ви обговорюєте, оскільки не має власної реалізації. A String- один з декількох конкретних класів, який реалізує CharSequenceінтерфейс. Отже String , a 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

CharSequence

A CharSequence- це інтерфейс, а не власне клас. Інтерфейс - це лише набір правил (методів), які повинен містити клас, якщо він реалізує інтерфейс. В Android a CharSequence- це парасолька для різних типів текстових рядків. Ось декілька поширених:

  • String (незмінний текст без проміжків стилізації)
  • StringBuilder (змінений текст без проміжків стилізації)
  • SpannableString (незмінний текст зі стильовими прольотами)
  • SpannableStringBuilder (текст, що змінюється, із прольотами стилів)

(Про відмінності між ними ви можете прочитати тут .)

Якщо у вас є CharSequenceоб'єкт, то він фактично є об'єктом одного з класів, який реалізує CharSequence. Наприклад:

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

Перевага від загального типу парасольки типу CharSequenceполягає в тому, що ви можете обробляти кілька типів одним методом. Наприклад, якщо у мене є метод, який бере CharSequenceпараметр a, я міг би передати 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, добре його надати String.

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

// ...

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

Однак навпаки не вірно. Якщо метод приймає Stringпараметр, ви не можете передавати йому щось, що загальновідомо є a CharSequence, оскільки це насправді може бути 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 індекс)
  2. довжина ()
  3. subSequence (int start, int end)
  4. toString ()

Будь ласка, зверніться до документації CharSequence


6
CharSequenceне реалізує String. Хоча зворотна правда.
seh

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