小丑拼图2018



阿罗哈!

因此,Java世界上最顽固的会议之一-传统上在世博会圣彼得堡在圣彼得堡举行的Joker 2018已经结束。 今年的会议有创纪录的参与者参加。 传统上,Odnoklassniki提供了帮助我们的开发人员解决在创建负载最大的Java项目之一时出现的重要问题。

那些回答问题的人都很受好评,我们将为您简要分析我们的问题。 我们只有在我们自己找出解决方案之后,才能将正确的答案隐藏在扰流板下,以便打开;-)

走吧

重复数据删除器


Cyril希望通过对equals()相等的对象进行重复数据删除来节省内存。 通过与String.intern类似的方式帮助他实现线程安全的dedup方法,但不仅限于字符串。

 public static Object dedup(Object obj) { } 

解决方案
塞里尔挠了挠头,提出了解决这个问题的几种选择,但是它们全都错了。 然后,他computeIfAbsent一下关于java.util.concurrent ,他想起了很棒的computeIfAbsent方法。 仅当Map没有键时,此方法才会执行在参数中传递给它的lambda,写入其结果并返回。 如果已经存在这样的密钥,则不会计算lambda,并且将返回与该密钥关联的当前值。 另外,基里尔回忆说,对于ConcurrentHashMap此方法是原子工作的,它使您可以非常优雅地解决问题。 满意的西里尔写了这段代码:

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

又高兴地再次scratch了鼻。

IP地址


Dima正在开发一种新的网络协议。 更正他将字节数组表示的IPv4地址转换为字符串的方法中的错误。

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

解决方案
IDE会立即显示第一个错误,从而阻止Dima甚至将方法添加到末尾。 符号'.' 具有char类型的char作为整数类型添加到字节中。 替换为'.'"." ,Dima对成功编译的代码感到非常高兴,以至于他立即进行了未经测试的启动。 “ Ay-ah-ah,Dima”想到了JVM,并给出了一些废话而不是IP地址。 与Dima不同,JVM确实知道在Java中, byte类型用于存储带符号的数字,即,八位字节大于127的所有地址在Java中都将由负数表示。 根据将这些数字强制转换为int的规则,数字的负号与原始字节中的相同。 啊,德米特里(Dmitry),有必要采取其他措施以丢弃标志部分,例如:

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


搅拌机


Marina需要按随机顺序混合列表项。 为什么此选项不合适,您将如何解决?

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

解决方案
Marina显然忘记了Comparator合同需要稳定性:比较两个相同的值时,比较的结果应该相同。 并且在Marina的实现中,每对的结果都是严格随机的,这很容易导致java.lang.IllegalArgumentException: Comparison method violates its general contract异常java.lang.IllegalArgumentException: Comparison method violates its general contract ! 如果Marina在晚上阅读文档,她会知道在这种情况下最好使用Collections.shuffle()方法。

答:违反比较者合同。 排序可能会引发异常。 最好使用Collections.shuffle()方法。

功能诞生场景


Egor喜欢以功能性风格编写代码,而不在乎代码的有效性。 估计如果将10行的ArrayList传递给该方法,则每次调用此方法都会创建多少个对象?

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

解决方案
与Yegor不同,学究的Alina不喜欢以功能样式编写所有内容,因为她知道如何计算间接费用。 单行

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

它一次创建两个对象:一个是调用p.or()的结果,第二个是创建s::contains谓词的结果。 后者无法缓存,因为它在上下文中捕获了变量s 。 乘以迭代次数,我们得到20个对象。 但是,如果JIT不对其进行优化,则还可以创建一个隐藏的Iterator 。 “如果不走运,有20甚至21个物体是罪人,”阿丽娜想。

答案:10个谓词or + 10个谓词contains + 1个Iterator具体取决于JIT优化。

Maxim发挥最大作用


Maxim在多线程程序中计算最大值,但希望不加锁。 帮助他纠正错误。

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

解决方案
哦,马克西姆! AtomicLong使用AtomicLong不能使程序线程安全。 AtomicLong.compareAndSwap有一个原子操作AtomicLong.compareAndSwap 。 从Java 8开始,完全不需要自己编写CAS循环,因为出现了很棒的原子方法accumulateAndGet 。 在这里使用它很方便:

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

Source: https://habr.com/ru/post/zh-CN427337/


All Articles