
Всем привет!
Закончилась одна из самых хардкорных конференций по Java – JPoint 2019, она проходила в седьмой раз и как всегда побила рекорд по посещаемости, в этот раз мероприятие привлекло более 1700 специалистов в области Java-разработки.
«Одноклассники» принимали участие во всех конференциях JPoint. Начиная с 2013 мы активно поддерживаем JPoint и на своих стендах устраиваем для участников различные активности по проверке знаний Java. В этом году у нас были знаменитые «нерешаемые» задачи от ведущих разработчиков OK.ru. Участники конференции, правильно ответившие на вопросы, получили призы.
Справедливости ради надо сказать, что из 600 листочков с задачами, которые мы раздали, обратно было получено менее 100, средний балл равен примерно 0.25.
Лучшим оказалось решение, набравшее 4 балла из 5 возможных.
Мы публикуем задачи и их решения, чтобы вы смогли проверить свои силы.
Битам быть
Эту задачу решили 40%, сдавших ответы.
Михаил создаёт потокобезопасный аналог
BitSet. Допишите реализацию метода setBit(). Для простоты можно считать размер
BitSet постоянным.public class ConcurrentBitSet { private final AtomicLongArray bits; public ConcurrentBitSet(int size) { assert size >= 0; int words = (size + 63) / 64; bits = new AtomicLongArray(words); } public void setBit(int index) { // TODO: Implement me! } }
Решение
Реализация с помощью
Аналогично выглядит реализация на старом-добром
updateAndGet()/getAndUpdate(), доступных с Java 8, может выглядеть так:public void setBit(int index) { int word = index >> 6; long mask = 1L << index; bits.updateAndGet(word, value -> value | mask); }
Аналогично выглядит реализация на старом-добром
compareAndSet():public void setBit(int index) { int word = index >> 6; long mask = 1L << index; long oldValue; long newValue; do { oldValue = bits.get(word); newValue = oldValue | mask; } while (!bits.compareAndSet(word, oldValue, newValue)); }
Enum уже не тот
Эту задачу решили 45%, сдавших ответы.
Татьяна хочет проверить, являются ли два объекта константами одного и того же
enum. Что она не учла?boolean sameEnum(Object o1, Object o2) { return o1.getClass().isEnum() && o1.getClass() == o2.getClass(); }
Решение
Подсказка кроется в документации к методу Enum.getDeclaringClass(), который используется, например, в
Для enum-констант с непустыми телами создаются промежуточные классы, поэтому правильный ответ может выглядеть так:
Enum.compareTo():public final Class<E> getDeclaringClass() { Class<?> clazz = getClass(); Class<?> zuper = clazz.getSuperclass(); return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper; }
Для enum-констант с непустыми телами создаются промежуточные классы, поэтому правильный ответ может выглядеть так:
boolean sameEnum(Object o1, Object o2) { return o1 instanceof Enum && o2 instanceof Enum && ((Enum) o1).getDeclaringClass() == ((Enum) o2).getDeclaringClass(); }
Некомпилируемые связи
Эту задачу решили 42%, сдавших ответы.
Имеется следующий интерфейс:
interface Link<T> { T next(); }
Измените сигнатуру (но не тело) метода
getTail(), чтобы код компилировался без ошибок и предупреждений.Link getTail(Link head) { if (head.next() == null) { return head; } return getTail(head.next()); }
Решение
Правильных минимальных ответов всего три:
Как это ни парадоксально выглядит, такая сигнатура не по зубам компилятору Java:
<T extends Link<T>> Link<T> getTail(Link<T> head) <T extends Link<T>> Link<T> getTail(T head) <T extends Link<T>> T getTail(T head)
Как это ни парадоксально выглядит, такая сигнатура не по зубам компилятору Java:
<T extends Link<T>> T getTail(Link<T> head)
Мессенджер
Эту задачу решили 14%, сдавших ответы.
Костя разрабатывает приложение для обмена сообщениями. Укажите ошибки в методе для отправки сообщения по сети.
void send(SocketChannel ch, String message) throws IOException { byte[] bytes = message.getBytes(); ByteBuffer header = ByteBuffer.allocate(4); header.putInt(bytes.length); ch.write(header); ch.write(ByteBuffer.wrap(bytes)); }
Решение
В этом коде имеются как минимум три ошибки:
Так может выглядеть исправленная версия:
- String.getBytes() использует кодировку платформы по-умолчанию, которая определяется на старте VM и зависит от локали и кодировки ОС, поэтому лучше явно передавать Charset
- После записи и перед чтением из
ByteBufferнеобходимо вызвать метод flip()/rewind()/position(0), в противном случае вSocketChannel(молча!) ничего не запишется
- Наконец, метод SocketChannel.write() возвращает количество успешно записанных байт и не гарантирует, что весь буфер будет записан за один вызов
Так может выглядеть исправленная версия:
void send(SocketChannel ch, String message) throws IOException { byte[] bytes = message.getBytes(StandardCharsets.UTF_8); ByteBuffer header = ByteBuffer.allocate(4); header.putInt(bytes.length); header.flip(); while (header.hasRemaining()) { ch.write(header); } ByteBuffer body = ByteBuffer.wrap(bytes); while (body.hasRemaining()) { ch.write(body); } }
Java в контейнере
Эту задачу решили 7.5%, сдавших ответы.
Какие параметры JVM следует прописать Алексею, чтобы не позволить ОС Linux убить Java-процесс из-за превышения лимита памяти, отведённого на контейнер?
-Xmx-XX:MaxMetaspaceSize-XX:ReservedCodeCacheSize-XX:+UseContainerSupport-XX:MaxRAMPercentage- Память JVM нельзя ограничить
Решение
Память, потребляемая Java-процессом, далеко не ограничивается только хипом, Metaspace и Code Cache. Многие другие структуры JVM также занимают память, причём не все из них регулируются настройками. Помимо виртуальной Java машины нативную память выделяет Java Class Library и пользовательский код посредством Direct ByteBuffers и Mapped ByteBuffers.
Параметр
Параметр
UseContainerSupport совместно с MaxRAMPercentage влияет лишь на размер хипа. Таким образом, нет гарантированного способа избежать превышения лимита только с помощью флагов JVM, и правильным ответом будет последний. Подробнее об использовании памяти Java процессом можно посмотреть в докладе Андрея Паньгина на Joker 2018 «Память Java процесса по полочкам».