Primitive type arrays boxing utilities

P

Piotr Kobzda

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 = parent.box(source);");
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, target);");
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 = parent.unbox(source);");
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, target);");
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 = source;");
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 = source;");
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]);
}
}
};
}
}
 
D

Daniel Pitts

Piotr said:
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
[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.)
 
P

Piotr Kobzda

Daniel said:
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
 
P

Piotr Kobzda

Stefan said:
See also:

»Commons Primitives provides a library of collections and
utilities specially designed for use with primitive types.«

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
 
S

Stefan Ram

Piotr Kobzda said:
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;
}
 
P

Piotr Kobzda

Stefan said:
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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,965
Messages
2,570,148
Members
46,710
Latest member
FredricRen

Latest Threads

Top