📜 ⬆️ ⬇️

Parsing Joker 2018



Aloha!

That ended one of the most hardcore conferences in the world of Java - Joker 2018, which traditionally takes place in St. Petersburg at Expoforum. This year a record number of participants took part in the conference. Odnoklassniki traditionally offered to help our developers solve non-trivial tasks that arise when creating one of the most heavily loaded Java projects.
')
Those who responded well to questions received prizes, and we offer you a brief analysis of our problems. We hid the correct answers under the spoiler, mind you, to open only after they themselves thought of the solution ;-)

Go!

Deduplicator


Cyril wants to save memory by deduplicating objects equal in equals() . Help him implement the thread-safe dedup method by analogy with String.intern , but not only for strings.

 public static Object dedup(Object obj) { } 

Decision
Scratching the back of his head first, Cyril was able to come up with several options for solving this problem, but they were all somehow wrong. Then he scratched his nose and the dock about java.util.concurrent , recalled the wonderful method computeIfAbsent . This method will perform the lambda passed to it in the parameter only if there is no key in the Map , write its result and return it. If there is already such a key, the lambda will not be calculated, and the current value associated with the key will be returned. In addition, Kirill recalled, for ConcurrentHashMap this method works atomically, which makes it very elegant to solve the problem. Satisfied Cyril wrote this code:

 private static final ConcurrentHashMap map = new ConcurrentHashMap(); public static Object dedup(Object obj) { return map.computeIfAbsent(obj, o -> o); } 

and gladly scratched my nose again.

IP address


Dima is developing a new network protocol. Correct the error in his method to translate the IPv4 address represented as a byte array into the string.

 String ipToString(byte[] ip) { return ip[0] + '.' + ip[1] + '.' + ip[2] + '.' + ip[3]; } 

Decision
The first error was immediately shown by the IDE, without letting Dima even finish the method to the end. The '.' of type char , added to the byte as an integer type. Replacing '.' on "." , Dima was so pleased with the successfully compiled code that he immediately launched it without testing. “Ahhhhhhh, Dima”, thought the JVM and gave out some nonsense instead of an IP address. Unlike Dima, the JVM knew exactly that in Java, the type byte serves to store signed numbers, that is, all addresses that have octets greater than 127 will be represented in Java by negative numbers. By the rules of casting these numbers to int , the negative sign of the number is the same as in the original byte. Oh, Dmitry, it was necessary to take additional measures in order to drop the symbolic part, for example:

 return (ip[0] & 255) + "." + (ip[1] & 255) + "." + (ip[2] & 255) + "." + (ip[3] & 255); 


Agile mixer


Marina needs to mix the elements of the list in a random order. Why is this option not suitable, and how would you fix it?

 Random random = ThreadLocalRandom.current(); list.sort((o1, o2) -> { return random.nextBoolean() ? +1 : -1; }); 

Decision
Marina obviously forgot that the Comparator contract requires stability: when comparing two identical values, the comparison result should be the same. And in Marina’s implementation, the result for each pair is strictly random, which could easily lead to the exception of java.lang.IllegalArgumentException: Comparison method violates its general contract ! If Marina read documentation in the evenings, she would know that in this case it is best to use the Collections.shuffle() method.

Answer: Comparator contract violated. Sorting can throw an exception. It is better to use the Collections.shuffle() method.

Den of the Functionalist


Egor loves to write in a functional style, not worrying about the effectiveness of the code. Estimate how many objects each call to this method creates, if an ArrayList of 10 lines is passed to it?

 Predicate<String> equalsAny(List<String> list) { Predicate<String> p = s -> false; for (String s : list) { p = p.or(s::contains); } return p; } 

Decision
Unlike Egor, the pedantic Alina does not like to write everything in a functional style, because she knows how to count overhead. Single line

p = p.or(s::contains);

will create two objects at once: one as the result of calling p.or() , and the second to create the predicate s::contains . The latter cannot be cached, since it captures the variable s in the context. Multiplying by the number of iterations, we get 20 objects. But also a hidden Iterator can be created if the JIT does not optimize it. “20 or even 21 objects, if not lucky, are sinful,” Alina thought.

Answer: 10 predicates or + 10 predicates contains + 1 Iterator depending on JIT optimizations.

Maxim turns on the maximum


Maxim calculates the maximum in a multi-threaded program, but wants to do without locks. Help him fix the error.

 AtomicLong max = new AtomicLong(); void addValue(long v) { if (v > max.get()) { max.set(v); } } 

Decision
Oh, Maxim! The use of AtomicLong does not make the program thread-safe. For this there is an atomic operation AtomicLong.compareAndSwap . And starting with Java 8, it’s not at all necessary to write the CAS loop yourself, because the remarkable atomic method accumulateAndGet . And here it is convenient to use just it:

 void addValue(long v) { max.accumulateAndGet(v, Math::max); } 

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


All Articles