
阿罗哈!
因此,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); }