Java Forum / General / October 2007
Primitive type arrays boxing utilities
Piotr Kobzda - 07 Oct 2007 01:31 GMT Below is the code generator of utility class that may possibly appear useful extension for java.util.Arrays methods.
Basic usages of the generated class includes:
1) Lists backed with primitive arrays manipulation.
int[] array = { 1, 2, 3 }; Collections.shuffle(PrimitiveArrays.asList(array));
2) Simple primitive type arrays boxing/unboxing.
int[] array = { 1, 2, 3 }; Integer[] boxedArray = PrimitiveArrays.box(array); array = PrimitiveArrays.unbox(boxedArray);
3) Type-safe, non-dimensions limited, extended boxing/unboxing of primitive arrays.
int[][][] a = {{{ 1, 2, 3 }}}; Integer[][][] b = PrimitiveArrays.intBoxer().deeper().deeper().box(a); // operate on b... PrimitiveArrays.intBoxer().deeper().deeper().unbox(b, a); // operate on updated a...
What do you think about that? Is that worth of becoming a new Java RFE?
Regards, piotr
import static java.lang.System.out;
public class Generator { static String[][] pairs = { {"boolean", "Boolean"}, {"byte", "Byte"}, {"char", "Character"}, {"double", "Double"}, {"float", "Float"}, {"int", "Integer"}, {"long", "Long"}, {"short", "Short"}, };
public static void main(String[] args) { // out.println("package java.util;");
out.println("import java.lang.reflect.Array;"); out.println("import java.util.AbstractList;"); out.println("import java.util.List;"); out.println("import java.util.RandomAccess;"); out.println(); out.println("public class PrimitiveArrays {"); out.println(); out.println("private static abstract class ArrayBackedList<E>"); out.println(" extends AbstractList<E> implements RandomAccess {"); out.println("}"); out.println();
generateAsListMethods(); generateBoxersBase(); generateBoxers(); generateBoxingMethods();
out.println("}"); }
static void generateAsListMethods() { new Object() { void gen(String from, String to) { out.println("public static List<"+ to +"> asList(final "+ from +"[] array) {"); out.println(" return new ArrayBackedList<"+ to +">() {"); out.println(); out.println(" @Override"); out.println(" public int size() {"); out.println(" return array.length;"); out.println(" }"); out.println(); out.println(" @Override"); out.println(" public "+ to +" get(int index) {"); out.println(" return array[index];"); out.println(" }"); out.println(); out.println(" @Override"); out.println(" public "+ to +" set(int index, "+ to +" element) {"); out.println(" "+ to +" r = array[index];"); out.println(" array[index] = element;"); out.println(" return r;"); out.println(" }"); out.println(" };"); out.println("}"); out.println(); }
{ for(String[] pair : pairs) { gen(pair[0], pair[1]); } } }; }
static void generateBoxersBase() { out.println("public static abstract class Boxer<S, T> {"); out.println(" private Class<S> typeOfS;"); out.println(" private Class<T> typeOfT;"); out.println(); out.println(" Boxer(Class<S> typeOfS, Class<T> typeOfT) {"); out.println(" this.typeOfS = typeOfS;"); out.println(" this.typeOfT = typeOfT;"); out.println(" }"); out.println(); out.println(" private Boxer<S[], T[]> child;"); out.println(); out.println(" public Boxer<S[], T[]> deeper() {"); out.println(" if (child != null) return child;"); out.println(); out.println(" final Boxer<S, T> parent = Boxer.this;"); out.println(" return child = new Boxer<S[], T[]>("); out.println(" arrayType(typeOfS), arrayType(typeOfT)) {"); out.println(); out.println(" @Override"); out.println(" public T[] box(S[] source) {"); out.println(" T[] target = newArray(typeOfT, source.length);"); out.println(" for(int i = 0, len = source.length; i < len; ++i)"); out.println(" target[i] = parent.box(source[i]);"); out.println(" return target;"); out.println(" }"); out.println(); out.println(" @Override"); out.println(" public T[] box(S[] source, T[] target) {"); out.println(" for(int i = 0, len = source.length; i < len; ++i)"); out.println(" parent.box(source[i], target[i]);"); out.println(" return target;"); out.println(" }"); out.println(); out.println(" @Override"); out.println(" public S[] unbox(T[] source) {"); out.println(" S[] target = newArray(typeOfS, source.length);"); out.println(" for(int i = 0, len = source.length; i < len; ++i)"); out.println(" target[i] = parent.unbox(source[i]);"); out.println(" return target;"); out.println(" }"); out.println(); out.println(" @Override"); out.println(" public S[] unbox(T[] source, S[] target) {"); out.println(" for(int i = 0, len = source.length; i < len; ++i)"); out.println(" parent.unbox(source[i], target[i]);"); out.println(" return target;"); out.println(" }"); out.println(); out.println(" @Override"); out.println(" public int dimensions() {"); out.println(" return parent.dimensions() + 1;"); out.println(" }"); out.println(); out.println(" };"); out.println(" }"); out.println(); out.println(" public abstract T box(S source);"); out.println(); out.println(" public abstract T box(S source, T target);"); out.println(); out.println(" public abstract S unbox(T source);"); out.println(); out.println(" public abstract S unbox(T source, S target);"); out.println(); out.println(); out.println(" public int dimensions() {"); out.println(" return 1;"); out.println(" }"); out.println(); out.println(" @SuppressWarnings(\"unchecked\")"); out.println(" static <T> Class<T[]> arrayType(Class<T> componentType) {"); out.println(" return (Class<T[]>) Array.newInstance(componentType, 0).getClass();"); out.println(" }"); out.println(); out.println(" @SuppressWarnings(\"unchecked\")"); out.println(" static <T> T[] newArray(Class<T> componentType, int length) {"); out.println(" return (T[]) Array.newInstance(componentType, length);"); out.println(" }"); out.println(); out.println("}"); out.println(); }
static void generateBoxers() { new Object() { void genBoxer(String from, String to) { out.println("private static class "+ to +"Boxer extends Boxer<"+ from +"[], "+ to +"[]> {"); out.println(" static final "+ to +"Boxer INSTANCE = new "+ to +"Boxer();"); out.println(); out.println(" "+ to +"Boxer() {"); out.println(" super("+ from +"[].class, "+ to +"[].class);"); out.println(" }"); out.println(); out.println(" @Override"); out.println(" public "+ to +"[] box("+ from +"[] source) {"); out.println(" return box(source, new "+ to +"[source.length]);"); out.println(" }"); out.println(); out.println(" @Override"); out.println(" public "+ to +"[] box("+ from +"[] source, "+ to +"[] target) {"); out.println(" for(int i = 0, len = source.length; i < len; ++i)"); out.println(" target[i] = source[i];"); out.println(" return target;"); out.println(" }"); out.println(); out.println(" @Override"); out.println(" public "+ from +"[] unbox("+ to +"[] source) {"); out.println(" return unbox(source, new "+ from +"[source.length]);"); out.println(" }"); out.println(); out.println(" @Override"); out.println(" public "+ from +"[] unbox("+ to +"[] source, "+ from +"[] target) {"); out.println(" for(int i = 0, len = source.length; i < len; ++i)"); out.println(" target[i] = source[i];"); out.println(" return target;"); out.println(" }"); out.println("}"); out.println(); }
void genAccessMethod(String from, String to) { out.println("public static Boxer<"+ from +"[], "+ to +"[]> "+ from +"Boxer() {"); out.println(" return "+ to +"Boxer.INSTANCE;"); out.println("}"); out.println(); }
{ for(String[] pair : pairs) { genBoxer(pair[0], pair[1]); } for(String[] pair : pairs) { genAccessMethod(pair[0], pair[1]); } } }; }
static void generateBoxingMethods() { new Object(){ void gen(String name, String from, String to, String boxer) { out.println("public static "+ to +"[] "+ name +"("+ from +"[] source) {"); out.println(" return "+ boxer +"Boxer.INSTANCE."+ name + "(source);"); out.println("}"); out.println(); }
{ for(String[] pair : pairs) { gen("box", pair[0], pair[1], pair[1]); } for(String[] pair : pairs) { gen("unbox", pair[1], pair[0], pair[1]); } } }; } }
Daniel Pitts - 07 Oct 2007 19:17 GMT > Below is the code generator of utility class that may possibly appear > useful extension for java.util.Arrays methods. [quoted text clipped - 25 lines] > Regards, > piotr [snip code]
Sure, and while your at it, suggest that List<Integer>.toArray(new int[0]) works as expected. (Actually, I don't know that it doesn't work, but I'm guessing it doesn't.)
 Signature Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
Piotr Kobzda - 07 Oct 2007 21:13 GMT > Sure, and while your at it, suggest that List<Integer>.toArray(new > int[0]) works as expected. (Actually, I don't know that it doesn't work, > but I'm guessing it doesn't.) That's impossible, because toArray() in List has the following two overrides only:
public Object[] toArray() public <T> T[] toArray(T[] a)
And none of them allows to return/accept the int[] type. Thus, the only possibility is to preserve the original behavior of those methods here, i.e. return array of objects.
There is also possibility to introduce additional toArray() overrides to the list returned from asList() (internal ArrayBackedList may do that). However, that would require this extended type to be declared as return type of asList() overrides, which will likely compromise the usability of them).
However, using proposed boxing utilities it's not hard to achieve what you expect from toArray() that way:
List<Integer> list = ... int[] array = unbox(list.toArray(new Integer[list.size()]));
// or having already created array of ints, that way:
intBoxer().unbox(list.toArray(new Integer[array.length]), array);
Not big difference IMHO, just one auxiliary array more...
piotr
Stefan Ram - 07 Oct 2007 21:18 GMT >> Sure, and while your at it, suggest that List<Integer>.toArray(new >However, using proposed boxing utilities it's not hard to achieve what See also:
»Commons Primitives provides a library of collections and utilities specially designed for use with primitive types.«
http://commons.apache.org/primitives/
Piotr Kobzda - 07 Oct 2007 23:37 GMT >>> Sure, and while your at it, suggest that List<Integer>.toArray(new >> However, using proposed boxing utilities it's not hard to achieve what [quoted text clipped - 5 lines] > > http://commons.apache.org/primitives/ Well, I know that library, but never had need to use it -- most of my typical needs are covered in my proposition.
There are also important differences between the library, and my proposal. Key differences includes: 1) the library classes are not "generified"; 2) Lists offered by library do not supports backing by a _given_ array; 3) that's much bigger library than a simple few methods we have here.
My proposal is just an extension for bunch of utility methods already defined in java.util.Arrays. In addition to asList(), which just box/unbox each access to the list elements (nothing revealing of course), it adds a few simple boxing utilities (a bit advanced stuff in implementation, but not mystic also), that's all. BTW -- even being such simple, there is functionality of boxing multidimensional arrays, which is not offered by Commons Primitives, nor any other library I know.
The proposal was originally designed as possible addition to already defined methods in java.util.Arrays in mind. As a side effect, having it all in Arrays might likely safe from accidental uses of asList(T...) with primitive array passed to it.
Of course, those interested in more advanced functionality, that the Commons Primitives library offer (primitive iterators, elements removal/addition, etc.), may still use it when needed.
To sum it up a bit, I think it would be nice to have it all at hand in standard Java library. Of course, if anybody else (but Daniel, and me) likes it as well. :-)
piotr
Stefan Ram - 08 Oct 2007 00:02 GMT >My proposal is just an extension for bunch of utility methods already Your proposal uses Java like a preprocessor (to generate Java code).
In fact, I am using a preprocessor to generate my Java code - sometimes also to abstract primitive types.
For example, this is code to finde the middle value of two number from the library »ram.jar«:
public INTEGRAL_TYPE INTEGRAL_TYPE()'#floor middle'() { /* The calculation tries to avoid overflows, which might cause unwanted changes of sign and consequent errors in the calculation. Based on code by Peter Luschny. */ final INTEGRAL_TYPE $result; result = INTEGRAL_TYPE_CAST ( ( this.number0 >> 1 )+( this.number1 >> 1 )+ ( this.number0 & this.number1 & 1 )); return $result; }
When the definitions are set for »int«, my preprocessor will generate the following from the above:
public int intFloorMiddle() { /* The calculation tries to avoid overflows, which might cause unwanted changes of sign and consequent errors in the calculation. Based on code by Peter Luschny. */ final int result; result = ( ( this.number0 >> 1 )+( this.number1 >> 1 )+ ( this.number0 & this.number1 & 1 )); return result; }
For the web page
http://www.purl.org/stefan_ram/java/de/dclj/ram/type/pair/PairOfIntAndInt.java
, this is automatically reformatted to use the common bracing and spacing style:
public int intFloorMiddle() { /* The calculation tries to avoid overflows, which might cause unwanted changes of sign and consequent errors in the calculation. Based on code by Peter Luschny. */ final int result;
result = ((this.number0 >> 1) + (this.number1 >> 1) + (this.number0 & this.number1 & 1)); return result; }
Piotr Kobzda - 08 Oct 2007 10:11 GMT > Your proposal uses Java like a preprocessor > (to generate Java code). Yes. And it was quite easy to write it to me.
The development of this code generator was supported by the following simple Java tool:
public class CodePrintOutputter { public static void main(String[] args) { java.util.Scanner s = new java.util.Scanner(System.in); while(true) { String line = s.nextLine(); if (line.trim().length() > 0) { line = line .replace("\\", "\\\\") .replace("\"", "\\\""); System.out.println("out.println(\""+ line +"\");"); } else { System.out.println("out.println();"); } } } }
That way, my original code used to create the generator is fully functional class, which just supports one primitive type (guess which one? ;) ). I'm able to experiment with that code, test it, choose from alternatives, etc. directly from the IDE. The final phase is almost copying & pasting of the original code into the generator's code. That approach is usually easier to follow, then development based on some proprietary preprocessor's language to me.
Ideally would be to have some standard preprocessor which works on syntactically correct Java. Preprocessor able to regenerate somehow a given source. Something similar to C preprocessor, but utilizing annotations, comments, and possibly other Java features. Anybody knows such kind of Java preprocessor?
> In fact, I am using a preprocessor to generate > my Java code - sometimes also to abstract primitive > types. There are a lot of templating engines able to support it also. But I'm not sure that using one of them for demonstrating both, a concept, and fully functional implementation would be better than simply give a Java generator which everybody is able to run without any extra effort.
But thanks Stefan for mentioning your preprocessor, next time I'll need to generate the code I'll likely take a closer look at it.
By the way...
> For example, this is code to finde the middle value > of two number from the library »ram.jar«: [...]
> result = ((this.number0 >> 1) + (this.number1 >> 1) + (this.number0 & this.number1 & 1)); > return result; > } What about using possibly a bit faster approach here:
result = ((this.number0 ^ this.number1) >> 1) + (this.number0 & this.number1);
?
piotr
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|