convert是什么意思,convert是什么意思中文翻译

  

     

  

  本文最初发表于 Piotr Mikowski 的个人站点 Medium 网站,经作者授权,由 InfoQ 中文站翻译分享。   

  

  在本文中,您将学到一些以前可能没有听说过的有用的Java特性。这是我最近看Java的时候发现并整理出来的私有特性列表。我就不重点说语言了,重点说API。   

  

  你喜欢Java,想知道它的最新特性吗?如果是这样的话,你可以看看我这篇关于Java 8之后的新特性的文章。接下来,在本文中,您将了解八个未知但有用的特性。那我们开始吧!   

  

  延迟队列   

  

  众所周知,Java中集合的类型有很多种。那么你听说过DelayQueue吗?它是一种特殊类型的Java集合,允许我们根据元素的延迟时间对元素进行排序。坦率地说,这是一门非常有趣的课。虽然DelayQueue类是Java集合的成员,但它位于java.util.concurrent包中。它实现了BlockingQueue接口。只有当元素的时间到期时,才能将其从队列中取出。   

  

  要使用这个集合,首先我们的类需要实现Delayed接口的getDelay方法。当然不一定是类,也可以是Java记录。   

  

  public record Delayed event(long start time,String msg)实现Delayed { public long get delay(time unit unit){ long diff=start time-system . current time millis();return unit.convert(diff,TimeUnit。毫秒);} public int compare to(Delayed o){ return(int)(this . start time-((DelayedEvent)o)。start time);}}复制代码   

  

  假设我们希望将元素延迟10秒,那么我们只需要在DelayedEvent类上将时间设置为当前时间加10秒。   

  

  final delayqueuedelaydevent delay queue=new delay queue();final long time first=system . current time millis()10000;delay queue . offer(new DelayedEvent(time first,' 1 '));log . info(' Done ');log.info(delayQueue.take()。msg());复制代码   

  

  从上面的代码中我们可以看到什么输出?如下图。   

  

     

  

  时间格式支持显示一天中的时间段。   

  

  好吧,我承认这个Java特性对你们大多数人来说不是很有用,但是我对这个特性情有独钟.……Java 8对时间处理API做了很多改进。从这个版本的Java开始,大多数情况下,我们不需要任何额外的库来处理时间,比如Joda Time。你可能想象不到,从Java 16开始,我们甚至可以用标准的格式化程序来表示一天中的时间,也就是“上午”或者“下午”。这是一种新的格式模式,叫做b。   

  

  字符串s=DateTimeFormatter。ofPattern('B ')。format(local datetime . now());system . out . println(s);复制代码   

  

  以下是我操作的结果。当然,你的结果可能会因时而异。   

  

     

  

  好的,等一下.现在你可能会问这种格式为什么叫b,其实对于这种类型的格式来说并不是最直观的称呼。但也许下表可以解决我们所有的疑惑。是d   

ateTimeFormatter 能够处理的模式字符和符号的片段。我猜想,B 是第一个空闲出来的字母。当然,我可能是错的。

  


  

  

StampedLock

  

我认为,Java Concurrent 是最有趣的 Java 包之一。同时,它也是一个不太为开发者所熟知的包,当开发人员主要使用 web 框架的时候更是如此。我们有多少人曾经在 Java 中使用过锁呢?锁是一种比 synchronized 块更灵活的线程同步机制。从 Java 8 开始,我们可以使用一种叫做 StampedLock 的新锁。StampedLock 是 ReadWriteLock 的一个替代方案。它允许对读操作进行乐观的锁定。而且,它的性能比 ReentrantReadWriteLock 更好。

  


  

假设我们有两个线程。第一个线程更新一个余额,而第二个线程则读取余额的当前值。为了更新余额,我们当然需要先读取其当前值。在这里,我们需要某种同步机制,假设第一个线程在同一时间内多次运行。第二个线程阐述了如何使用乐观锁来进行读取操作。

  


  

StampedLock lock = new StampedLock();Balance b = new Balance(10000);Runnable w = () -> { long stamp = lock.writeLock(); b.setAmount(b.getAmount() + 1000); System.out.println("Write: " + b.getAmount()); lock.unlockWrite(stamp);};Runnable r = () -> { long stamp = lock.tryOptimisticRead(); if (!lock.validate(stamp)) { stamp = lock.readLock(); try { System.out.println("Read: " + b.getAmount()); } finally { lock.unlockRead(stamp); } } else { System.out.println("Optimistic read fails"); }};复制代码

  


  

现在,我们同时运行这两个线程 50 次。它的结果应该是符合预期的,最终的余额是 60000。

  


  

ExecutorService executor = Executors.newFixedThreadPool(10);for (int i = 0; i < 50; i++) { executor.submit(w); executor.submit(r);}复制代码

  

并发累加器

  

在 Java Concurrent 包中,有意思的并不仅仅有锁,另外一个很有意思的东西是并发累加器(concurrent accumulator)。我们也有并发的加法器(concurrent adder),但它们的功能非常类似。LongAccumulator(我们也有 DoubleAccumulator)会使用一个提供给它的函数更新一个值。在很多场景下,它能让我们实现无锁的算法。当多个线程更新一个共同的值的时候,它通常会比 AtomicLong 更合适。

  


  

我们看一下它是如何运行的。要创建它,我们需要在构造函数中设置两个参数。第一个参数是一个用于计算累加结果的函数。通常情况下,我们会使用 sum 方法。第二个参数表示累积器的初始值。

  


  

现在,让我们创建一个初始值为 10000 的 LongAccumulator,然后从多个线程调用 accumulate() 方法。最后的结果是什么呢?如果你回想一下的话,我们做的事情和上一节完全一样,但这一次没有任何锁。

  


  

LongAccumulator balance = new LongAccumulator(Long::sum, 10000L);Runnable w = () -> balance.accumulate(1000L);ExecutorService executor = Executors.newFixedThreadPool(50);for (int i = 0; i < 50; i++) { executor.submit(w);}executor.shutdown();if (executor.awaitTermination(1000L, TimeUnit.MILLISECONDS)) System.out.println("Balance: " + balance.get());assert balance.get() == 60000L;复制代码

  

十六进制格式

  

关于这个特性并没有什么大的故事。有时我们需要在十六进制的字符串、字节或字符之间进行转换。从 Java 17 开始,我们可以使用 HexFormat 类实现这一点。只要创建一个 HexFormat 的实例,然后就可以将输入的 byte 数组等格式化为十六进制字符串。你还可以将输入的十六进制字符串解析为字节数组,如下所示。

  


  

HexFormat format = HexFormat.of();byte<> input = new byte<> {127, 0, -50, 105};String hex = format.formatHex(input);System.out.println(hex);byte<> output = format.parseHex(hex);assert Arrays.compare(input, output) == 0;复制代码

  

数组的二分查找

  

假设我们想在排序的数组中插入一个新的元素。如果数组中已经包含该元素的话,Arrays.binarySearch() 会返回该搜索键的索引,否则,它返回一个插入点,我们可以用它来计算新键的索引:-(insertion point)-1。此外,在 Java 中,binarySearch 方法是在一个有序数组中查找元素的最简单和最有效的方法。

  


  

让我们考虑下面的例子。我们有一个输入的数组,其中有四个元素,按升序排列。我们想在这个数组中插入数字 3,下面的代码展示了如何计算插入点的索引。

  


  

int<> t = new int<> {1, 2, 4, 5};int x = Arrays.binarySearch(t, 3);assert ~x == 2;复制代码

  

Bit Set

  

如果我们需要对 bit 数组进行一些操作该怎么办呢?你是不是会使用 boolean<> 来实现呢?其实,有一种更有效、更节省内存的方法来实现。这就是 BitSet 类。BitSet 类允许我们存储和操作 bit 的数组。与 boolean<> 相比,它消耗的内存要少 8 倍。我们可以对数组进行逻辑操作,例如:and、or、xor。

  


  

比方说,有两个 bit 的数组, 我们想对它们执行 xor 操作。为了做到这一点,我们需要创建两个 BitSet 的实例,并在实例中插入样例元素,如下所示。最后,对其中一个 BitSet 实例调用 xor 方法,并将第二个 BitSet 实例作为参数。

  


  

BitSet bs1 = new BitSet();bs1.set(0);bs1.set(2);bs1.set(4);System.out.println("bs1 : " + bs1);BitSet bs2 = new BitSet();bs2.set(1);bs2.set(2);bs2.set(3);System.out.println("bs2 : " + bs2);bs2.xor(bs1);System.out.println("xor: " + bs2);复制代码

  


  

如下是运行上述代码的结果:

  


  

  

Phaser

  

最后,我们介绍本文最后一个有趣的 Java 特性。和其他一些样例一样,它也是 Java Concurrent 包的元素,被称为 Phaser。它与更知名的 CountDownLatch 相当相似。然而,它提供了一些额外的功能。它允许我们设置在继续执行之前需要等待的线程的动态数量。在 Phaser 中,已定义数量的线程需要在进入下一步执行之前在屏障上等待。得益于此,我们可以协调多个阶段的执行。

  


  

在下面的例子中,我们设置了一个具有 50 个线程的屏障,在进入下一个执行阶段之前,需要到达该屏障。然后,我们创建一个线程,在 Phaser 实例上调用 arriveAndAwaitAdvance() 方法。它会一直阻塞线程,直到所有的 50 个线程都到达屏障。然后,它进入 phase-1,同样会再次调用 arriveAndAwaitAdvance() 方法。

  


  

Phaser phaser = new Phaser(50);Runnable r = () -> { System.out.println("phase-0"); phaser.arriveAndAwaitAdvance(); System.out.println("phase-1"); phaser.arriveAndAwaitAdvance(); System.out.println("phase-2"); phaser.arriveAndDeregister();};ExecutorService executor = Executors.newFixedThreadPool(50);for (int i = 0; i < 50; i++) { executor.submit(r);}复制代码

  


  

如下是执行上述代码的结果:

  


  

相关文章