В свете атаки polykill.io автор блога Low Level Learning поделился ссылкой на браузерный эксплоит для V8, который открывает удивительный мир unsafe JavaScript через функцию-интринсик вида
Очень залипательное чтение на ночь.
LongArray.xor(Long). Валидация аргументов там есть и с первого взгляда выглядит достаточной, но, как предупреждал дядюшка Фрейд, иногда лонг — это не просто лонг.Очень залипательное чтение на ночь.
🔥4👍2
По-явански
Может, нужно проверять хэш-сумму драйвера уровня ядра перед установкой? Да не, бред какой-то…
Судя по детальному разбору полётов, скриншот действительности не соответствует.
YouTube
IT WAS A REGEX?!? - Full CrowdStrike Report Released
Recorded live on twitch, GET IN
### Report Link
https://www.crowdstrike.com/wp-content/uploads/2024/08/Channel-File-291-Incident-Root-Cause-Analysis-08.06.2024.pdf
### My Stream
https://twitch.tv/ThePrimeagen
### Best Way To Support Me
Become a backend…
### Report Link
https://www.crowdstrike.com/wp-content/uploads/2024/08/Channel-File-291-Incident-Root-Cause-Analysis-08.06.2024.pdf
### My Stream
https://twitch.tv/ThePrimeagen
### Best Way To Support Me
Become a backend…
❤2👍1💊1
Проектируем запись звонка
Например, мы разрабатываем свой Зум, Скайп или Телеграм. Как реализовать функцию записи звонка?
Во время разговора каждый участник созвона слышит некую «свою версию», где его собственный голос отсутствует, потому что «эхо» мешало бы говорить. Сетевые помехи влияют как на исходящий голос, который слышен остальным, так и на входящие голоса.
При стриминге мы
а) можем терять кусочки речи. Если это было недавно — можно их доставить и проиграть ускоренно (слышал такое поведение в телеграме), но чаще они просто выбрасываются и никогда не возмещаются;
б) подстраиваем качество под ширину канала. Оно непостоянно и может многократно подпрыгивать и падать.
При записи же хочется сохранить все реплики в максимальном качестве, и, возможно, отдельными дорожками. Два этих варианта — стрим и запись — друг с другом «спорят», потому что на стриме слать максимальное качество некогда. Возникает очевидная идея — «досылать» фреймы, «исковерканные» на стриме, позже.
У этого решения тоже есть проблемы:
а) запись в полном качестве будет доступна не сразу, а только после «досылки» клиентами, которая закончится когда-то потом (может, и никогда),
б) клиент может обмануть сервис и прислать совсем не те фреймы, которые собеседники слышали во время разговора,
в) клиент должен знать про запись, где-то хранить её и расходовать трафик на досылку.
В итоге довольно стандартная задача становится ооооччень нетривиальной, если решать её качественно.
Например, мы разрабатываем свой Зум, Скайп или Телеграм. Как реализовать функцию записи звонка?
Во время разговора каждый участник созвона слышит некую «свою версию», где его собственный голос отсутствует, потому что «эхо» мешало бы говорить. Сетевые помехи влияют как на исходящий голос, который слышен остальным, так и на входящие голоса.
При стриминге мы
а) можем терять кусочки речи. Если это было недавно — можно их доставить и проиграть ускоренно (слышал такое поведение в телеграме), но чаще они просто выбрасываются и никогда не возмещаются;
б) подстраиваем качество под ширину канала. Оно непостоянно и может многократно подпрыгивать и падать.
При записи же хочется сохранить все реплики в максимальном качестве, и, возможно, отдельными дорожками. Два этих варианта — стрим и запись — друг с другом «спорят», потому что на стриме слать максимальное качество некогда. Возникает очевидная идея — «досылать» фреймы, «исковерканные» на стриме, позже.
У этого решения тоже есть проблемы:
а) запись в полном качестве будет доступна не сразу, а только после «досылки» клиентами, которая закончится когда-то потом (может, и никогда),
б) клиент может обмануть сервис и прислать совсем не те фреймы, которые собеседники слышали во время разговора,
в) клиент должен знать про запись, где-то хранить её и расходовать трафик на досылку.
В итоге довольно стандартная задача становится ооооччень нетривиальной, если решать её качественно.
👍9❤2🔥2💊1
Mobile Developer
Исследуем Compiler Explorer [EN] https://android-developers.googleblog.com/2024/09/become-better-android-developer-compiler-explorer.html Очень полезная тулза, которая позволяет понять магию компиляторов - появилась для Android 👉 Как выглядит code shrinking…
Теперь я знаю, что обе версии функции имеют одинаковый размер в 452 байта 🌚
🌚8🔥4🤔3💊1
Hello nullability my old friend
Котлин давно вошёл в грешную жизнь Android SDK, и с годами в Java-коде проросли аннотации нуллабельности.
Compose стал повсеместно использовать value-классы — а компилятор доэволюционировал и перестал рассыпаться от одного их вида.
Но пришла новая напасть: значения вроде
В парадигме, где все параметры опциональные, а фоллбэки можно забрать из
Почему же нельзя использовать типы
Потому что они будут бокситься, а композ станет тормозить ещё более чудовищно.
Заявить, что в терминах данного value-класса null выражается через
Нельзя и сделать sealed-иерархию value-классов вида
KEEP на это есть, но он заброшен. Там предлагают различать варианты силеда посредством боксинга, оставив только один вариант труъ-инлайн, так что данное направление бесперспективно.
Возможен ещё один вариант: симулировать иерархию классов, не уповая на сабтайпинг в языке.
Не хотелось бы везде писать
Можно добавить новый уровень сложности: интерфейс
Итого: не нужно допускать новых null-значений. Явно передать лишний параметр не страшно. Страшно рисовать «обводку непонятного цвета и неизвестной толщины».
Котлин давно вошёл в грешную жизнь Android SDK, и с годами в Java-коде проросли аннотации нуллабельности.
Compose стал повсеместно использовать value-классы — а компилятор доэволюционировал и перестал рассыпаться от одного их вида.
Но пришла новая напасть: значения вроде
{Dp, Color, Size, Offset, …}.Unspecified. Теперь можно запросить «обводку непонятного цвета и неизвестной толщины», компилятор этому не препятствует.В парадигме, где все параметры опциональные, а фоллбэки можно забрать из
CompositionLocal, это удобно. Хотя идея, что у Text("LOL") «откуда-то» берётся цвет, размер, шрифт и начертание, совершенно нездоровая. А вот при вызове Dp.toPx() всё рушится, ведь, как учили в первом классе, NaN умноженный на число даёт NaN.Почему же нельзя использовать типы
Color? и Dp??Потому что они будут бокситься, а композ станет тормозить ещё более чудовищно.
Заявить, что в терминах данного value-класса null выражается через
Float.NaN, также нельзя — null хардкодом прописан в компилятор.Нельзя и сделать sealed-иерархию value-классов вида
sealed value class OptionDp(…) {
object Unspecified : OptionDp(Float.NaN)
class Dp(…) : OptionDp(…)
}KEEP на это есть, но он заброшен. Там предлагают различать варианты силеда посредством боксинга, оставив только один вариант труъ-инлайн, так что данное направление бесперспективно.
Возможен ещё один вариант: симулировать иерархию классов, не уповая на сабтайпинг в языке.
value class OptionDp(…) {
/*companion*/ val Unspecified = OptionDp(Float.NaN)
fun unwrap() = Dp(value)
}
value class Dp(…) {
init { require(value.isFinite()) }
val option get() = OptionDp(value)
}Не хотелось бы везде писать
32.dp.option, да и замена Dp на OptionDp становится ломающим изменением.Можно добавить новый уровень сложности: интерфейс
AsOptionDp с единственным методом, который реализовывали бы и Dp, и OptionDp. И пусть библиотечная функция вызывает метод конвертации. Добро пожаловать в рубрику «к Мишиному сожалению, Котлин не Раст»: даже при передаче в inline-функцию value-класс боксится, а вызов остаётся виртуальным.Итого: не нужно допускать новых null-значений. Явно передать лишний параметр не страшно. Страшно рисовать «обводку непонятного цвета и неизвестной толщины».
👍13🤔4💊3🔥2❤1🤨1
Разделение на Iterable и Sequence несостоятельно
Как работает этот код?
Зависит от того,
Если Iterable:
• filter – создать копию, выкинув не соответствующее предикату,
• map — создать копию, применив трансформацию,
• sorted — создать копию, отсортировать,
• take — создать копию, ограниченную по размеру,
• toMutableList — создать копию.
Если Sequence:
• filter — отфильтровать итератор,
• map — мапнуть итератор,
• sorted — собрать итератор в коллекцию и отсортировать,
• take — отдать итератор ограниченного размера,
• toMutableList — собрать итератор в коллекцию.
Здесь не будет нытья о том, что обе цепочки неэффективны.Миша пытается мириться с тем, что Котлин — не Раст. Здесь будет нытьё о том, что имена некоторых методов нам врут.
То, что называется
То, что сейчас называется
Сортировка — это всегда буферизация, поэтому пусть будет
(Зато существует отдельная версия для in-place-сортировки:
Копирующий
Тогда вариант с
Теперь явно видно, что здесь происходит: копирование, копирование и ещё три раза копирование!
Разделение на Iterable и Sequence тогда теряет всякий смысл: по названию операции видно, оборачивание это или копирование.
К слову,
Вспоминается ещё одно уродство, которое мимикрирует под коллекции: операции на строках —
Как работает этот код?
expr
.filter { ... }
.map { ... }
.sorted()
.take(k)
.toMutableList()
Зависит от того,
expr: Iterable или Sequence.Если Iterable:
• filter – создать копию, выкинув не соответствующее предикату,
• map — создать копию, применив трансформацию,
• sorted — создать копию, отсортировать,
• take — создать копию, ограниченную по размеру,
• toMutableList — создать копию.
Если Sequence:
• filter — отфильтровать итератор,
• map — мапнуть итератор,
• sorted — собрать итератор в коллекцию и отсортировать,
• take — отдать итератор ограниченного размера,
• toMutableList — собрать итератор в коллекцию.
Здесь не будет нытья о том, что обе цепочки неэффективны.
То, что называется
Iterable.filter(): List, должно называться collectMatching. Честный filter можно сделать на итераторе.Sequence.map оборачивает апстримовый итератор. List.map по этой же логике должен возвращать view на другой List.То, что сейчас называется
Iterable.map, должно называться collectMapping.Сортировка — это всегда буферизация, поэтому пусть будет
.collectSorted(): List. Отдельная версия для сиквенсов не имеет смысла — мы всегда собираем итератор в лист.(Зато существует отдельная версия для in-place-сортировки:
MutableList.sort(): Unit. Ой, оказывается, можно обработать особый случай, а не врать, пытаясь подогнать всё под единый интерфейс!)Копирующий
List.take — collectFirst. Мгновенный List.take, работающий через subList — takeLeaking.toMutableList() — collectToMutableList().Тогда вариант с
expr: Iterable выглядел бы так:expr
.collectMatching { ... }
.collectMapping { ... }
.collectSorted()
.collectFirst(k)
.collectToMutableList()
Теперь явно видно, что здесь происходит: копирование, копирование и ещё три раза копирование!
Разделение на Iterable и Sequence тогда теряет всякий смысл: по названию операции видно, оборачивание это или копирование.
К слову,
list1 + list2 — тоже копирование, и выглядеть оно должно как (list1 + list2).collectToList().Вспоминается ещё одно уродство, которое мимикрирует под коллекции: операции на строках —
"qwe".map(Character::toUpperCase), "qwe".count(Character::isLetter) и тому подобные. Это недоразумение показывает нам деталь реализации — UTF-16 — выраженную через тип Character, который бесполезен и вообще не должен существовать. Если предыдущие рассуждения об операциях на коллекциях можно списать на стиль и безразличие котлина к производительности, то здесь уже вопрос корректности. Character и CharSequence — это баг.🔥7🤨6👍4❤1
Уведомление об уведомлении
Письмо на почту:
Сообщение от коллеги:
Было? Бесит? 🥲
Мы ведь не знаем, как у наших коллег настроены уведомления. Но со стороны трекера задач #идея напрашивается очень простая: отвечать на действие пользователя всплывашкой вида «мы отправили уведомление исполнителю на почту» или даже «мы кинули уведомление исполнителю прямо влицо браузер».
Всё! Ты знаешь, что коллега увидел. Или коллега знает, что ты увидел.
Письмо на почту:
%username% назначил вас исполнителем задачи %taskname%
Сообщение от коллеги:
Привет! Перекинул на тебя задачу https://…
Было? Бесит? 🥲
Мы ведь не знаем, как у наших коллег настроены уведомления. Но со стороны трекера задач #идея напрашивается очень простая: отвечать на действие пользователя всплывашкой вида «мы отправили уведомление исполнителю на почту» или даже «мы кинули уведомление исполнителю прямо в
Всё! Ты знаешь, что коллега увидел. Или коллега знает, что ты увидел.
👀9🥱6🤨2👍1💊1
Варианты корутин
В разных языках корутины ведут себя по-разному. Вот краткий обзор.
C#, JavaScript
Eager & parallel by default:
+
– забытый
Go
Fire and forget: go statement запускает указанную функцию отдельно.
+ «неокрашенные» функции
– для возврата значения нужна обвязка в виде канала
Kotlin
Sequential by default:
–
+ параллельность явная
Rust
Lazy: async-функция возвращает фьючу, но запускается она только после
+
+ и они явно
– у тебя там локальная переменная не-
В разных языках корутины ведут себя по-разному. Вот краткий обзор.
C#, JavaScript
Eager & parallel by default:
async-функция возвращает уже запущенный Task/Promise, а await приостанавливает выполнение до завершения задачи.+
async не является отдельным видом функций, а просто намекает, что возвращается фьюча– забытый
await приводит к параллельному выполнениюGo
Fire and forget: go statement запускает указанную функцию отдельно.
+ «неокрашенные» функции
– для возврата значения нужна обвязка в виде канала
Kotlin
Sequential by default:
suspend-функция может приостанавливаться. Для параллельного запуска нужен отдельный вызов корутин-билдера — async, launch.–
suspend-функции нельзя вызывать из обычных, иерархия функциональных типов разделяется+ параллельность явная
Rust
Lazy: async-функция возвращает фьючу, но запускается она только после
.await.+
async-функции можно вызывать из обычных+ и они явно
await-ятся либо явно spawn-ятся параллельно– у тебя там локальная переменная не-
Send-типа, так что struct для фьючи сгенерить не получилось, извиняй, братан👍13
idea.plugin-0.33.jar
411.4 KB
Выпустил новую версию плагина для IDE:
• аллокации вараргов теперь бросаются в глаза,
•
• ну и фиксы, конечно же,
• а релиз теперь, помимо Маркетплейса, можно найти здесь и там.
K2, к сожалению. будет уже когда-нибудь потом.
• аллокации вараргов теперь бросаются в глаза,
•
<vector pathData теперь переписываем умнее, например, Ll может заменяться на HhVv, а несколько таких команд — склеиваться в одну,• ну и фиксы, конечно же,
• а релиз теперь, помимо Маркетплейса, можно найти здесь и там.
K2, к сожалению. будет уже когда-нибудь потом.
🔥9👍4🤬1💊1😡1
Зигота
В Андроиде зиготой называется процесс, от которого форкаются процессы приложений. Он содержит уже загруженный код SDK, а код приложения загружается после форка.
В биологическом же смысле зигота содержит уже готовый ДНК организма, унаследованный от обоих родителей.
Таким образом, зигота в Андроиде — на самом деле яйцеклетка, в которую в процессе запуска (оплодотворения) после форка внедряется код приложения (сперматозоида).
В Андроиде зиготой называется процесс, от которого форкаются процессы приложений. Он содержит уже загруженный код SDK, а код приложения загружается после форка.
В биологическом же смысле зигота содержит уже готовый ДНК организма, унаследованный от обоих родителей.
Таким образом, зигота в Андроиде — на самом деле яйцеклетка, в которую в процессе запуска (оплодотворения) после форка внедряется код приложения (сперматозоида).
😁40👍7🌚6❤1🤯1
В Java нет UB и утечек памяти
Это действительно так с точки зрения C/C++: целочисленное переполнение определено, разыменование нуля — тоже. Компилятор не делает вид, что этого не может быть.
Если ссылку на объект потеряли, то и объекта скоро не станет.
Но мы же не в C/C++? В другой среде термины могут трактоваться иначе.
Если запустить несколько потоков, какой стартанёт первым? Одному планировщику известно.
Если один поток пишет в массив, а второй читает, что он там увидит? Зависит от архитектуры процессора, размера кэшей и снова от планировщика.
Ну чем не UB?
Если навечно зарегистрировать какой-нибудь слушатель, который удерживает временный объект, можно ли сказать, что последний утёк? Разрешаю.
Вот и выходит, что UB есть и утечки есть. В Си — сишные; в Джаве — джавовые.
Это действительно так с точки зрения C/C++: целочисленное переполнение определено, разыменование нуля — тоже. Компилятор не делает вид, что этого не может быть.
Если ссылку на объект потеряли, то и объекта скоро не станет.
Но мы же не в C/C++? В другой среде термины могут трактоваться иначе.
Если запустить несколько потоков, какой стартанёт первым? Одному планировщику известно.
Если один поток пишет в массив, а второй читает, что он там увидит? Зависит от архитектуры процессора, размера кэшей и снова от планировщика.
Ну чем не UB?
Если навечно зарегистрировать какой-нибудь слушатель, который удерживает временный объект, можно ли сказать, что последний утёк? Разрешаю.
Вот и выходит, что UB есть и утечки есть. В Си — сишные; в Джаве — джавовые.
👍12😐4❤2🔥2🤬1
Почти онлайн
Сейчас вышки LTEс коронавирусными чипами от меня далековато, поэтому порой бывает так, что в руках при включённом экране я вижу только локальные данные, а интернет неожиданно ловится в кармане. Телеграм пользуется моментом, а остальные приложения заставляют меня страдать.
Офлайн можно обработать следующими способами, от самого халтурного до наиболее предпочтительного:
1. Что-то пошло не так. Выйди и зайди нормально.
2. Сломалось, вот тебе утешительная кнопочка retry.
3. Не сработало, но мы дождёмся интернета и попробуем снова. Оставайтесь на линии.
4. Иногда таймаутится, но мы будем долбиться в сеть, вдруг сработает.
5. Готово! Можешь уходить, твоё действие сохранено и стоит в очереди на отправку.
Вход в банк — это уровень 1: будешь вводить пин-код снова и снова, пока не повезёт поймать волну.
Отправка сообщения в Телеграме — уровень 5: нажал кнопку — и забыл, больше ничего делать не нужно. Не сейчас — так через час. А если нет — то завтра или через неделю, мы всё записали и ждём момента.
Сейчас вышки LTE
Офлайн можно обработать следующими способами, от самого халтурного до наиболее предпочтительного:
1. Что-то пошло не так. Выйди и зайди нормально.
2. Сломалось, вот тебе утешительная кнопочка retry.
3. Не сработало, но мы дождёмся интернета и попробуем снова. Оставайтесь на линии.
4. Иногда таймаутится, но мы будем долбиться в сеть, вдруг сработает.
5. Готово! Можешь уходить, твоё действие сохранено и стоит в очереди на отправку.
Вход в банк — это уровень 1: будешь вводить пин-код снова и снова, пока не повезёт поймать волну.
Отправка сообщения в Телеграме — уровень 5: нажал кнопку — и забыл, больше ничего делать не нужно. Не сейчас — так через час. А если нет — то завтра или через неделю, мы всё записали и ждём момента.
💘17👍5🤯4❤1🔥1
Голос из-под шторки | Миша Левченко
Антихрупкость – Нассим Талеб
Довольно интересная книга, и, как видно по закладкам, вызывающая у меня обилие разнообразных реакций.
Близко к сердцу пришлась мысль, что большое — хрупко: оно сложное, внесение изменений аукается неожиданными последствиями, а внезапные встряски могут всё разрушить непредсказуемым образом.
Что в очередной раз возвращает нас к философии Unix. :)
Близко к сердцу пришлась мысль, что большое — хрупко: оно сложное, внесение изменений аукается неожиданными последствиями, а внезапные встряски могут всё разрушить непредсказуемым образом.
Что в очередной раз возвращает нас к философии Unix. :)
❤5
По-явански
Довольно интересная книга, и, как видно по закладкам, вызывающая у меня обилие разнообразных реакций. Близко к сердцу пришлась мысль, что большое — хрупко: оно сложное, внесение изменений аукается неожиданными последствиями, а внезапные встряски могут всё…
Ещё тезис: не доверяйте людям, которые не рискуют собственной шкурой. Например, инвестиционные рекомендации ценны только тогда, когда исходят от человека, сделавшего на этом состояние.
Это обобщение принципа, который я давно исповедую: обучать должен только тот, поднялся на практике предмета, а не на преподавании.
Легко учить убивать драконов: никто не заявит, что твои приёмы против них неэффективны. Только вот в этом лесу нет никаких драконов.
Это обобщение принципа, который я давно исповедую: обучать должен только тот, поднялся на практике предмета, а не на преподавании.
Легко учить убивать драконов: никто не заявит, что твои приёмы против них неэффективны. Только вот в этом лесу нет никаких драконов.
👍13
Роберт Лафоре. Структуры данных и алгоритмы Java, второе издание
Чувствуется, что Java — не родной, не любимый язык автора: кодстайл не соблюдается, классы и файлы порой называются со строчной буквы. Имена методов часто не передают их сути. Первый же пример кода (с. 32) не скомпилируется. Скоупы переменных чересчур широкие. Положение фигурных скобок выбрано рандомом (попробуй разбери вложенность на с. 167). Вместо стандартного
С момента выхода оригинала (2003, SAMS, времена Java 1.4) до перевода в моих руках (2019, Питер, Java 11) прошла целая эпоха. Было очень наивными решением издавать книгу без изменений. Например, для демонстрации материала автор написал несколько апплетов — олды здесь? Непосвещённый читатель задолбается их запускать. Также автор по какой-то причине упоминает классы
Работа с массивами. Понимаю, что пример, где делают всё ручками, быть должен — это закон жанра. Но как насчёт использования методов
Везде, где автор использует массивы, нет примеров ресайза — а неискушённый читатель может и не знать про
Не понятно, что автору мешало в своих коллекциях сделать инициализацию готовым массивом. Десять строчек с
Кстати, видите утечку памяти на с. 77? А она есть! См. EJ3*, Item 29, первый листинг.
Дэку уделён один абзац — мол, такая штука есть, но не очень нужна. На самом деле
О хорошем. В книге доходчиво объясняется асимптотика, вплоть до изображений графиков и разъяснения, что такое логарифм — если кто не знал, а потом забыл.
Алгоритмы сортировки хорошо объясняются на словах и вдобавок визуализируются. Деревья и графы визуализируются — а это очень важно для понимания темы. Двоичные деревья (поиска и пирамида a.k.a. куча), красно-чёрные, В-деревья — это всё очень интересно и увлекательно (хотя к-ч я так до конца и не понял).
Есть перевод математических выражений в префиксную нотацию, есть префиксное дерево. Можно было бы и про AST немного поговорить :)
Связанный список. Автор утверждает, что эта структура данных, в отличие от вектора, использует память эффективно, ведь пустой хвост массива отсутствует. Посчитаем? Полупустой массив занимает 2n памяти. Узлы же, в каждом из которых есть ссылка на данные, следующий узел, невидимая ссылка на класс и пэддиннг, занимает 4n. Частота и сложность аллокаций, локальность — тут только свернуться клубочком и плакать.
Ещё один момент из реальной жизни забыт: проверки корректности входных данных. Книжный стек считает, что пользователь сам проверит, не пустой ли он. Наивно! (EJ3*, Item 49)
В рекомендациях по выбору структуры данных делается нелепое допущение: проще — лучше, ведь тебе придётся реализовывать алгоритм самому! Доходит до того, что вектор при малом объёме данных стоит предпочесть хэш-таблице. На деле же критерий выбора звучит так: нужен порядок вставки или доступ по ключу?
Вердикт: интересная книга. Читать только под присмотром взрослых.
*EJ3 — Joshua Bloch. Effective Java, Third Edition
*
Чувствуется, что Java — не родной, не любимый язык автора: кодстайл не соблюдается, классы и файлы порой называются со строчной буквы. Имена методов часто не передают их сути. Первый же пример кода (с. 32) не скомпилируется. Скоупы переменных чересчур широкие. Положение фигурных скобок выбрано рандомом (попробуй разбери вложенность на с. 167). Вместо стандартного
toString (EJ3*, Item 12) у многих классов есть функция, которая печатает содержимое в stdout. Наличие псевдополя length у массивов игнорируется, хотя во введении в Джаву автор объяснял про него. Модификатор final не используется. int[] после создания вручную заполняется нулями. Ну и так далее — короче, с языком полный провал.С момента выхода оригинала (2003, SAMS, времена Java 1.4) до перевода в моих руках (2019, Питер, Java 11) прошла целая эпоха. Было очень наивными решением издавать книгу без изменений. Например, для демонстрации материала автор написал несколько апплетов — олды здесь? Непосвещённый читатель задолбается их запускать. Также автор по какой-то причине упоминает классы
Stack, Vector и Hashtable. Хоть дженерики пока не появились, но привычные нам коллекции тогда уже завезли.Работа с массивами. Понимаю, что пример, где делают всё ручками, быть должен — это закон жанра. Но как насчёт использования методов
indexOf*, System.arraycopy, Arrays.binarySearch в дальнейшем? Без них в реальном мире жить неуютно, как и говорить о сортировке, не упоминая компараторов.Везде, где автор использует массивы, нет примеров ресайза — а неискушённый читатель может и не знать про
Arrays.copyOf.Не понятно, что автору мешало в своих коллекциях сделать инициализацию готовым массивом. Десять строчек с
arr.insert() — это ж боль.Кстати, видите утечку памяти на с. 77? А она есть! См. EJ3*, Item 29, первый листинг.
Дэку уделён один абзац — мол, такая штука есть, но не очень нужна. На самом деле
ArrayDeque (кольцевой буфер) — это самая универсальная коллекция во всём Котлине: она тебе и вектор, и обе очереди.О хорошем. В книге доходчиво объясняется асимптотика, вплоть до изображений графиков и разъяснения, что такое логарифм — если кто не знал, а потом забыл.
Алгоритмы сортировки хорошо объясняются на словах и вдобавок визуализируются. Деревья и графы визуализируются — а это очень важно для понимания темы. Двоичные деревья (поиска и пирамида a.k.a. куча), красно-чёрные, В-деревья — это всё очень интересно и увлекательно (хотя к-ч я так до конца и не понял).
Есть перевод математических выражений в префиксную нотацию, есть префиксное дерево. Можно было бы и про AST немного поговорить :)
Связанный список. Автор утверждает, что эта структура данных, в отличие от вектора, использует память эффективно, ведь пустой хвост массива отсутствует. Посчитаем? Полупустой массив занимает 2n памяти. Узлы же, в каждом из которых есть ссылка на данные, следующий узел, невидимая ссылка на класс и пэддиннг, занимает 4n. Частота и сложность аллокаций, локальность — тут только свернуться клубочком и плакать.
Ещё один момент из реальной жизни забыт: проверки корректности входных данных. Книжный стек считает, что пользователь сам проверит, не пустой ли он. Наивно! (EJ3*, Item 49)
В рекомендациях по выбору структуры данных делается нелепое допущение: проще — лучше, ведь тебе придётся реализовывать алгоритм самому! Доходит до того, что вектор при малом объёме данных стоит предпочесть хэш-таблице. На деле же критерий выбора звучит так: нужен порядок вставки или доступ по ключу?
Вердикт: интересная книга. Читать только под присмотром взрослых.
*EJ3 — Joshua Bloch. Effective Java, Third Edition
*
Arrays.indexOf не существует. Его надо написать один раз либо использовать методы из ArraysKt👍8❤5👏2
Mike's IDE Extensions-0.34-base.jar
392.8 KB
🔌 Поддержка K2 готова, а вместе с ней
• улучшен фикс в инспекции по ловле регекспов
• жалуюсь на
• блюду порядок сортировки атрибутов стиля при вызове
В целом получился чисто технический релиз, основная фишка лишь в том, что теперь плагин работает 😌
• улучшен фикс в инспекции по ловле регекспов
[а-яА-Я]• жалуюсь на
"#ЦЦВВЕЕТТ".toColorInt() так же, как на Color.parseColor• блюду порядок сортировки атрибутов стиля при вызове
context.withStyledAttributes так же, как и для context.obtainStyledSttributesВ целом получился чисто технический релиз, основная фишка лишь в том, что теперь плагин работает 😌
🔥3❤1