JuniorJava Developer > Java Core > Collections
Короткий ответ Ответ зависит от того, что понимается под «поиском». В ArrayList доступ по индексу get(i) занимает O(1): массив хранится непрерывно. Поиск по значению — contains(element) — требует линейного прохода с проверкой через equals, поэтому в худшем случае это O(n). В HashSet contains вычисляет хэш-код, находит корзину и сравнивает элементы внутри неё — в среднем O(1) независимо от размера множества. Как это работает подробнее Худший случай HashSet — когда из-за плохих хэш-кодов все элементы «свалились» в одну корзину: поиск деградирует до O(n). В Java 8+ длинные цепочки превращаются в сбалансированное дерево, и тогда худший случай ограничен O(log n). Для корректной работы HashSet элементы должны правильно переопределять equals и hashCode. Практический вывод: частые проверки «содержится ли элемент» быстрее делать через HashSet, чем через список. Пример кода Что использовать на практике Для частых проверок «содержится ли элемент» используйте HashSet — в среднем O(1) против O(n) у списка. ArrayList берит
arraylistbig-o
MiddlePython Developer > Основы Python > Многопоточность и асинхронность
Короткий ответ Потоки выбирают, когда программа в основном ожидает ввода-вывода: чтение файлов, сетевые запросы, работа с базой данных. Multiprocessing выбирают для задач, нагружающих процессор, так как каждый процесс имеет собственный интерпретатор и собственный GIL. Как это работает подробнее Ключ к выбору — где находится узкое место: в ожидании внешних ресурсов или в вычислениях. I/O-bound сценарии. Поток почти всё время ждёт ответа от сети, диска или базы; из-за GIL параллельно исполняется лишь один поток, но остальные успевают работать в паузах ожидания, поэтому суммарная пропускная способность растёт. CPU-bound сценарии. Для сложных вычислений и обработки больших данных GIL мешает потокам одного процесса исполняться параллельно, поэтому создают несколько процессов. Изоляция. У каждого процесса свой интерпретатор и свой GIL, поэтому они реально работают на разных ядрах, а ошибка в одном процессе не остановит остальные. Комбинирование. В крупных приложениях оба подхода часто сочетают под разные части сист
cpu-boundio-bound
MiddleJava Developer > Java Core > ООП
Короткий ответ Нет. Переопределить можно только метод, который класс унаследовал от родителя и который наследнику виден, а private-метод виден лишь внутри своего класса и подклассам не передаётся. Метод с той же сигнатурой, объявленный в наследнике, — это новый самостоятельный метод, а аннотация @Override над ним вызовет ошибку компиляции. Как это работает подробнее Такое поведение называют сокрытием (hiding), а не переопределением. Аналогично ведут себя статические методы: одноимённый статический метод в наследнике лишь скрывает родительский, и какой из них вызовется, зависит от типа ссылки, а не от фактического типа объекта. Переопределение возможно только для унаследованных и видимых методов: не private, не static, не final. Аннотация @Override — компилятор проверяет, что метод действительно переопределяется; для private-метода это ошибка компиляции. Доступ private — метод не становится частью API наследника: для него он просто не существует. Пример кода Что использовать на практике Переопределяй только ун
hidingjava
JuniorPython Developer > Основы Python > Исключения и контекстные менеджеры
Короткий ответ Да, создавать собственные исключения можно и часто нужно: достаточно объявить класс, наследующий от Exception или его подкласса, и возбуждать его оператором raise. Это позволяет сообщать об ошибках, специфичных для приложения, и обрабатывать их отдельно от стандартных. Как это работает подробнее Процесс состоит из трёх шагов: определить класс исключения, бросить его при особом условии и перехватить в try/except. Определение класса. Новый класс наследуется от встроенного Exception или подходящего подкласса вроде ValueError или RuntimeError. Соглашение об именах. Имена классов исключений принято заканчивать словом Error. Оператор raise. Исключение возбуждается в момент наступления особого условия и затем перехватывается в try/except. Дополнительные данные. Переопределением __init__() и __str__() можно хранить значение, вызвавшее ошибку, и формировать читаемое сообщение. Пример кода Что использовать на практике Точность обработки. Лови собственные исключения отдельно от стандартных, чтобы ветка ре
classescustom-exceptions
JuniorPython Developer > Основы Python > Основы Python
Короткий ответ Да, убрать все пробелы из строки можно несколькими способами: связкой ''.join(s.split()), методом replace(' ', '') или списковым включением, отфильтровывающим пробельные символы. Как это работает подробнее Выбор способа зависит от того, что именно нужно сделать с пробелами: удалить полностью, снять по краям или схлопнуть серии. Полное удаление. split() без аргумента режет строку по любым пробельным символам, а ''.join(...) склеивает слова без разделителя — все пробелы исчезают. Замена. replace(' ', '') заменяет каждый пробел на пустую строку — подходит, когда нужно убрать только обычные пробелы. Фильтрация. Включение ''.join(c for c in s if c != ' ') оставляет все символы, кроме пробела. Края и схлопывание. strip(), lstrip() и rstrip() убирают пробелы с концов строки, а ' '.join(s.split()) заменяет серии пробелов одним. Пример кода Что использовать на практике Очистка ввода. При обработке пользовательского ввода, текстовых файлов и внешних данных удаляй лишние пробелы через split() и join(). То
joinpython
JuniorPython Developer > Основы Python > Основы Python
Короткий ответ split() и join() — парные методы строк, выполняющие противоположные операции: split() разбивает строку на список подстрок, а join() собирает список строк обратно в одну строку. Как это работает подробнее Важно помнить, что join() вызывается у строки-разделителя, а не у списка: разделитель.join(список). Разбиение. str.split(sep) делит строку по заданному разделителю; если он не указан, строка разбивается по пробельным символам. Склейка. sep.join(list_of_strings) объединяет элементы списка, вставляя между ними указанный разделитель. Ограничение. Вторым аргументом split() можно задать максимальное число разбиений. Неизменяемость. Строка в Python неизменяема, поэтому оба метода не меняют исходный объект, а создают новые. Пример кода Что использовать на практике Разбор ввода. Используй split() для обработки пользовательского ввода, CSV-подобных строк и текстовых данных. Сборка строк. Применяй join(), когда нужно собрать строку из фрагментов, — это быстрее, чем конкатенация в цикле. Пустой разделител
joinpython
MiddlePython Developer > Основы Python > Объектно-ориентированное программирование
Короткий ответ Да, Python поддерживает множественное наследование: один класс может наследовать сразу от нескольких базовых. Такой класс получает доступ к атрибутам и методам каждого из родителей, а конфликты одноимённых методов разрешаются по порядку MRO. Как это работает подробнее Синтаксис. После имени класса в скобках через запятую перечисляются все родители, например class C(A, B). Доступ к членам. Дочерний класс получает атрибуты и методы всех перечисленных базовых классов. Примеси. Множественное наследование удобно для реализации mixins — небольших вспомогательных классов, добавляющих функциональность. Неоднозначность. Если два родителя определяют метод с одним именем, Python выбирает реализацию по MRO, что детерминированно решает проблему ромба. Пример кода Что использовать на практике Примеси. Применяй множественное наследование для небольших mixins, добавляющих классам отдельные возможности. Порядок родителей. Выбирай порядок перечисления базовых классов осознанно — от него зависит MRO. Простота. Ес
classesmro
JuniorJava Developer > Java Core > JVM и память
Короткий ответ Потому что в момент запуска приложения у JVM ещё нет ни одного объекта класса, а метод main() нужно вызвать до создания каких-либо экземпляров. Объявление static позволяет JVM обратиться к методу через сам класс, без объекта, — это и есть вход в программу. Как это работает подробнее Каноническая сигнатура: public static void main(String[] args). Здесь public — доступность метода для JVM извне, void — метод ничего не возвращает, а String[] args — массив аргументов командной строки. Если убрать static, компиляция пройдёт, но при запуске JVM не найдёт подходящий метод и завершится ошибкой вида Main method is not static. Точка входа — JVM находит метод с канонической сигнатурой и вызывает его, не создавая экземпляр класса. Перегрузка — другие перегруженные варианты main() допустимы, но запускается только вариант с массивом String. Инициализация — первые объекты приложения создаются уже внутри main(), что решает проблему «курицы и яйца». Пример кода Что использовать на практике Всегда объявляй точку
entry-pointjava
MiddleJava Developer > Java Core > Multithreading
Короткий ответ Потому что эти методы работают с монитором объекта, а не с потоком: поток ожидает на конкретном объекте-«замке», и уведомление тоже отправляется через объект. Встроенный монитор есть у любого наследника Object, поэтому естественно разместить методы ожидания и уведомления в самом Object, чтобы их можно было вызывать для любого объекта-монитора. Как это работает подробнее Механика такая: поток вызывает someObject.wait() из синхронизированного блока по someObject, освобождает его монитор и засыпает, а другой поток вызывает notify()/notifyAll() на том же объекте, чтобы разбудить ожидающих. Именно привязка к объекту позволяет независимо синхронизироваться на разных «замках»: ожидание на одном объекте не мешает потокам работать с другим. notify() будит один произвольный поток из очереди ожидания на мониторе, notifyAll() — все ожидающие, после чего они конкурируют за монитор. notifyAll() безопаснее в большинстве сценариев: он снижает риск «потерянного уведомления». Пример кода Что использовать на прак
javamonitor
JuniorJava Developer > Java Core > JVM и память
Короткий ответ Программу на Java компилируют один раз в промежуточный байт-код, а исполняет его JVM, реализация которой существует под каждую платформу: Windows, Linux, macOS. Поэтому один и тот же .class-файл работает без перекомпиляции везде, где есть подходящая JVM. Этот принцип называют «Write Once, Run Anywhere». Как это работает подробнее Секрет — в разделении труда между компилятором и средой исполнения: компилятор не привязывается к конкретному «железу», а JVM делает это за него. Компиляция. javac превращает исходный код не в машинный код процессора, а в промежуточный байт-код (.class-файлы), одинаковый для всех платформ. Исполнение. Каждая реализация JVM «знает», как превратить байт-код в команды именно своей ОС и архитектуры. Переносимым является байт-код, а JVM платформозависима — её устанавливают отдельно под каждую систему. Фиксированные примитивы. В отличие от C/C++, где размер int зависит от архитектуры, спецификация Java задаёт размеры жёстко: например, int — всегда 32 бита. Пример кода Что ис
bytecodecross-platform