Всем привет!
Закончилась одна из самых хардкорных конференций по 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 процесса по полочкам».Комментарии (2)
Maccimo
10.04.2019 23:33Спасибо за задачки.
На фоне «у нас синтаксическая ошибка!», «а у нас сырые типы в 2019!» с соседних стендов очень контрастируют.
Интересно, почему у «Мессенджера» такой низкий процент, неужели никто не использует NIO?
gnkoshelev
Постоянно забываю про «новые» методы
updateAndGet
и др. и пишу по старинке, повторяя код из них.А последний вопрос я, видимо, неправильно понял, выбрав вариант с
-XX:+UseContainerSupport
.В случае с UseContainerSupport размер хипа будет выбираться исходя из ограничений контейнера, а не физических размеров оперативной памяти, поэтому хип гарантированно не выйдет за ограничение (с немалым таким запасом).
К слову, и без всяких контейнеров OOM Killer может прийти за java-процессом. :)
P.S. Спасибо за топовые задачки (и настолку ;)).