【问题标题】:ByteArray to DoubleArray in KotlinKotlin 中的 ByteArray 到 DoubleArray
【发布时间】:2020-10-01 16:46:17
【问题描述】:

我想将一个字节数组外推成一个双精度数组。

我知道如何在 Java 中做到这一点。但是 AS 转换器不适用于此... :-D

这是我想用 Kotlin 写的类:

class ByteArrayToDoubleArrayConverter {

    public double[] invoke(byte[] bytes) {
        double[] doubles = new double[bytes.length / 2];
        int i = 0;

        for (int n = 0; n < bytes.length; n = n + 2) {
            doubles[i] = (bytes[n] & 0xFF) | (bytes[n + 1] << 8);
            i = i + 1;
        }

        return doubles;
    }
}

这将是预期结果的典型示例:

class ByteArrayToDoubleArrayConverterTest {

    @Test
    fun `check typical values`() {
        val bufferSize = 8
        val bytes = ByteArray(bufferSize)
        bytes[0] = 1
        bytes[1] = 0

        bytes[2] = 0
        bytes[3] = 1

        bytes[4] = 0
        bytes[5] = 2

        bytes[6] = 1
        bytes[7] = 1
        val doubles = ByteArrayToDoubleArrayConverter().invoke(bytes)
        assertTrue(1.0 == doubles[0])
        assertTrue(256.0 == doubles[1])
        assertTrue(512.0 == doubles[2])
        assertTrue(257.0 == doubles[3])
    }
}

有什么想法吗?谢谢!!!

【问题讨论】:

  • 您只进行了 16 位转换。双打不应该更多吗?
  • 很好的洞察力,@NomadMaker。它适用于 16 位音频。这样就可以了。
  • 问题是您正在制作一个 16 位整数,然后将其隐式转换为双精度数。好像效率不高。
  • 其实我对这种操作并不熟悉。我只想在 Kotlin 中做到这一点。
  • 样式注释,而不是val bytes = ByteArray(bufferSize) ...,你可以一次性声明和初始化:val bytes = byteArrayOf(1,0,0,1,0,2,1,1)

标签: java arrays kotlin


【解决方案1】:

我认为使用辅助函数会更清楚。这是一个使用 lambda 将字节对转换为 DoubleArray 的扩展函数:

inline fun ByteArray.mapPairsToDoubles(block: (Byte, Byte) -> Double)
    = DoubleArray(size / 2){ i -> block(this[2 * i], this[2 * i + 1]) }

这使用了 DoubleArray 构造函数,该构造函数接受初始化 lambda 和大小,因此您无需在构造后循环设置值。

然后,所需的函数只需要知道如何将每对字节转换为双精度。虽然它作为扩展函数而不是类会更惯用:

fun ByteArray.toDoubleSamples() = mapPairsToDoubles{ a, b ->
    (a.toInt() and 0xFF or (b.toInt() shl 8)).toDouble()
}

然后您可以使用例如:

bytes.toDoubleSamples()

(.toXxx() 是返回对象的转换版本的函数的常规名称。此类函数的标准名称是 toDoubleArray(),但通常会转换 每个 值到它自己的双重;你正在做的事情更专业,所以一个更专业的名字会避免混淆。)

那里唯一尴尬的事情(以及从 Java 直接转换失败的原因)是 Kotlin 对其数字类型更加挑剔,并且不会像 Java 和 C 那样自动提升它们;它的位运算符也没有字节重载。所以你需要在每个字节上显式调用toInt(),然后才能调用andshl,然后在结果上调用toDouble()

结果是代码更短,希望更易读,也非常高效! (没有中间数组或列表,而且——感谢inline——甚至没有任何不必要的函数调用。)

(它比大多数 Kotlin 代码更尴尬,因为原始数组不像基于引用的数组那样得到很好的支持——它们本身不像列表那样得到很好的支持。这主要是出于遗留原因Java 兼容性。但遗憾的是,ByteArray 没有 chunked() 实现,这本可以避免使用辅助函数,但代价是临时列表。)

【讨论】:

  • 这是一个很好的答案,但在我看来,lambda 并没有添加任何有用的东西并且模糊了逻辑。无相同逻辑 lambda 的替代方案:fun UByteArray.packToDoubles() : DoubleArray { fun toDouble(lowByte : UByte, highByte: UByte) : Double = (lowByte.toUInt() or (highByte.toUInt() shl 8)).toDouble() return DoubleArray(size / 2){ i -&gt; toDouble(this[2 * i], this[2 * i + 1]) } }
  • @DavidSoroko 使用 lambda 的优点是 mapPairsToDoubles() 可以被重用于进行其他类型的转换。 (即使没有,编写通用函数的原则通常可以帮助它们更清晰、更集中。) 我发现我的版本更容易阅读,因为它更清楚地分离了逻辑。但总有个人品味的元素! (很遗憾,您无法在 cmets 中正确格式化代码。 请随时为您提供单独的答案。)
  • 我的只是一个评论,不需要单独的答案。至于可重用性,可以通过移出嵌套函数来重用(byte,byte)-&gt; double 逻辑。在这是特定于案例的实现细节的情况下,我认为隐藏该逻辑是一种优势。是的,对格式化感到羞耻。
  • 精彩的答案,@gidds! :-)
猜你喜欢
  • 1970-01-01
  • 2017-10-19
  • 2018-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-17
相关资源
最近更新 更多