Odnoklassniki analisando no Joker 2019



De 28 a 29 de outubro, o Joker 2019 foi realizado em São Petersburgo - a maior e mais hardcore conferência na vastidão da Rússia dedicada ao desenvolvimento de Java. O evento foi realizado pela sétima vez e, como sempre, bateu o recorde de presença, desta vez atraindo mais de 2000 especialistas.

Os colegas de classe tradicionalmente participam do Joker como parceiros do evento. Este ano, em nosso estande, pode-se tentar lidar com as famosas tarefas "insolúveis" dos principais engenheiros da OK.RU. Os participantes da conferência que responderam às perguntas receberam os prêmios corretamente.

Para ser honesto, devo dizer que, dos 1.000 folhetos com as tarefas que entregamos, menos de 100 foram devolvidos.O melhor foi a solução, que obteve 4,5 pontos em 5.

Publicamos tarefas e suas soluções para que você possa testar sua força.

1. Enum heróico


O código fonte de um jogo pouco conhecido revelou esse código. Qual é a má implementação do Group.of e como corrigi-lo?

 enum Group { Few(1, 4), Several(5, 9), Pack(10, 19), Lots(20, 49), Swarm(50, Integer.MAX_VALUE); Group(int min, int max) { ... } public static Group of(int count) { for (Group group : Group.values()) { if (count >= group.min && count <= group.max) { return group; } } throw new IllegalArgumentException(); } } 

Solução
Se você não fala sobre estilos de codificação, esse fragmento tem uma desvantagem objetiva - um potencial problema de desempenho. Embora a pesquisa linear frequentemente se mostre um gargalo, nesse caso, não é o caso, porque esse enum possui apenas cinco elementos. E o que realmente pode afetar negativamente o desempenho é a alocação excessiva de memória ao chamar Group.values() . O problema é que o método values() de enum sempre retorna uma nova cópia da matriz e o HotSpot ainda não é capaz de otimizá-la. Uma solução simples é fazer sua própria cópia da matriz values() e iterar sobre ela:

 private static final Group[] ALL_GROUPS = Group.values(); public static Group of(int count) { for (Group group : ALL_GROUPS) { .... } 


2. Sonhos


O Java 13 já foi lançado e o Nikolai ainda compreende apenas os fluxos. Indique erros no método que calcula a diferença entre os elementos de fluxo máximo e mínimo.

 int getDiameter(Stream<Integer> stream) { int min = stream.min(Integer::compare).get(); int max = stream.max(Integer::compare).get(); return max - min; } 

Solução
Os fluxos em Java geralmente são únicos: a chamada da segunda operação do terminal (neste caso, max ) falhará:

 java.lang.IllegalStateException: stream has already been operated upon or closed 

Além disso, min e max retornam Optional , a operação get() na qual lançará NoSuchElementException para um fluxo vazio. Portanto, é mais correto verificar isPresent() antes de chamar get() ou usar outros métodos Optional : orElse , ouElseThrow , etc.

Finalmente, o fato de que a diferença entre as duas int não pode mais se encaixar no int não escapará ao desenvolvedor cuidadoso e valeria a pena alterar o tipo do valor de retorno para long .

3. Buffer seguro


ByteBuffer primitivo de sincronização Java pode tornar o thread de operações de put e get seguro em um ByteBuffer genérico?

 final ByteBuffer buf = ByteBuffer.allocate(SIZE); int get(int offset) { return buf.get(offset); } void put(int offset, int value) { buf.putInt(offset, value); } 

Escolha a opção mais eficiente se você souber que existem muitos threads e obtenha execuções com muito mais frequência do que colocar.

  • sincronizado
  • Reentrantlock
  • ReentrantReadWriteLock
  • Stampedlock
  • Semáforo
  • Ler e escrever int em Java é sempre atômico

Solução
ReentrantReadWriteLock implora por ReentrantReadWriteLock leitor e gravador, e geralmente essa será uma solução eficaz. Mas observe que, nesse caso, as operações get e put são muito simples - a probabilidade de uma put competitiva poder interferir com get é pequena; além disso, as condições de put são menos prováveis ​​de ocorrer com operações de put. Portanto, você pode aplicar o mecanismo de bloqueio otimista fornecido pelo StampedLock .

StampedLock será mais eficiente que o ReentrantReadWriteLock devido ao fato de que, em caso de sucesso otimista do caminho rápido, as variáveis ​​compartilhadas não serão atualizadas, enquanto o ReentrantReadWriteLock realiza no mínimo um CAS .

4. Presentes


Ilya está desenvolvendo uma vitrine de presentes em uma rede social. Ajude-o a escrever o método add para uma estrutura que não contenha mais que N dos presentes mais recentes. Um presente não deve ser adicionado se ele já estiver presente ou se for mais antigo que o restante de N.

 interface Present { long getId(); Date getCreated(); } void add(Present p) { // Implement me } 

Solução
TreeSet ou o PriorityQueue naturalmente adequado como uma estrutura de dados para adicionar efetivamente presentes e excluir os mais antigos não pior do que para O (log N). Todo o truque é apenas no comparador: não é suficiente comparar presentes apenas com getCreated() , porque a data de criação não precisa ser única. Portanto, você precisa comparar primeiro por getCreated() e depois por getId() . Esse comparador garantirá a exclusividade dos elementos e a ordenação por data.

 TreeSet<Present> tree = new TreeSet<>( Comparator.comparing(Present::getCreated) .thenComparing(Present::getId)); 

Ainda é uma questão pequena: ao adicionar um presente, verifique se o tamanho não excede N e, se necessário, exclua o primeiro elemento mais antigo da coleção.

 void add(Present p) { if (tree.add(p) && tree.size() > N) { tree.pollFirst(); } } 


5. Você não vai esperar


Por que Julia nunca esperará o final deste programa?

 var executor = Executors.newFixedThreadPool(4); for (File f : File.listRoots()) { executor.submit(() -> f.delete()); } executor.awaitTermination(2, TimeUnit.HOURS); 

Solução
A documentação waititTermination sugere que a solicitação de desligamento deve preceder a execução. É simples: Julia esqueceu de chamar executor.shutdown () .

Source: https://habr.com/ru/post/pt474502/


All Articles