【问题标题】:How to avoid repetition when working with primitive types?使用原始类型时如何避免重复?
【发布时间】:2010-03-19 19:57:34
【问题描述】:

我需要对各种原始类型执行算法;该算法本质上是相同的,只是变量的类型不同。比如,

/**
* Determine if <code>value</code> is the bitwise OR of elements of <code>validValues</code> array. 
* For instance, our valid choices are 0001, 0010, and 1000.
* We are given a value of 1001.  This is valid because it can be made from
* ORing together 0001 and 1000.
* On the other hand, if we are given a value of 1111, this is invalid because
* you cannot turn on the second bit from left by ORing together those 3
* valid values.
*/
public static boolean isValid(long value, long[] validValues) {
    for (long validOption : validValues) {
        value &= ~validOption;
    }
    return value == 0;
}

public static boolean isValid(int value, int[] validValues) {
    for (int validOption : validValues) {
        value &= ~validOption;
    }
    return value == 0;
}

如何避免这种重复?我知道没有办法泛化原始数组,所以我的双手似乎被束缚了。我有原始数组的实例,而不是说 Number 对象的盒装数组,所以我也不想走那条路。

我知道关于数组、自动装箱等方面的原语有很多问题,但我还没有看到它以这种方式表达,我还没有看到关于如何交互的决定性答案这些数组。

我想我可以这样做:

public static<E extends Number> boolean isValid(E value, List<E> numbers) {
    long theValue = value.longValue();
    for (Number validOption : numbers) {
        theValue &= ~validOption.longValue();
    }
    return theValue == 0;
}

然后

public static boolean isValid(long value, long[] validValues) {
    return isValid(value, Arrays.asList(ArrayUtils.toObject(validValues)));
}

public static boolean isValid(int value, int[] validValues) {
    return isValid(value, Arrays.asList(ArrayUtils.toObject(validValues)));
}

这样真的好多了吗?这种方式将创建比原始实现更多的对象,尽管它会干掉源代码。对此问题的任何想法将不胜感激。

【问题讨论】:

  • 好问题。尽管我看不到使用泛型的方法,但在完全不相关的注释中,考虑到迭代是在 1.5 语法中管理的方式,我猜你正在为每个数字进行自动装箱。我猜只做旧式数组计数会更快。
  • @I82Much:在这里查看我的回答和 Tom Hawtin 的评论:stackoverflow.com/questions/2337170/… 基本上没有“简单”的方法。您要么必须处理大量的重复,要么使用自定义预处理器或源代码检测或某些模板系统。甚至 Sun 也在使用这种技术这一事实证明了它是有用的。基本上,这也是令人惊叹的 Trove 原语集合的做法。请参阅 polygenelubricants 的问题和我 +5 赞成/接受的答案。

标签: java


【解决方案1】:

我之前问过一个类似的问题 (Managing highly repetitive code and documentation in Java),并注意到 java.util.Arrays 的源代码在处理原始数组类型的算法中高度重复

其实源码中包含这样的注释:

七种基本类型的代码基本相同。生活吧。

我接受的答案建议使用代码生成器来代替您使用代码模板。还有评论说 Sun/Oracle 在内部也使用了模板系统。

您也可以使用反射来减少重复,但这可能会很慢,而且可能不值得付出努力。如果你想测试它的性能,这个 sn-p 演示了该技术:

import java.lang.reflect.Array;

static long maskFor(Class<?> c) {
    return (
        c.equals(int.class) ? 1L << Integer.SIZE :
        c.equals(short.class) ? 1L << Short.SIZE :
        c.equals(byte.class) ? 1L << Byte.SIZE :
        0
    ) - 1;
}   
public static void reflectPrimitiveNumericArray(Object arr) throws Exception {
    int length = Array.getLength(arr);
    Class<?> componentType = arr.getClass().getComponentType();
    long mask = maskFor(componentType);
    System.out.format("%s[%d] = { ", componentType, length);
    for (int i = 0; i < length; i++) {
        long el = Array.getLong(arr, i) & mask;
        System.out.print(Long.toBinaryString(el) + " ");
    }
    System.out.println("}");
}

您可以为arr 以及其他原始数组类型传递int[]。所有内容都被转换为long,并使用位掩码来处理符号扩展。

reflectPrimitiveNumericArray(new byte[] { (byte) 0xF0 });
// byte[1] = { 11110000 }
reflectPrimitiveNumericArray(new int[] { 0xF0F0F0F0 });
// int[1] = { 11110000111100001111000011110000 }
reflectPrimitiveNumericArray(new long[] { 0xF0F0F0F0F0F0F0F0L });
// long[1] = { 1111000011110000111100001111000011110000111100001111000011110000 }

【讨论】:

  • -1 - 我的看法是,除非重复的代码达到数千行,否则将代码生成引入混合可能会使代码更难维护......尤其是对于那些不熟悉代码生成技术。
  • 我只是分享我从我的问题中学到的东西。如何处理这些知识取决于 OP。
  • @Stephen C:+1 ... 真的没有必要对他投反对票(顺便说一句,我写了接受的答案)。您可能不喜欢源代码生成,您可能不喜欢其他人正在这样做......但是人们编写使用原语的高性能集合的方式。去看看Trove。他们的 TLongLongHashMap/TIntIntHashMap 等完全破坏了默认的 Java 集合内存和性能。他们是怎么做到的?源代码生成/模板/预处理器等。在各种情况下,这些技术是有用的并且它们提供了真正的好处。
  • @WizardOfOdds - 实际上,我确实喜欢源代码生成。 (我不会详细介绍我的背景,但相信我,我知道这些东西。)但是,源生成最好用于模型到源的转换,而不是作为一个美化的宏前-处理器。这是后者的一个例子。
  • @Stephen C:还要考虑这只是他拥有的一种算法的一个例子。他可能有很多,加起来可能有数千行。
【解决方案2】:

如果您查看java.util.Arrays,您会发现即使他们也必须针对每种原始类型专门化所有算法(binarySearchequals 等)。

如果性能是一个问题,我不建议依赖自动装箱,但如果不是(在您分析之后),这将是一个有效的选项。

【讨论】:

    【解决方案3】:

    恐怕你不走运 - 没有远离原始类型。

    【讨论】:

      【解决方案4】:

      在前世,我们有一些针对财务数据优化的原始类型集合(内存中的数百万个订单,使用分块数组等)。我们的解决方案很像 Trove 的,带有一些存根文件。 '原始'源文件会说......例如HashSet_key。对于 keyvalue 的一些存根类,我们遵循 Trove 的模型,使用 ant 任务生成 HashSetInt、HashSetLong 等...

      我一直认为这是一种“笨拙”的方法,但它确实有效。我很好奇是否有人曾经尝试过 Velocity,或者可能是 FMPP,并获得了一些稍微干净的结果?我使用 ant 解决方案的主要问题是所有代码都非常接近,而在许多源代码生成中,您可能能够更好地分离模板文件。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-11-24
        • 2023-03-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-18
        相关资源
        最近更新 更多