使用原始类型时如何避免重复?

时间:2010-03-19 19:57:35

标签: java

我需要对各种原始类型执行算法;算法基本相同,但变量的类型除外。例如,

/**
* 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)));
}

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

4 个答案:

答案 0 :(得分:6)

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

实际上,源代码包含以下注释:

  

七种原始类型中的每一种的代码大致相同。 C'est la vie。

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

你也可以使用反射来减少重复,但这可能会很慢,也许不值得努力。如果你想测试它的性能,这个片段演示了这个技术:

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("}");
}

您可以为int[]以及其他原始数组类型传递arr。所有内容都被转换为long,并通过位屏蔽来解决符号扩展问题。

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

答案 1 :(得分:5)

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

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

答案 2 :(得分:2)

我觉得你很害怕 - 我不敢离开原始类型。

答案 3 :(得分:1)

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

我一直认为这是一种'janky'方法,但它有效。我很好奇有人曾经尝试过Velocity,或者可能是FMPP,并且有一些稍微清洁的结果吗?我对ant解决方案的主要问题是所有代码都非常接近,而在很多源代码生成中,您可以更好地分离模板文件。