Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Если задача — единожды вставить элемент в середину списка, то, по сути, не важно, какая реализация списка используется — обе операции будут O(n).
ну это сложно отличать скорость от трудоемкости, особенно когда это одно и тоже
Есть следующий вопрос. Правильно ли я понимаю следующее. Сложности, которыми вы оперируете, находятся в разных координатных системах. Т.е., если у ArrayList и LinkedList параметр O(1) равен разному физическому времени, поэтому одинаковые сложности O(n) — не означают, что время вставки будет одинаково. Оно может ощутимо различаться.
А если мы пробегаем не весь linkedList, а как максимум его половину — сложность точно будет O(n)? Может, O(n/2) или O(n)/2? Мы же пробегаем половину.
Вообще, вопрос был с подвохом. ИМХО, если он звучал именно так, как вы написали — вы вообще не по той ветке пошли. Надо было сказать, что вставка по индексу и вставка по итератору отличаются.
Поскольку в ArrayList при этом происходит сдвиг элементов, каждое такое удаление будет сопровождаться выделением памяти под новый массив и копирование в него оставшихся элементов из старого.
Предполагаю, её использование оправдано в ситуациях, когда надо часто удалять элементы из середины списка.
Какой содержимое листа Integer,String, Long или может что-то свое? Или считались только заголовки?
Хотя, я думаю, разница в скорости между ОЗУ и кэшем слишком мала
обращение к памяти — это сотни наносекунд
Вы ошиблись почти на порядок: один промах мимо кеша в память стоит порядка 500 инструкций.
Для ArrayList поиск элемента по индексу не составляет труда, так как элементы списка находятся в массиве. Алгоритмическая сложность составляет O(1). В случае LinkedList придётся последовательно перебрать все элементы, пока не доберёмся до нужного элемента. Сложность будет O(n). Вставка в ArrayList связана со сдвигом всех элементов, находящихся после точки вставки, поэтому алгоритмическая сложность этой операции O(n). В LinkedList вставка заключается в создании нового связующего объекта и установки ссылок на него у соседних элементов. Сложность O(1). В сумме сложность вставки в середину у ArrayList и у LinkedList получается одинаковая — O(n).
Если задача — вставлять элементы в середину в цикле (как делаете это вы в своем тесте), то логично использовать для этой цели LinkedList через ListIterator, так как в этом случае каждая вставка будет O(1).
System.arraycopy — операция нативная, так что выполняется практически за O(1) почти вне зависимости от размера массива.new Object[n], по крайней мере до размера массива 256, выполняется за время O(n)…System.arraycopy, оказывается быстрее, чем итерирование по связанному списку. Удвоение размеров ArrayList'а оказывается медленнее итерирования по связанному списку — вот что странно.new Object[n], что было бы логично, а Array.newInstance, через использование Arrays.copyOf. Получается, главный тормоз — Array.newInstance, который оказывается медленнее операции, выполняемой за O(n).Array.newInstance вместо new Object[]: неоправдавшийся расчёт на интринсик Arrays.copyOf.Вы неправильно используете big-O нотацию, O(5) это то же самое, что и O(1) *по определению*.
Не очень понятно, почему переход по ссылкам должен быть медленный, JIT превратит это в пару машинных инструкций.
В общем, ИМХО реализация в Java совсем непричем.
Не очень понятно, почему переход по ссылкам должен быть медленный, JIT превратит это в пару машинных инструкций.Потому что одно дело когда работа идет с непрерывном куском памяти, там переходы быстрые и короткие, другое дело длинный переход в совсем другую страницу памяти.
calloc не делает обнуления, Он из /dev/zero (или аналога на Windows) странички прихватывает. Их потом «по запросу» обнуляют и выдают, сразу после аллокации там одна и та же страничка растиражирована много раз.O(n). Исключения составляют случаи, когда JVM знает, что обнулять не надо, например, в Arrays.copyOf или Object.clone.ZeroTLAB.O(1) из O(n).n. Пусть поток main у тебя сверхбыстрый, но если фоновых вычислений ассимптотически больше, это значит, что в какой-то момент main просто будет вынужден остановиться, чтобы дождаться другого потока.Судя по perfomance тестам в инете от 2 до 20 раз более быстрый чем ручное копирование массивов.
ArrayFill.copyBytes 1300.313 ± 13.482 ns/op
ArrayFill.manualCopyBytes 1477.442 ± 13.030 ns/op
…
Use System.arrayCopy, it is better than your handrolled loop. But surprisingly not hugely better, hmmm.
Даже если массив из 2-х элементов копируется в 1.001 раза медленней, чем массив из 1 элемента. это уже O(n).
Вставка в середину: ArrayList против LinkedList