Сообщения

Сообщения за 2021

Когда использовать ArrayList, а когда LinkedList

Изображение
ArrayList с ArrayDeque предпочтительнее во многих других случаях использования, чем LinkedList. Если вы не уверены - начните с ArrayList. В ArrayList доступ к элементу занимает линейное время, а добавление элемента занимает время O(n) (худший случай). В LinkedList добавление элемента занимает O(n) времени, а доступ также занимает O(n) времени, но LinkedList использует больше памяти, чем ArrayList. LinkedList и ArrayList - две разные реализации интерфейса List. LinkedList реализует его с помощью двусвязного списка. ArrayList реализует его с помощью массива динамического изменения размера. Как и в случае стандартных операций со связанными списками и массивами, различные методы будут иметь разное время выполнения алгоритмов. Для LinkedList<E> get(int index) равно O(n) (в среднем n/4 шагов), но O(1), когда index = 0 или index = list.size() - 1 (в этом случае вы также можете использовать getFirst() и getLast()). Одно из основных преимуществ LinkedList<E> add(int index, E

Интерфейс Collection: коллекции представлений, неизменяемые коллекции

Изображение
Коллекции представлений Большинство коллекций управляют хранением содержащихся в них элементов. Напротив, коллекции представлений (view collections) сами по себе не хранят элементы, а вместо этого полагаются на вспомогательную коллекцию для хранения фактических элементов. Операции, которые не обрабатываются самой коллекцией представлений, делегируются вспомогательной коллекции. Примеры коллекций представлений включают коллекции оболочек, возвращаемые такими методами, как Collections.checkedCollection, Collections.synchronizedCollection и Collections.unmodifiableCollection. Другие примеры коллекций представлений включают коллекции, которые обеспечивают другое представление одних и тех же элементов, например, предоставляемое List.subList, NavigableSet.subSet или Map.entrySet. Любые изменения, внесенные в вспомогательную коллекцию, отображаются в коллекции представлений. Соответственно, любые изменения, внесенные в коллекцию представлений - если изменения разрешены - записываются в вспом

Интерфейс Collection в Java

Изображение
Корневой интерфейс в иерархии коллекции. Коллекция представляет собой группу объектов, называемых ее элементами. Некоторые коллекции позволяют дублировать элементы, а другие нет. Некоторые упорядочены, а другие нет. JDK не предоставляет прямых реализаций этого интерфейса: он предоставляет реализации более конкретных подинтерфейсов, таких как Set и List. Этот интерфейс обычно используется для передачи коллекций и управления ими там, где требуется максимальная универсальность. Пакеты или мультимножества (неупорядоченные коллекции, которые могут содержать повторяющиеся элементы) должны реализовывать этот интерфейс напрямую. Все универсальные классы реализации Collection (которые обычно реализуют Collection косвенно через один из ее подинтерфейсов) должны предоставлять два «стандартных» конструктора: конструктор void (без аргументов), который создает пустую коллекцию, и конструктор с одним аргументом типа Collection, которая создает новую коллекцию с теми же элементами, что и ее аргумент.

Спецификация Java 11: 17.7. Неатомарная обработка double и long

Изображение
В рамках модели памяти языка программирования Java одна запись в не-volatile long или double значение рассматривается как две отдельные записи: по одной в каждую 32-битную половину. Это может привести к ситуации, когда поток видит первые 32 бита 64-битного значения при одной записи, а вторые 32 бита - при другой записи. Запись и чтение volatile long и double значений всегда атомарны. Запись и чтение ссылок всегда атомарны, независимо от того, реализованы ли они как 32-битные или 64-битные значения. В некоторых реализациях может оказаться удобным разделить одно действие записи для 64-битного long или double значения на два действия записи для смежных 32-битных значений. Для повышения эффективности это поведение зависит от реализации; реализация виртуальной машины Java может выполнять запись в long и double значения атомарно или в двух частях. Реализациям виртуальной машины Java рекомендуется избегать разделения 64-битных значений, где это возможно. Программистам рекомендуется объявл

Спецификация Java 11: 17.6. Разрыв слова

Изображение
Одним из соображений при реализации виртуальной машины Java является то, что каждое поле и элемент массива считаются отдельными; обновления одного поля или элемента не должны взаимодействовать с чтениями или обновлениями любого другого поля или элемента. В частности, два потока, которые обновляют смежные элементы массива байтов по отдельности, не должны мешать или взаимодействовать и не нуждаются в синхронизации для обеспечения последовательной согласованности. Некоторые процессоры не предоставляют возможность записи в один байт. Было бы незаконно реализовывать обновления массива байтов на таком процессоре, просто считывая все слово, обновляя соответствующий байт, а затем записывая все слово обратно в память. Эта проблема иногда известна как разрыв слова (word tearing), и на процессорах, которые не могут легко обновить отдельный байт, потребуется другой подход. Пример - обнаружение разрыва слов Следующая программа представляет собой тестовый пример для обнаружения разрывов слов:

Спецификация Java 11: 17.5. Чтение, изменение final полей

Изображение
17.5.1. Семантика final полей Пусть o будет объектом, а c будет конструктором для o, в котором записано final поле f. Замораживание (freeze action) последнего поля f из o происходит, когда c выходит, как обычно, так и внезапно. Обратите внимание, что если один конструктор вызывает другой конструктор, а вызываемый конструктор устанавливает final поле, замораживание final поля происходит в конце вызываемого конструктора. Для каждого выполнения на поведение чтения влияют два дополнительных частичных порядка: цепочка разыменования dereferences() и цепочка памяти (memory chain) mc(), которые считаются частью выполнения (и, таким образом, фиксируются для любого конкретного выполнения). Эти частичные порядки должны удовлетворять следующим ограничениям (которые не обязательно должны иметь уникальное решение): Цепочка разыменования: если действие a является чтением или записью поля или элемента объекта o потоком t, который не инициализировал o, то должно существовать некоторое чтение r пот

Спецификация Java 11: 17.5. Семантика final полей

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

Спецификация Java 11: 17.4.5. Порядок происходит-до (happens-before)

Изображение
Два действия могут быть упорядочены отношениями "происходит-до". Если одно действие происходит раньше другого, то первое видимо и по порядку раньше второго. Если у нас есть два действия x и y, мы пишем hb(x, y), чтобы указать, что x происходит-до y. Если x и y являются действиями одного и того же потока и x стоит перед y в программном порядке, тогда hb(x, y). От конца конструктора объекта до начала финализатора (§12.6) для этого объекта существует граница "происходит-до". Если действие x синхронизируется со следующим действием y, то у нас также есть hb(x, y). Если hb(x, y) и hb(y, z), то hb(x, z). Методы wait класса Object (§17.2.1) имеют связанные с ними действия блокировки и разблокировки; их отношения "происходит-до" определяются этими связанными действиями. Следует отметить, что наличие связи между двумя действиями не обязательно означает, что они должны происходить в таком порядке в реализации. Если изменение порядка дает результаты, соответс

Спецификация Java 11: 17.4.4. Порядок синхронизации

Изображение
У каждого выполнения есть порядок синхронизации. Порядок синхронизации - это общий порядок всех действий синхронизации выполнения. Для каждого потока t порядок синхронизации действий синхронизации (§17.4.2) в t согласуется с программным порядком (§17.4.3) t. Действия синхронизации порождают отношение synchronized-with (синхронизирован-с) для действий, определяемое следующим образом: Действие разблокировки на мониторе m синхронизируется-со всеми последующими действиями блокировки на m (где "последующие" определены в соответствии с порядком синхронизации). Запись в изменчивую (volatile) переменную v (§8.3.1.4) синхронизируется-со всеми последующими чтениями v любым потоком (где "последующие" определены в соответствии с порядком синхронизации). Действие, запускающее поток, синхронизируется с первым действием в потоке, которое оно запускает. Запись значения по умолчанию (ноль, false или null) в каждую переменную синхронизируется с первым действием в каждом потоке.

Спецификация Java 11: 17.4.3. Программы и порядок программ

Изображение
Среди всех межпотоковых действий, выполняемых каждым потоком t, программный порядок t является общим порядком, который отражает порядок, в котором эти действия будут выполняться в соответствии с внутрипотоковой семантикой t. Набор действий является последовательным, если все действия происходят в общем порядке (порядке выполнения), который согласуется с порядком выполнения программы, и, кроме того, каждое чтение r переменной v видит значение, записанное записью w в v таким образом, что: w стоит перед r в порядке выполнения, и нет другой записи w' такой, что w предшествует w', а w' предшествует r в порядке выполнения. Последовательная согласованность - это очень надежная гарантия видимости и упорядоченности выполнения программы. В рамках последовательно согласованного выполнения существует общий порядок всех отдельных действий (таких как чтение и запись), который согласуется с порядком выполнения программы, и каждое отдельное действие является атомарным и сразу видно к

Спецификация Java 11: 17.4.1. Общие переменные. 17.4.2. Действия

Изображение
17.4.1. Общие переменные Память, которая может быть разделена между потоками, называется общей памятью или памятью кучи (heap memory). Все поля экземпляра, статические поля и элементы массива хранятся в динамической памяти. В этой главе спецификации используется термин переменная для обозначения как полей, так и элементов массива. Локальные переменные (§14.4), формальные параметры метода (§8.4.1) и параметры обработчика исключений (§14.20) никогда не используются совместно между потоками и не зависят от модели памяти. Два доступа к одной и той же переменной (чтение или запись) считаются конфликтующими, если хотя бы одно из обращений является записью. 17.4.2. Действия Межпотоковое действие - это действие, выполняемое одним потоком, которое может быть обнаружено другим потоком или на которое может оказывать непосредственное влияние. Программа может выполнять несколько видов межпотокового действия: Чтение (обычное или не-volatile). Чтение переменной. Запись (обычная или не-volati

Спецификация Java 11: 17.4. Модель памяти

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

Спецификация Java 11: 17.3. Sleep и Yield

Изображение
Thread.sleep переводит выполняющийся в данный момент поток в спящий режим (временно прекращает выполнение) на указанную продолжительность, в зависимости от точности и аккуратности системных таймеров и планировщиков. Поток не теряет права собственности на какие-либо мониторы, и возобновление выполнения будет зависеть от планирования и доступности процессоров, на которых будет выполняться поток. Важно отметить, что ни Thread.sleep, ни Thread.yield не имеют семантики синхронизации. В частности, компилятору не нужно сбрасывать записи, кэшированные в регистрах, в общую память перед вызовом Thread.sleep или Thread.yield, а также компилятору не нужно перезагружать значения, кэшированные в регистрах, после вызова Thread.sleep или Thread.yield. Например, в следующем (сломанном) фрагменте кода предположим, что this.done является не-volatile boolean полем: while (!this.done) Thread.sleep(1000); Компилятор может прочитать поле this.done только один раз и повторно использовать кэширова

Спецификация Java 11: 17.2.2. Уведомление. 17.2.3. Прерывания

Изображение
17.2.2. Уведомление Действия уведомления происходят при вызове методов notify и notifyAll. Пусть поток t будет потоком, выполняющим любой из этих методов для объекта m, и пусть n будет количеством действий блокировки t на m, которые не были сопоставлены действиями разблокировки. Происходит одно из следующих действий: Если n равно нулю, возникает исключение IllegalMonitorStateException. Это тот случай, когда поток t еще не имеет блокировки для цели m. Если n больше нуля и это действие уведомления, то если набор ожидания m не пуст, поток u, который является членом текущего набора ожидания m, выбирается и удаляется из набора ожидания. Нет никакой гарантии, какой поток в наборе ожидания выбран. Это удаление из набора ожидания позволяет возобновить u в действии ожидания. Обратите внимание, однако, что действия блокировки u при возобновлении не могут быть успешными до тех пор, пока t полностью не разблокирует монитор для m. Если n больше нуля и это действие notifyAll, то все пот

Спецификация Java 11: 17.2. Набор ожиданий и уведомления

Изображение
Каждый объект, помимо связанного монитора, имеет связанный набор ожиданий (wait set). Набор ожиданий - это набор потоков. Когда объект создается впервые, его набор ожидания пуст. Элементарные действия, которые добавляют потоки и удаляют потоки из наборов ожидания, являются атомарными. Наборы ожидания управляются исключительно с помощью методов Object.wait, Object.notify и Object.notifyAll. На манипуляции с наборами ожидания также может влиять статус прерывания потока и методы класса Thread, работающие с прерыванием. Кроме того, методы класса Thread для сна и присоединения к другим потокам имеют свойства, производные от свойств ожидания и уведомлений. 17.2.1. Ожидание (wait) Действия ожидания происходят при вызове метода wait() или форм wait(long millisecs) и wait(long millisecs, int nanosecs). Вызов wait(long millisecs) с нулевым параметром или вызов wait(long millisecs, int nanosecs) с двумя нулевыми параметрами эквивалентен вызову wait(). Поток нормально возвращается из ожидани

Спецификация Java 11: 17.1. Синхронизация

Изображение
Язык программирования Java предоставляет несколько механизмов для связи между потоками. Самый простой из этих методов - синхронизация, которая реализуется с помощью мониторов. Каждый объект в Java связан с монитором, который поток может блокировать или разблокировать. Только один поток одновременно может удерживать блокировку на мониторе. Любые другие потоки, пытающиеся заблокировать этот монитор, блокируются до тех пор, пока они не смогут получить блокировку на этом мониторе. Поток t может блокировать конкретный монитор несколько раз; каждая разблокировка отменяет эффект одной операции блокировки. Оператор synchronized (§14.19) вычисляет ссылку на объект; затем он пытается выполнить действие блокировки на мониторе этого объекта и не продолжает дальше, пока действие блокировки не будет успешно завершено. После выполнения действия блокировки выполняется тело синхронизированного оператора. Если выполнение тела когда-либо завершается, нормально или внезапно, действие разблокировки автома

Спецификация Java 11: Глава 17. Потоки и блокировки

Изображение
Хотя большая часть обсуждения в предыдущих главах спецификации касается только поведения кода при выполнении одного оператора или выражения за раз, то есть одним потоком, виртуальная машина Java может поддерживать множество потоков выполнения одновременно. Эти потоки независимо выполняют код, который работает со значениями и объектами, находящимися в общей основной памяти. Потоки могут поддерживаться за счет наличия множества аппаратных процессоров, квантования времени одного аппаратного процессора или квантования времени многих аппаратных процессоров. Потоки представлены классом Thread. Единственный способ для пользователя создать поток - создать объект этого класса; каждый поток связан с таким объектом. Поток запускается, когда метод start() вызывается для соответствующего объекта Thread. Поведение потоков, особенно при неправильной синхронизации, может сбивать с толку и противоречить интуиции. В этой главе описывается семантика многопоточных программ; он включает правила, при кото

Спецификация Java 11: 9.9. Типы функций

Изображение
Тип функции функционального интерфейса I - это тип метода (§8.2), который может использоваться для переопределения (§8.4.8) абстрактного метода(ов) I. Пусть M будет набором абстрактных методов, определенных для I. Тип функции I состоит из следующего: Параметры типа, типы формальных параметров и тип возвращаемого значения: Пусть m - метод в M с: сигнатурой, которая является субсигнатурой каждой сигнатуры метода в M; и тип возврата R (возможно, void), где либо R совпадает с типом возвращаемого значения каждого метода в M, либо R является ссылочным типом и является подтипом каждого типа возвращаемого значения метода в M (после адаптации для любых параметров типа (§8.4.4), если оба метода имеют одинаковую сигнатуру). Если такого метода не существует, пусть m будет методом в M с: сигнатурой, которая является субсигнатурой каждой сигнатуры метода в M; и тип возвращаемого значения, такой что m может быть заменен типом возвращаемого значения (§8.4.5) для каждого метода в M. Пара

Спецификация Java 11: 9.8. Функциональные интерфейсы

Изображение
Функциональный интерфейс - это интерфейс, который имеет только один абстрактный метод (помимо методов Object) и, таким образом, представляет собой контракт с одной функцией. Этот "единственный" метод может принимать форму нескольких абстрактных методов с замещающими эквивалентными сигнатурами, унаследованными от суперинтерфейсов; в этом случае унаследованные методы логически представляют один метод. Для интерфейса I пусть M будет набором абстрактных методов, которые являются членами I, не имеющими такой же сигнатуры, как любой public метод экземпляра класса Object (§4.3.2). Тогда I является функциональным интерфейсом, если существует метод m в M, для которого выполняются оба следующих условия: Сигнатура m - это субсигнатура (§8.4.2) каждой сигнатуры метода в M. m заменяется возвращаемым типом (§8.4.5) для каждого метода в M. В дополнение к обычному процессу создания экземпляра интерфейса путем объявления и создания экземпляра класса (§15.9), экземпляры функциональных ин