Кодировки: основы.



Компактные строки - важное обновление в java 9. Чтобы лучше понять проблему и новое решение, разберём в этом посте отличия между кодировками. А в среду обсудим компактные строки.



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

1️⃣ ASCII-based

2️⃣ Unicode-based



1️⃣ ASCII

В большинстве ASCII кодировок символ занимает 1 байт и содержит число от 0 до 256. Первые 128 значений транслируются одинаково во всех кодировках:

0-31: управляющие последовательности - перенос строки, конец файла и т.д.

32-127: латинский алфавит, цифры, знаки препинания.



Специфичные символы языков отображаются на значения 128-255. Разные кодировки - разные наборы символов:

🔸Кириллица: ISO-8859-5, Windows-1251.

🔸Греческий алфавит: ISO-8859-7, Windows-1253.

🔸Исландские символы: OEM 861.



200 символ может стать Ш, Θ, È или чем-то ещё. Фраза «Я люблю Java” в другой кодировке отобразится как «Ď ŰîŃŰî Java».



Итого:

Один символ занимает 1 байт — компактно.

Всего 256 значений — нет места для эмодзи.

Неоднозначность трактовки.

Нельзя использовать украинский и норвежский язык в одном тексте.



2️⃣ Unicode

В основе преобразований лежит таблица с большинством символов, которые используются в мире. Но не со всеми, многие азиатские иероглифы записываются в памяти как комбинация 2-3 символов. Или эмоджи, например,👩‍❤️‍💋‍👩 - это сочетание 8 Unicode-кодов.



Изначально Unicode использовал 2 байта для записи символа и кодировку UCS-2. После 256 символов ASCII казалось, что диапазона 0-65536 хватит навсегда. Эта кодировка использовалась в ранних версиях java для типа char.



Со временем в таблицу добавилось больше символов и встал вопрос об эффективном хранении данных. Сегодня, чтобы однозначно представить символ юникода нужно 32 бита — так символы хранятся в UTF-32.

Прямое отображение.

Простота обработки.

Неэффективный расход памяти — если использовать только латиницу с кодами типа 0..045 и 0...077, ¾ памяти будет занято нулями.



На смену UCS-2 с фиксированными 2 байтами пришёл UTF-16 с переменной длиной. Если значение символа превышает 65536, то оно занимает 4 байта. Java перешла на UTF-16.



Кодировка UTF-8 тоже использует переменное количество памяти. Для каждого символа задаётся, сколько он занимает места — 1, 2, 3 или 4 байта.

Экономный расход памяти для латинских символов.

Обработка и поиск происходят чуть медленнее.

Отметка длины находится в первых 2 битах и уменьшает диапазон значений.



Если в структуре 2 или больше байтов, то одни процессоры быстрее считывают их в прямом порядке, а другие — в обратном. Поэтому у UTF-16 и UTF-32 могут быть приставки LE или BE: Little/big endian.



ASCII-кодировки не умеют читать символы больше 255, поэтому когда они встречают юникод-символы, то показывают и текст «я люблю Java” превращается в “? ????? Java”.