Итак, вчера мы с вами поиграли в джавовский вариант «Интеллектуальное казино против знатоков», и при этом, при всем уважении к хабровчанам, телезрители выиграли! Победителем этого этапа стал Сергей SerCe Целовальников, решивший три задачи. Как и обещали — мы вручаем ему небольшой приз: VIP-билет на JPoint!
Но радости в сторону. Вчера нас с телезрителями обвинили в том, что большинство вчерашних задач были связаны с Java весьма косвенно. Мы принимаем это обвинение, и поэтому сегодня у нас вариант на чистой Java! никаких спрингов, эксэмэлей и паттернов. Это будет настоящее испытание для истинных любителей хардкора!
Под катом — ответы на вчерашний раунд и суперблиц! Против знатоков сегодня играет телезритель из Петербурга Андрей apangin Паньгин.
Но сначала ответы на вчерашние задачи.
А теперь самая хардкорная задача вчерашнего дня, по мнению президента, премьера и министра обороны элитарного клуба.
Суперблиц от Андрея Паньгина
Вот и настало время истинного хардкора! Знатоки поднимают глаза вверх, и на их экранах появляется улыбающийся apangin. Он кагбэ машет всем знатокам и кидает им тысячи воздушных поцелуев.
Вопрос первый
Вопрос второй
Вопрос третий
Такие дела!
Ваши ответы пишите ниже в комментариях под спойлером. У вас есть 12 часов. Первый, кто решит все три задачи, получит специальный приз от Андрея и Одноклассников. Остальные решившие все три задачи получат памятные призы от JUG.ru.
Удачи!
UPDATE:
Победил lany. Его ответы можете найти ниже в каментах! А вот как его награждали:
Но радости в сторону. Вчера нас с телезрителями обвинили в том, что большинство вчерашних задач были связаны с Java весьма косвенно. Мы принимаем это обвинение, и поэтому сегодня у нас вариант на чистой Java! никаких спрингов, эксэмэлей и паттернов. Это будет настоящее испытание для истинных любителей хардкора!
Под катом — ответы на вчерашний раунд и суперблиц! Против знатоков сегодня играет телезритель из Петербурга Андрей apangin Паньгин.
Но сначала ответы на вчерашние задачи.
Различные аспекты от Николая Гарбузова
Правильный ответ. Скомпилируется успешно, но ничего не выведет на консоль при компиляции. При этом вызов метода Quiz.count в рантайме приведет к StackOverflowError ошибке!
Скомпилируется ли следующий аспект AJC компилятором?
Если да — то что он выведет на консоль при компиляции?
public aspect QuizAspect { public static int count(int i) { return i++; } before (int n) : execution(public int QuizAspect.count(int)) && args(n) && if(QuizAspect.count(1)>1) { System.out.println("QuizAspect " + n); } }
Правильный ответ. Скомпилируется успешно, но ничего не выведет на консоль при компиляции. При этом вызов метода Quiz.count в рантайме приведет к StackOverflowError ошибке!
Можно подумать, что выражение в if() начнет выпоняться при компиляции кода, но это не так.
(не стоит путать аспекты с мета программированием)
Аспект лишь встроит код совета внутрь метода count, добавит if() к этому коду.
Выражения от Владимира Ситникова
В чём подвох удалять Java-комментарии таким выражением? Укажите 3 причины, почему так делать нельзя. (считаем, что исходник написан нормальными символами) —Pattern.compile("/\\*(?:[^*]|\\*[^/])*\\*/")
Ответ
- Если это джавовская строка, а в ней якобы комент — то не будет работать
- Если комент заканчивается на две звездочки слэш, то работать не будет
- Если комментарий большой, то получим Будет StackOverflowError, т.к. java regexp’ы используют backtracking.
Пофиксить можно двумя способами:
- Possessive quantifier:
Pattern.compile("/\\*(?:[^*]|\\*(?!/))*+\\*/")
- Independent group:
Pattern.compile("/\\*(?>,[^*]|\\*(?!/))*\\*/")
Спринговые контексты от Николай Алименкова
Есть 2 Spring контекста:
1. a.xml с бином
<util:list id="myList"> <value>3</value> <value>4</value> </util:list>
2. b.xml с бином
<util:list id="myList"> <value>6</value> </util:list>
Что напечатает такой фрагмент кода:
System.out.println(new ClassPathXmlApplicationContext("a.xml", "b.xml").getBean("myList"));
И как можно заставить его бросить ошибку, не изменяя логику работы кода?
Ответ
Выведет 6, а заставить бросить ошибку можно установив allowBeanDefinitionOverriding у контекста в false.
А теперь самая хардкорная задача вчерашнего дня, по мнению президента, премьера и министра обороны элитарного клуба.
OOM от Никиты Сальникова-Тарновского
Ответ
В обоих случаях javac понимает, что переменная bytes не будет использована после окончание внутреннего блока. Поэтому второй созданный массив, который мы кладем в переменную bytes1, займет тот же слот, что и переменная bytes. Как следствие, после выполнения присваивания bytes1 < — new bytes[SIZE] значение, которое было в переменной bytes становится недоступным и GC может его удалить. Тем самым OOM2 требует всего лишь 70% от хипа, а не 105%.
Update 1
Вот интересный анализ от cheremin на тему этой задачи.
Update 2
Для тех, у кого оба варианта бросают OOM: для воспроизведения бага запускайте вашу JVM с опцией -server
Ниже приведены 2 программы. Каждая из них пытается аллоцировать суммарно памяти больше размера хипа. Но одна из них выкидывает java.lang.OutOfMemoryError, а вторая нет. Почему?
public class OOM1 { private static final int SIZE = (int) (Runtime.getRuntime().maxMemory() * 0.55); public static void main(String[] args) { { byte[] bytes = new byte[SIZE]; System.out.println(bytes.length); } byte[] bytes1 = new byte[SIZE]; System.out.println(bytes1.length); System.out.println("I allocated memory successfully"); } } public class OOM2 { private static final int SIZE = (int) (Runtime.getRuntime().maxMemory() * 0.35); public static void main(String[] args) { { byte[] bytes = new byte[SIZE]; System.out.println(bytes.length); } byte[] bytes1 = new byte[SIZE]; System.out.println(bytes1.length); byte[] bytes2 = new byte[SIZE]; System.out.println(bytes2.length); System.out.println("I allocated memory successfully"); } }
Ответ
В обоих случаях javac понимает, что переменная bytes не будет использована после окончание внутреннего блока. Поэтому второй созданный массив, который мы кладем в переменную bytes1, займет тот же слот, что и переменная bytes. Как следствие, после выполнения присваивания bytes1 < — new bytes[SIZE] значение, которое было в переменной bytes становится недоступным и GC может его удалить. Тем самым OOM2 требует всего лишь 70% от хипа, а не 105%.
Update 1
Вот интересный анализ от cheremin на тему этой задачи.
Update 2
Для тех, у кого оба варианта бросают OOM: для воспроизведения бага запускайте вашу JVM с опцией -server
Груви от Баруха Садогурского и Спринг от Жени Борисова
А вот тут нас ждет облом. jbaruch и EvgenyBorisov попросили меня сохранить интригу и не публиковать ответы на их задачи до JPoint. Сказали, чтобы все желающие узнать ответы приходили на их доклады.
Желающие узнать ответы — попробуйте написать им в личку. Может быть они сжалятся над вами!
Желающие узнать ответы — попробуйте написать им в личку. Может быть они сжалятся над вами!
Суперблиц от Андрея Паньгина
Вот и настало время истинного хардкора! Знатоки поднимают глаза вверх, и на их экранах появляется улыбающийся apangin. Он кагбэ машет всем знатокам и кидает им тысячи воздушных поцелуев.
Вопрос первый
Что не так с этим кодом? Как его исправить?
public static double[] getRandomVector(int size) { double[] vector = new double[size]; Arrays.parallelSetAll(vector, i -> Math.random()); return vector; }
Вопрос второй
Как такое может быть, что публичный метод работает заметно быстрее идентичного приватного?
public class Modifiers { static final Inner inner = new Inner(); static class Inner { int x = 1; int getX1() { return x; } int getX2() { return getX1(); } int getX3() { return getX2(); } int getX4() { return getX3(); } int getX5() { return getX4(); } int getX6() { return getX5(); } int getX7() { return getX6(); } int getX8() { return getX7(); } int getX9() { return getX8(); } private int getPrivate() { return getX9(); } public int getPublic() { return getX9(); } } @Benchmark public int testGetPrivate() { return inner.getPrivate(); } @Benchmark public int testGetPublic() { return inner.getPublic(); } }
Вопрос третий
Можно ли в Java создать класс (именно класс, не интерфейс) без единого конструктора, даже приватного?
Такие дела!
Ваши ответы пишите ниже в комментариях под спойлером. У вас есть 12 часов. Первый, кто решит все три задачи, получит специальный приз от Андрея и Одноклассников. Остальные решившие все три задачи получат памятные призы от JUG.ru.
Удачи!
UPDATE:
Победил lany. Его ответы можете найти ниже в каментах! А вот как его награждали:
MaximChistov
На хабре целая статья про этот изврат была, нагуглят быстро :)
23derevo Автор
deleted
apangin
Не экземпляр класса создать, а сам класс такой, что Class.getDeclaredConstructors() вернёт для него пустой массив.
lany
3.
Работает в javac, не работает в ecj. Я про это писал здесь. Второй вариант — сгенерировать класс на лету, например, с помощью ASM, как это сделал LSD.
Над вторым ща подумаю.
lany
lany
А вот getPublic пролазит:
23derevo Автор
lany
Поймаю :D
lany
m08pvv
о том, что скорее всего Math.random() в java не очень хорош в concurrency, о чём можно почитать в документации и убедиться. Соответсвенно, использовать ThreadLocalRandom
Mercury13
2. Точно не в курсе, но, думаю, какое-то хитрое поведение оптимизатора, который понимает, что getPublic() можно свернуть в return x; а getPrivate() — почему-то нет.
3. На уровне байткода, разумеется, можно. На уровне языка… думаю, что-нибудь наподобие внутреннего private abstract без нестатических данных.
23derevo Автор
Mercury13
Я подсмотрел правильные ответы.
Второе — ключи компилятора. В такие дебри не совался.
А третье — просто не было времени запустить NetBeans и посмотреть.
lany
Вот на вчерашней Никитиной задаче я затупил, хотя про слоты всё знал. Ну да, сейчас понял. Достаточно первый пример модифицировать вот так, и он заработает:
Мораль: пойду на доклад к Никите :D
23derevo Автор
ты там осторожно, а то упоритесь вместе по хардкору и натворите чего-нибудь не того!
ragesteel
Спасибо что теперь хоть по Java, без всяких там Springов Groovyей!
Во второй задаче — всё просто, вызов private будет обёрнут ещё в access метод, соответственно лишних overhead.
По третьей — тут уж я не такой знаток. Наверняка можно какой-нибудь библиотекой кодогенерации так сделать. Получить список констркторов, все удалить и сохранить. А вот чтобы из Java. Немного гуглежа и я нашёл обсуждение на StackOveflow — да, javac добавляет конструктор по умолчанию, это положено по спецификации. А при создании java байткода вручную таких ограничений нет, спецификация класс-файла не обязывает иметь конструктор.
apangin
leventov
Твоя задачка про рандомы натолкнула меня на некоторые размышления
relgames
2 — Разница в том, что для private метода генерируется специальный метод access (предполагаю, т.к. внутренний класс хранится в отдельном class файле, иначе никак):
3 — не хочу даже думать над этим извратом, один вопрос — зачем???
leventov
1. Поясните, что вы имеете ввиду
relgames
Тесты заставили меня поменять свои взгляды gist.github.com/relgames/840d89280237acd4c385
На малых значениях последовательное заполнение действительно быстрее параллельного, но не из-за кэша, а из-за расходов на параллельность. При увеличении размера массива параллельная обработка становится быстрее. В целом, это доказывает рассуждения о том, что parallelStream() нужен не везде, а в тех случаях, когда элементов много либо же когда операции долгие.
Неожиданным открытием было то, что ThreadLocalRandom работает быстрее Random всегда и везде — на больших, маленьких, параллельных и последовательных операциях. Подозреваю, это из-за синхронизации в Random.
leventov
А также недавнее упоминание на русском:habrahabr.ru/company/jugru/blog/255219
Кстати, 23derevo, выходит что Walrus либо оговорился (100 микросекунд/миллисекунд), либо вы неправильно расшифровали.
Да :)
23derevo Автор
Я спрошу его.
23derevo Автор
Да, Серега ошибся. Хотя оценка Дага выглядит ну ОЧЕНЬ консервативной.
leventov
Как раз наоборот, 100 микросекунд это же в 1000 раз меньше, чем 100 миллисекунд. То есть это довольно рискованная, казалось бы, оценка
23derevo Автор
тьфу, вечер пятницы :)
Спросил Андрея тут.
lany
С сегодняшнего дня перехожу на ThreadLocalRandom!
23derevo Автор
а у тебя так много рандома под контеншеном? ;)
Math.random() компактнее выглядит, глаз не режет. Вот увидит любой из твоих менее шарящих коллег ThreadLocalRandom и заподозрит подвох. Начнет копать не туда… Тебе оно нада?
lany
Math.random() — это вообще плохо. Во-первых, с него можно получить только дробные числа от 0 до 1. Это требуется не очень часто. Чаще нужно Random.nextInt(a, b). А таскать где-то созданный Random на протяжении всего алгоритма может быть не очень удобно. В результате люди либо пишут что-то уродливое вроде (int)(Math.random()*(b-a))+a. В этом случае ThreadLocalRandom.current().nextInt(a, b) гораздо меньше режет глаз.
По хорошему стохастический алгоритм должен принимать на вход фабрику рэндомов. Тогда можно и стабильного поведения для тестов добиться, и тредлокал использовать, если скорость нужна.
lany
relgames
lany
Кстати, про regexp — не могу пройти мимо:
Нет, нельзя это фиксать! Я не хочу видеть ни первый, ни второй код в своём приложении. Надо написать грамматику, хоть antlr, хоть на худой конец javacc. Это очень просто, куча примеров в сети, и главное — легко поддерживать и дополнять новыми фичами.
Я неплохо читаю регексы, я даже кроссворд разгадал примерно за час. Но моё, возможно, холиварное мнение в том, что если начинают требоваться штуки с бэктрекингом, то убирайте это и пишите грамматику.
werewolfspb
будут использовать общий экземпляр класса Random. Там, конечно, не просто synchronize, а CAS вызовы внутри в nextDouble(),
но тем не менее, авторы JDK8 предлагают в таких случаях использовать ThreadLocalRandom.
Просто, чтобы потоки не дрались между собой. Соответственно фикс простой: