Почему класс StringBuffer использует Array в качестве базовой структуры данных вместо LinkedList? - PullRequest
0 голосов
/ 10 сентября 2018

Классы StringBuffer / StringBuilder в Java в основном используются для изменения значений String без необходимости каждый раз инициализировать новый объект String.

Есть ли конкретная причина, по которой она не использует LinkedList вместо Char Array в качестве базовой структуры данных?

Вставка символа в массив всегда приводит к O (n) времени, чтобы скопировать все элементы в следующий индекс, где, как это было бы сделано за O (1) время в случае LinkedList.

1 Ответ

0 голосов
/ 10 сентября 2018

Случайный доступ

StringBuilder имеет операции произвольного доступа, такие как charAt или substring, которые будут чрезвычайно медленными для связанного списка.

1008 * вставок * На самом деле списки массивов не намного медленнее, чем связанные списки, даже если речь идет о других операциях, таких как вставка. Обычно StringBuilder не будет использоваться для создания строк из миллиона символов, поэтому маловероятно, что нам потребуется слишком много раз изменять размер буфера. В конце Я должен исправить, что вставка в конце всегда требует O(n) копию элементов. Наихудший случай действительно O(n), но амортизируемая сложность O(1), потому что мы не просто выделяем один элемент за раз. Когда массив не достаточно велик, чтобы сделать другую вставку, большинство реализаций удваивает размер массива. Посередине Вставки в середине всегда требуют копирования элементов справа от вставки, так что да, это довольно медленно, но это не типичный вариант использования для StringBuilder, где большинство вставок происходит в конце. Кроме того, связанные списки имеют одинаковую среднюю сложность при вставках в середине, поскольку сначала они должны достичь правого узла путем итерации списка. локальность данных

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

след памяти

Поскольку мы удваиваем размер массива при каждом изменении размера, динамические массивы могут иметь довольно большой объем памяти (но, по крайней мере, нам не нужно будет копировать элементы слишком часто). Связанные списки также имеют довольно большой объем памяти, поскольку для них требуется одна дополнительная ссылка и указатель для каждого элемента, а элементы компактно хранятся в массиве. В среднем я бы сказал, что типичный список массивов будет иметь меньший объем памяти, чем связанный список, но я могу ошибаться. Это особенно верно для примитивных типов - таких как char - потому что связанные списки требуют объектов-оболочек (по крайней мере, в Java, так как нет указателей), тогда как мы можем использовать гораздо более компактные примитивные массивы.

Последние заметки

Наконец, я использовал StringBuilder в своем ответе вместо StringBuffer, потому что это рекомендуемая реализация для большинства случаев использования. StringBuffer предпочтительнее только тогда, когда жесткое требование безопасности потока. В противном случае StringBuilder будет иметь лучшую производительность.

PS: Наиболее заметная структура данных Python - list, и угадайте, что ... она реализована с помощью динамического массива! Масштабируемые массивы очень часто являются лучшим выбором, чем связанные списки. Единственный случай, когда связанный список является заметно более производительным, - это когда приложение фокусируется на элементах, близких к заголовку списка, и часто делает вставки или удаления в этой области.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...