Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid repetition when working with primitive types?

Tags:

java

I have the need to perform algorithms on various primitive types; the algorithm is essentially the same with the exception of which type the variables are. So for instance,

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

How can I avoid this repetition? I know there's no way to genericize primitive arrays, so my hands seem tied. I have instances of primitive arrays and not boxed arrays of say Number objects, so I do not want to go that route either.

I know there are a lot of questions about primitives with respect to arrays, autoboxing, etc., but I haven't seen it formulated in quite this way, and I haven't seen a decisive answer on how to interact with these arrays.

I suppose I could do something like:

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;
}

and then

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

Is that really much better though? That way will create a lot more objects than the original implementation, though it DRYs up the source code. Any thoughts in this matter would be appreciated.

like image 712
I82Much Avatar asked Mar 19 '10 19:03

I82Much


People also ask

Which data type can be used to avoid repetitions?

C++ static code analysis: "auto" should be used to avoid repetition of types.

How does Java handle primitive data types?

However, Java provides support for character strings using the String class of Java. lang package. String class has some special support from the Java Programming language, so, technically it is a primitive data type. While using String class, a character string will automatically create a new String Object.

Why do primitive types exist?

The main reason primitive data type are there because, creating object, allocating heap is too costly and there is a performance penalty for it. As you may know primitive data types like int, float etc are most used, so making them as Objects would have been huge performance hit.


2 Answers

I asked a similar question before (Managing highly repetitive code and documentation in Java), and noted that the source code for java.util.Arrays is highly repetitive in its algorithms to deal with primitive array types.

In fact, the source code contains this comment:

The code for each of the seven primitive types is largely identical. C'est la vie.

The answer that I accepted suggests the use of a code generator that lets you work with code templates instead. There's also a comment that Sun/Oracle uses a templating system internally as well.

You can also use reflection to reduce repetition, but this is likely to be slow, and perhaps not worth the effort. If you want to test out its performance, this snippet demonstrates the technique:

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

You can pass an int[] for arr, as well as other primitive array types. Everything is cast into long, with bit-masking to address sign extension.

reflectPrimitiveNumericArray(new byte[] { (byte) 0xF0 });
// byte[1] = { 11110000 }
reflectPrimitiveNumericArray(new int[] { 0xF0F0F0F0 });
// int[1] = { 11110000111100001111000011110000 }
reflectPrimitiveNumericArray(new long[] { 0xF0F0F0F0F0F0F0F0L });
// long[1] = { 1111000011110000111100001111000011110000111100001111000011110000 }
like image 132
polygenelubricants Avatar answered Sep 22 '22 14:09

polygenelubricants


If you look in java.util.Arrays, you'll see that even they had to specialize all their algorithms (binarySearch, equals, etc.) for each primitive type.

I would not recommend relying on autoboxing if performance is an issue, but if not (after you've profiled), it would be a valid option.

like image 43
Michael Myers Avatar answered Sep 22 '22 14:09

Michael Myers