← Назад к вопросам

Какая внутренняя структура у строки?

1.0 Junior🔥 161 комментариев
#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Внутренняя структура String в Java

String — один из самых используемых классов, и его структура значительно изменилась за историю Java. Важно понимать эти изменения для оптимизации производительности.

История эволюции String (важно!)

Java 8 и ранее:

public final class String {
    private final char[] value;  // Массив char (16-бит символы)
    private final int offset;
    private final int count;
    private int hash;
}
// Проблема: даже для ASCII текста используются 16 бит на символ

Java 9+ (String Compression):

public final class String {
    private final byte[] value;  // Массив byte!
    private final byte coder;    // 0 = Latin1 (1 байт), 1 = UTF-16 (2 байта)
    private int hash;
}

Как это работает в Java 9+

String ascii = "Hello";    // coder=0, value={0x48,0x65,0x6C,0x6C,0x6F}
                            // 5 байт вместо 10

String unicode = "Привет"; // coder=1, value={...UTF-16 encoded...}
                            // 12 байт (6 символов * 2 байта)

String mixed = "H привет"; // coder=1 (нужен UTF-16 для всей строки)
                            // 16 байт

Структура в памяти (объект String)

Объект String:
┌─────────────────────────────────────┐
│ Object Header (16 байт)             │
│ - Mark Word (8 байт)                │
│ - Klass Pointer (4 байт)            │
│ - Padding (4 байта на 64-bit JVM)   │
├─────────────────────────────────────┤
│ byte[] value (8 байт ссылка)        │
│ byte coder (1 байт: 0 или 1)        │
│ int hash (4 байта, 0 если не считан)│
│ Padding (3 байта выравнивание)      │
├─────────────────────────────────────┤
│ Внутри value[]:                     │
│ Object Header (16 байт)             │
│ Array Length (4 байта)              │
│ byte[] данные                       │
└─────────────────────────────────────┘

Размер String на примерах

// Строка "Hi" (ASCII, Java 9+)
String str = "Hi";
// String объект: 24 байта
// byte[] value: 16 + 2 = 18 байт
// Итого: ~42 байта

// Строка "" (пустая)
String empty = "";
// String объект: 24 байта
// byte[] value: 16 + 0 = 16 байт (пустой массив)
// Итого: ~40 байт

String Pool (Intern)

String str1 = "Hello";     // Новая строка в String Pool
String str2 = "Hello";     // Ссылка на ту же строку из Pool
String str3 = new String("Hello"); // Новая строка в Heap

str1 == str2; // true (одна и та же ссылка)
str1 == str3; // false (разные объекты)
str1.equals(str3); // true (одинаковое содержимое)

str3.intern() == str1; // true (str3 добавлен в Pool)

Immutability и как это работает

public final class String implements CharSequence, Comparable<String>, Serializable {
    private final byte[] value; // final - не может быть переделано!
    private final byte coder;   // final
    // ...
}

// Попытка изменить
String str = "Hello";
// str.value[0] = 'J'; // ОШИБКА компиляции - private final

// Вместо этого создаётся новая String
String str2 = str + " World"; // Новая строка

Механизм конкатенации строк

До Java 9:

String result = "Hello" + " " + "World"; // Компилятор → StringBuilder
// Более 20 лет это так работает!

Java 9+ (StringBuilder по умолчанию):

String result = "Hello" + " " + "World";
// Компилируется в:
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" ");
sb.append("World");
result = sb.toString();

String Template (Java 21 Preview):

String name = "World";
String result = "Hello, \{name}!"; // Более эффективно

Hashcode кэширование

public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        // Кэшируем хеш при первом запросе
        hash = h = isLatin1() ? 
            StringLatin1.hashCode(value) :
            StringUTF16.hashCode(value);
    }
    return h;
}

// Почему это важно:
String key = "myKey";
int h1 = key.hashCode(); // Вычисляется один раз
int h2 = key.hashCode(); // Просто возвращает кэшированное значение

Производительность String операций

// ❌ МЕДЛЕННО - создаст 1000 строк
String result = "";
for (int i = 0; i < 1000; i++) {
    result += i; // O(n²) - каждая итерация копирует всю строку
}

// ✅ БЫСТРО - использует StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(i);
}
String result = sb.toString();

Substring (важное изменение)

Java 8: Substring делил массив (offset + count)

String big = "0123456789";
String sub = big.substring(5); // Делил массив, экономил память? НЕТ!
// Реально хранит весь char[] и offset/count

Java 9+: Substring создаёт новый массив

String big = "0123456789";
String sub = big.substring(5); // Создаёт новый byte[] с 5 символами
// Правильно - если big удалён, sub может быть GC

Важные выводы

  • String использует byte[] с coder для оптимизации памяти (Java 9+)
  • String immutable - изменение создаёт новую строку
  • String Pool экономит память для часто используемых строк
  • Hashcode кэшируется после первого запроса
  • Конкатенация через + автоматически переводится в StringBuilder
  • Для циклических конкатенаций всегда используй StringBuilder
  • Substring с Java 9 создаёт новый массив (нет утечек памяти)
Какая внутренняя структура у строки? | PrepBro