Итак, вчера мы с вами поиграли в джавовский вариант «Интеллектуальное казино против знатоков», и при этом, при всем уважении к хабровчанам, телезрители выиграли! Победителем этого этапа стал Сергей SerCe Целовальников, решивший три задачи. Как и обещали — мы вручаем ему небольшой приз: VIP-билет на JPoint!
Но радости в сторону. Вчера нас с телезрителями обвинили в том, что большинство вчерашних задач были связаны с Java весьма косвенно. Мы принимаем это обвинение, и поэтому сегодня у нас вариант на чистой Java! никаких спрингов, эксэмэлей и паттернов. Это будет настоящее испытание для истинных любителей хардкора!
Под катом — ответы на вчерашний раунд и суперблиц! Против знатоков сегодня играет телезритель из Петербурга Андрей apangin Паньгин.
![](//habrastorage.org/files/48e/b31/3d4/48eb313d4f9140a2a314017840b42a49.jpg)
Но сначала ответы на вчерашние задачи.
А теперь самая хардкорная задача вчерашнего дня, по мнению президента, премьера и министра обороны элитарного клуба.
Суперблиц от Андрея Паньгина
Вот и настало время истинного хардкора! Знатоки поднимают глаза вверх, и на их экранах появляется улыбающийся apangin. Он кагбэ машет всем знатокам и кидает им тысячи воздушных поцелуев.
Вопрос первый
Вопрос второй
Вопрос третий
Такие дела!
Ваши ответы пишите ниже в комментариях под спойлером. У вас есть 12 часов. Первый, кто решит все три задачи, получит специальный приз от Андрея и Одноклассников. Остальные решившие все три задачи получат памятные призы от JUG.ru.
Удачи!
UPDATE:
Победил lany. Его ответы можете найти ниже в каментах! А вот как его награждали:
Но радости в сторону. Вчера нас с телезрителями обвинили в том, что большинство вчерашних задач были связаны с Java весьма косвенно. Мы принимаем это обвинение, и поэтому сегодня у нас вариант на чистой Java! никаких спрингов, эксэмэлей и паттернов. Это будет настоящее испытание для истинных любителей хардкора!
Под катом — ответы на вчерашний раунд и суперблиц! Против знатоков сегодня играет телезритель из Петербурга Андрей apangin Паньгин.
![](http://habrastorage.org/files/48e/b31/3d4/48eb313d4f9140a2a314017840b42a49.jpg)
Но сначала ответы на вчерашние задачи.
Различные аспекты от Николая Гарбузова
Правильный ответ. Скомпилируется успешно, но ничего не выведет на консоль при компиляции. При этом вызов метода 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. Его ответы можете найти ниже в каментах! А вот как его награждали:
![](http://habrastorage.org/files/a84/efd/950/a84efd950f644137a66b304f506291cd.jpg)
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.
Просто, чтобы потоки не дрались между собой. Соответственно фикс простой: