Better alternative to reflection than large switch statement using java 8 - java

My old code create a FrameBody subclass based on identifier
Class<AbstractID3v2FrameBody> c = (Class<AbstractID3v2FrameBody> Class.forName("org.jaudiotagger.tag.id3.framebody.FrameBody" + identifier);
Class<?>[] constructorParameterTypes = {Class.forName("java.nio.ByteBuffer"), Integer.TYPE};
Object[] constructorParameterValues = {byteBuffer, frameSize};
Constructor<AbstractID3v2FrameBody> construct = c.getConstructor(constructorParameterTypes);
frameBody = (construct.newInstance(constructorParameterValues));
But profile analysis shows its a bit slow, the alternative to using reflecting is a big switch statement
switch(identifier)
{
case ID3v24Frames.FRAME_ID_AUDIO_ENCRYPTION:
frameBody = new FrameBodyAENC(byteBuffer, frameSize);
break;
case ID3v24Frames.FRAME_ID_ATTACHED_PICTURE:
frameBody = new FrameBodyAPIC(byteBuffer, frameSize);
break;
.......
}
but I have over 100 identifiers, so its seems a bit cumbersome
Is there a more elegant way to do this in Java 8 ?

EDIT:
This can be simplified indeed as Holger suggests:
Map<String, BiFunction<ByteBuffer, Integer, AbstractID3v2FrameBody>> LOOKUP = Map.of(
FRAME_ID_AUDIO_ENCRYPTION, FrameBodyAENC::new,
FRAME_ID_ATTACHED_PICTURE, FrameBodyAPIC::new
);
And then as simple as:
LOOKUP.get(ID3v24Frames.FRAME_ID_AUDIO_ENCRYPTION)
.apply(byteBuffer, frameSize);
PREVIOUS SUGGESTION
This is just a sample, you will need to adapt it to your classes:
private static final Lookup L = MethodHandles.lookup();
private static final MethodHandle CONS;
static {
try {
CONS = L.findConstructor(SomeClass.class, MethodType.methodType(void.class, int.class));
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
private static final Map<String, MethodHandle> LOOK_UP = Map.of(
"SOME_CLASS", CONS
);
public static void main(String[] args) {
try {
SomeClass sc = (SomeClass) LOOK_UP.get("SOME_CLASS").invokeExact(42);
System.out.println(sc.getX());
} catch (Throwable t) {
t.printStackTrace();
}
}
static class SomeClass {
private final int x;
public SomeClass(int x) {
this.x = x;
}
public int getX() {
return x;
}
}

Related

Type-safe retrieval from collection of generic class

I have a generic Container which holds objects of type E:
public class Container<E> {
private final E e;
public Container(E e) {
this.e = e;
}
public E get() {
return e;
}
}
I have a bucket which holds various containers:
public class Bucket {
private final Map<String, Container<?>> containers = new HashMap<>();
public void put(String name, Container<?> container) {
containers.put(name, container);
}
public Container<?> get(String name) {
Container<?> container = containers.get(name);
return container;
}
}
I want to be able to put containers (of various types) into the bucket and retrieve them back in a type-safe way.
Container<Long> longs = new Container<>(100L);
Container<String> strings = new Container<>("Hello");
Bucket bucket = new Bucket();
bucket.put("longs", longs);
bucket.put("strings", strings);
But as you can I lost type-safety:
Container<?> longs1 = bucket.get("longs");
Container<?> strings1 = bucket.get("strings");
I can't seem to figure out what it'd take that would allow me achieve the following:
Container<Long> longs1 = bucket.get("longs");
Container<String> strings1 = bucket.get("strings");
My solution. I guess it serves my need pretty good:
public class Container<E> {
private final E e;
public Container(E e) {
this.e = e;
}
public <T> T get(Class<T> target) {
if (target.isAssignableFrom(e.getClass())) {
return (T) e;
}
throw new ClassCastException(e.getClass().getName() + " '" + e + "' cannot be converted to " + target.getName());
}
}
public class Bucket {
private final Map<String, Container<?>> containers = new HashMap<>();
public void put(String name, Container<?> container) {
containers.put(name, container);
}
public Container<?> getContainer(String name) {
return containers.get(name);
}
}
Putting it to test:
Container<Long> longs = new Container<>(100L);
Container<String> strings = new Container<>("Hello");
Bucket bucket = new Bucket();
bucket.put("longs", longs);
bucket.put("strings", strings);
Container<?> longContainer = bucket.getContainer("longs");
Long aLong = longContainer.get(Long.class);
log.debug("{}", aLong); // Prints 100
Container<?> stringContainer = bucket.getContainer("strings");
String aString = stringContainer.get(String.class);
log.debug("{}", aString); // Prints Hello
log.debug("{}", stringContainer.get(Long.class)); // Throws java.lang.ClassCastException: java.lang.String 'Hello' cannot be converted to java.lang.Long

Refactoring by passing generic method

I have simplified my code to this:
static private String waitForString(String expected, int attempts) {
String actual = null;
for (int i = 0; i < attempts; i++){
actual = getString();
if (validateString(actual, expected)) {
return actual;
}
}
return null;
}
static private int waitForInt(int expected, int attempts) {
int actual = 0;
for (int i = 0; i < attempts; i++){
actual = getInt();
if (validateInt(actual, expected)) {
return actual;
}
}
return 0;
}
Since I'm using the same loop (and since I have more than one class with more than one corresponding "getter" method and validation method) I would like to refactor it. I tried this:
static <T> T helperMethod(Method getMethod, Method validator,T expected, int attempts) {
T actual = null;
for (int i = 0; i < attempts; i++){
actual = method.invoke(null);
if (validator.invoke(null, actual, expected)) {
return actual;
}
}
return null;
}
However I'm getting following errors:
actual = method.invoke(null);
error: incompatible types: Object cannot be converted to T
validator.invoke(null, actual, expected)
error: incompatible types: Object cannot be converted to boolean
Can I specify in the function declaration only to accept methods with the correct return type? If so, how?
Ideas for other ways to refactor will be appreciated.
EDITED
To make it clear, I wasnt asking how reflect the return type of the method.
Thanks you VGR for the solution.
Do not use reflection.
Reflection is slower, hard for a developer (including yourself) to follow, and cannot be checked by the compiler for correct arguments and return type.
The correct way to accomplish the equivalent of a “pointer to a method” in Java is to wrap the various method calls in a common interface. As of Java 8, as Markus Benko pointed out, you should use suppliers and predicates:
static <T> T waitForValue(Supplier<T> getMethod, BiPredicate<T, T> validator, T expected, int attempts) {
T actual = null;
for (int i = 0; i < attempts; i++){
actual = getMethod.get();
if (validator.test(actual, expected)) {
return actual;
}
}
return null;
}
private static String waitForString(String expected, int attempts) {
return waitForValue(ThisClass::getString, ThisClass::validateString, expected, attempts);
}
private static int waitForInt(int expected, int attempts) {
return waitForValue(ThisClass::getInt, ThisClass::validateInt, expected, attempts);
}
If you’re using an older version of Java, you can do the same thing with a little more work:
private interface Getter<T> {
T get();
}
private interface Validator<T> {
boolean test(T actual, T expected);
}
static <T> T waitForValue(Getter<T> getMethod, Validator<T> validator, T expected, int attempts) {
T actual = null;
for (int i = 0; i < attempts; i++){
actual = getMethod.get();
if (validator.test(actual, expected)) {
return actual;
}
}
return null;
}
private static String waitForString(String expected, int attempts) {
Getter<String> getter = new Getter<String>() {
#Override
public String get() {
return getString();
}
};
Validator<String> validator = new Validator<String>() {
#Override
public boolean test(String actual, String expected) {
return validateString(actual, expected);
}
};
return waitForValue(getter, validator, expected, attempts);
}
private static int waitForInt(int expected, int attempts) {
Getter<Integer> getter = new Getter<Integer>() {
#Override
public Integer get() {
return getInt();
}
};
Validator<Integer> validator = new Validator<Integer>() {
#Override
public boolean test(Integer actual, Integer expected) {
return validateInt(actual, expected);
}
};
return waitForValue(getter, validator, expected, attempts);
}
Avoid using reflection for different reasons: loose of JVM optimizations, your code compiles but explodes at runtime, the code is hard to be debugged.
You can try by creating an interface with the implementations for each type you need to validate.
Something like:
Interface:
public interface InputHandler<T> {
Boolean wait(T expected);
}
Implementations:
An handler implementation for the input String:
public class StringHandler implements InputHandler<String> {
#Override
public Boolean wait(String expected) {
String actual = getString();
return validateString(actual, expected);
}
private String getString() {
// ...
return null;
}
private boolean validateString(String actual, String expected) {
// ...
return false;
}
}
An handler implementation for the input Integer:
public class IntegerHandler implements InputHandler<Integer> {
#Override
public Boolean wait(Integer expected) {
Integer actual = getInt();
return validateInt(actual, expected);
}
private boolean validateInt(Integer actual, Integer expected) {
// ...
return false;
}
private Integer getInt() {
// ...
return null;
}
}
You can add and remove all the "handlers" you need really fast.
App to run the example:
public class Test {
public static void main(String[] args) {
waitForValidInput(new StringHandler(), "a", 3);
waitForValidInput(new IntegerHandler(), 5, 3);
}
static private <T> T waitForValidInput(InputHandler<T> validator, T expected, int attempts) {
for (int i = 0; i < attempts; i++) {
if(validator.wait(expected)) {
return expected;
}
}
return null;
}
}
Try this:
static <T> T helperMethod(Method method, Method validator, T expected, int attempts) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
T actual = null;
for (int i = 0; i < attempts; i++) {
actual = (T)method.invoke(null);
if ((Boolean)validator.invoke(null, actual, expected)) {
return actual;
}
}
return null;
}
(also added exceptions in the signature and changed getMehod to method in the arguments)

JAVA: Parsing String to static final int value

I have simple problem, but I'm not able to fix it. I have this interface...
public interface KeyInput extends Input {
public static final int TEST1 = 0x01;
public static final int TEST2 = 0x02;
}
...this string variable...
String inputString = "TEST1";
...and this method.
public void doSomething(int _input) {
}
I want to parse inputString variable to KeyInput static final int value. So that I could call....
doSomething(KeyInput.parse(inputString));
I know the enum valueOf, but this doesn't work here...
If you have only these two (or any other fixed number of) values, you might just enumerate them in switch:
public static int parse(String input) {
int res = -1;
switch (input) {
"TEST1":
res = TEST1;
break;
"TEST2":
res = TEST2;
break;
// ... other options
default: throw new IllegalArgumentException("unknown string");
}
}
The other option is to keep this values inside some map, so you can do this:
private static final Map <String, Integer> TESTS = new HashMap<>();
static {
TESTS.put("TEST1", 0x01);
TESTS.put("TEST2", 0x02);
// ...
}
public static int parse(String input) {
if (TESTS.containsKey(input))
return TESTS.get(input);
else
throw new IllegalArgumentException("unknown string");
}
Still, if you see the enums as an option in your case, I can consider this solution:
public enum Keys {
TEST1(0x01), TEST2(0x02);
int value;
private Keys(int value) {
this.value = value;
}
public getValue() {
return value;
}
}
Here you'll just do valueOf as you suggesed:
public static int parse(String input) {
return Keys.valueOf(input).getValue();
}
If all these options is now for your case, you should use reflection (though, I'm quite sure, it's not the case):
public static int parse(String input) {
Field[] fields = KeyInput.class.getDeclaredFields();
for (Field field : fields) {
if (Modifier.isStatic(fields.getModifiers()) && field.getDeclaringClass().equals(int.class) && field.getName().equals(input)) {
return field.getInt(null);
}
}
throw new IllegalArgumentException("unknown string");
}

Thread safe multitons in Java

Given the following multiton:
public class Multiton
{
private static final Multiton[] instances = new Multiton[...];
private Multiton(...)
{
//...
}
public static Multiton getInstance(int which)
{
if(instances[which] == null)
{
instances[which] = new Multiton(...);
}
return instances[which];
}
}
How can we keep it thread safe and lazy without the expensive synchronization of the getInstance() method and the controversy of double-checked locking? An effective way for singletons is mentioned here but that doesn't seem to extend to multitons.
UPDATE: with Java 8, it can be even simpler:
public class Multiton {
private static final ConcurrentMap<String, Multiton> multitons = new ConcurrentHashMap<>();
private final String key;
private Multiton(String key) { this.key = key; }
public static Multiton getInstance(final String key) {
return multitons.computeIfAbsent(key, Multiton::new);
}
}
Mmm that's good!
ORIGINAL ANSWER
This is a solution which builds on the Memoizer pattern as described in JCiP. It uses a ConcurrentHashMap like one of the other answers, but instead of storing the Multiton instances directly, which can lead to creating unused instances, it stores the computation that leads to the creation of the Multiton. That additional layer solves the problem of unused instances.
public class Multiton {
private static final ConcurrentMap<Integer, Future<Multiton>> multitons = new ConcurrentHashMap<>();
private static final Callable<Multiton> creator = new Callable<Multiton>() {
public Multiton call() { return new Multiton(); }
};
private Multiton(Strnig key) {}
public static Multiton getInstance(final Integer key) throws InterruptedException, ExecutionException {
Future<Multiton> f = multitons.get(key);
if (f == null) {
FutureTask<Multiton> ft = new FutureTask<>(creator);
f = multitons.putIfAbsent(key, ft);
if (f == null) {
f = ft;
ft.run();
}
}
return f.get();
}
}
This will provide you a threadsafe storage mechanism for your Multitons. The only downside is that it is possible to create a Multiton that will not be used in the putIfAbsent() call. The possibility is small but it does exist. Of course on the remote chance it does happen, it still causes no harm.
On the plus side, there is no preallocation or initialization required and no predefined size restrictions.
private static ConcurrentHashMap<Integer, Multiton> instances = new ConcurrentHashMap<Integer, Multiton>();
public static Multiton getInstance(int which)
{
Multiton result = instances.get(which);
if (result == null)
{
Multiton m = new Multiton(...);
result = instances.putIfAbsent(which, m);
if (result == null)
result = m;
}
return result;
}
You could use an array of locks, to at least be able to get different instances concurrently:
private static final Multiton[] instances = new Multiton[...];
private static final Object[] locks = new Object[instances.length];
static {
for (int i = 0; i < locks.length; i++) {
locks[i] = new Object();
}
}
private Multiton(...) {
//...
}
public static Multiton getInstance(int which) {
synchronized(locks[which]) {
if(instances[which] == null) {
instances[which] = new Multiton(...);
}
return instances[which];
}
}
With the advent of Java 8 and some improvements in ConcurrentMap and lambdas it is now possible to implement a Multiton (and probably even a Singleton) in a much tidier fashion:
public class Multiton {
// Map from the index to the item.
private static final ConcurrentMap<Integer, Multiton> multitons = new ConcurrentHashMap<>();
private Multiton() {
// Possibly heavy construction.
}
// Get the instance associated with the specified key.
public static Multiton getInstance(final Integer key) throws InterruptedException, ExecutionException {
// Already made?
Multiton m = multitons.get(key);
if (m == null) {
// Put it in - only create if still necessary.
m = multitons.computeIfAbsent(key, k -> new Multiton());
}
return m;
}
}
I suspect - although it would make me feel uncomfortable - that getInstance could be further minimised to:
// Get the instance associated with the specified key.
public static Multiton getInstance(final Integer key) throws InterruptedException, ExecutionException {
// Put it in - only create if still necessary.
return multitons.computeIfAbsent(key, k -> new Multiton());
}
You're looking for an AtomicReferenceArray.
public class Multiton {
private static final AtomicReferenceArray<Multiton> instances = new AtomicReferenceArray<Multiton>(1000);
private Multiton() {
}
public static Multiton getInstance(int which) {
// One there already?
Multiton it = instances.get(which);
if (it == null) {
// Lazy make.
Multiton newIt = new Multiton();
// Successful put?
if ( instances.compareAndSet(which, null, newIt) ) {
// Yes!
it = newIt;
} else {
// One appeared as if by magic (another thread got there first).
it = instances.get(which);
}
}
return it;
}
}

How can I get an int[] out of an Iterator?

I have what amounts to an Iterator<Integer>... actually it's a class Thing that accepts a Visitor<SomeObject> and calls visit() for a subset of the SomeObjects it contains, and I have to implement Visitor<SomeObject> so it does something like this:
// somehow get all the Id's from each of the SomeObject that Thing lets me visit
public int[] myIdExtractor(Thing thing)
{
SomeCollection c = new SomeCollection();
thing.visitObjects(new Visitor<SomeObject>()
{
public void visit(SomeObject obj) { c.add(obj.getId()); }
}
);
return convertToPrimitiveArray(c);
}
I need to extract an int[] containing the results, and I'm not sure what to use for SomeCollection and convertToPrimitiveArray. The number of results is unknown ahead of time and will be large (10K-500K). Is there anything that would be a better choice than using ArrayList<Integer> for SomeCollection, and this:
public int[] convertToPrimitiveArray(List<Integer> ints)
{
int N = ints.size();
int[] array = new int[N];
int j = 0;
for (Integer i : ints)
{
array[j++] = i;
}
return array;
}
Efficiency and memory usage are of some concern.
It's not too difficult to come up with a class that collects ints in an array (even if you are not using some library which does it for you).
public class IntBuffer {
private int[] values = new int[10];
private int size = 0;
public void add(int value) {
if (!(size < values.length)) {
values = java.util.Arrays.copyOf(values, values.length*2);
}
values[size++] = value;
}
public int[] toArray() {
return java.util.Arrays.copyOf(values, size);
}
}
(Disclaimer: This is stackoverflow, I have not even attempted to compile this code.)
As an alternative you could use DataOutputStream to store the ints in a ByteArrayOutputStream.
final ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
final DataOutputStream out = new DataOutputStream(byteOut);
...
out.writeInt(value);
...
out.flush();
final byte[] bytes = byteOut.toByteArray();
final int[] ints = new int[bytes.length/4];
final ByteArrayInputStream byteIn = new ByteArrayInputStream(bytes);
final DataInputStream in = new DataOutputStream(byteIn);
for (int ct=0; ct<ints.length; ++ct) {
ints[ct] = in.readInt();
}
(Disclaimer: This is stackoverflow, I have not even attempted to compile this code.)
You could look at something like pjc to handle this. That is a collections framework made for primitives.
for benchmarking's sake I put together a test program using an LFSR generator to prevent the compiler from optimizing out test arrays. Couldn't download pjc but I assume timing should be similar to Tom's IntBuffer class, which is by far the winner. The ByteArrayOutputStream approach is about the same speed as my original ArrayList<Integer> approach. I'm running J2SE 6u13 on a 3GHz Pentium 4, and with approx 220 values, after JIT has run its course, the IntBuffer approach takes roughly 40msec (only 40nsec per item!) above and beyond a reference implementation using a "forgetful" collection that just stores the last argument to visit() (so the compiler doesn't optimize it out). The other two approaches take on the order of 300msec, about 8x as slow.
edit: I suspect the problem with the Stream approach is that there is the potential for exceptions which I had to catch, not sure.
(for arguments run PrimitiveArrayTest 1 2)
package com.example.test.collections;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class PrimitiveArrayTest {
interface SomeObject {
public int getX();
}
interface Visitor {
public void visit(SomeObject obj);
}
public static class PlainObject implements SomeObject
{
private int x;
public int getX() { return this.x; }
public void setX(int x) { this.x = x; }
}
public static class Thing
{
/* here's a LFSR
* see http://en.wikipedia.org/wiki/Linear_feedback_shift_register
* and http://www.ece.cmu.edu/~koopman/lfsr/index.html
*/
private int state;
final static private int MASK = 0x80004;
private void _next()
{
this.state = (this.state >>> 1)
^ (-(this.state & 1) & MASK);
}
public Thing(int state) { this.state = state; }
public void setState(int state) { this.state = state; }
public void inviteVisitor(Visitor v, int terminationPoint)
{
PlainObject obj = new PlainObject();
while (this.state != terminationPoint)
{
obj.setX(this.state);
v.visit(obj);
_next();
}
}
}
static public abstract class Collector implements Visitor
{
abstract public void initCollection();
abstract public int[] getCollection();
public int[] extractX(Thing thing, int startState, int endState)
{
initCollection();
thing.setState(startState);
thing.inviteVisitor(this, endState);
return getCollection();
}
public void doit(Thing thing, int startState, int endState)
{
System.out.printf("%s.doit(thing,%d,%d):\n",
getClass().getName(),
startState,
endState);
long l1 = System.nanoTime();
int[] result = extractX(thing,startState,endState);
long l2 = System.nanoTime();
StringBuilder sb = new StringBuilder();
sb.append(String.format("%d values calculated in %.4f msec ",
result.length, (l2-l1)*1e-6));
int N = 3;
if (result.length <= 2*N)
{
sb.append("[");
for (int i = 0; i < result.length; ++i)
{
if (i > 0)
sb.append(", ");
sb.append(result[i]);
}
sb.append("]");
}
else
{
int sz = result.length;
sb.append(String.format("[%d, %d, %d... %d, %d, %d]",
result[0], result[1], result[2],
result[sz-3], result[sz-2], result[sz-1]));
}
System.out.println(sb.toString());
}
}
static public class Collector0 extends Collector
{
int lastint = 0;
#Override public int[] getCollection() { return new int[]{lastint}; }
#Override public void initCollection() {}
#Override public void visit(SomeObject obj) {lastint = obj.getX(); }
}
static public class Collector1 extends Collector
{
final private List<Integer> ints = new ArrayList<Integer>();
#Override public int[] getCollection() {
int N = this.ints.size();
int[] array = new int[N];
int j = 0;
for (Integer i : this.ints)
{
array[j++] = i;
}
return array;
}
#Override public void initCollection() { }
#Override public void visit(SomeObject obj) { ints.add(obj.getX()); }
}
static public class Collector2 extends Collector
{
/*
* adapted from http://stackoverflow.com/questions/1167060
* by Tom Hawtin
*/
private int[] values;
private int size = 0;
#Override public void visit(SomeObject obj) { add(obj.getX()); }
#Override public void initCollection() { values = new int[32]; }
private void add(int value) {
if (!(this.size < this.values.length)) {
this.values = java.util.Arrays.copyOf(
this.values, this.values.length*2);
}
this.values[this.size++] = value;
}
#Override public int[] getCollection() {
return java.util.Arrays.copyOf(this.values, this.size);
}
}
static public class Collector3 extends Collector
{
/*
* adapted from http://stackoverflow.com/questions/1167060
* by Tom Hawtin
*/
final ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
final DataOutputStream out = new DataOutputStream(this.byteOut);
int size = 0;
#Override public int[] getCollection() {
try
{
this.out.flush();
final int[] ints = new int[this.size];
final ByteArrayInputStream byteIn
= new ByteArrayInputStream(this.byteOut.toByteArray());
final DataInputStream in = new DataInputStream(byteIn);
for (int ct=0; ct<ints.length; ++ct) {
ints[ct] = in.readInt();
}
return ints;
}
catch (IOException e) { /* gulp */ }
return new int[0]; // failure!?!??!
}
#Override public void initCollection() { }
#Override public void visit(SomeObject obj) {
try {
this.out.writeInt(obj.getX());
++this.size;
}
catch (IOException e) { /* gulp */ }
}
}
public static void main(String args[])
{
int startState = Integer.parseInt(args[0]);
int endState = Integer.parseInt(args[1]);
Thing thing = new Thing(0);
// let JIT do its thing
for (int i = 0; i < 20; ++i)
{
Collector[] collectors = {new Collector0(), new Collector1(), new Collector2(), new Collector3()};
for (Collector c : collectors)
{
c.doit(thing, startState, endState);
}
System.out.println();
}
}
}
Instead of convertToPrimitiveArray, you can use List.toArray(T[] a):
ArrayList<int> al = new ArrayList<int>();
// populate al
int[] values = new int[al.size()];
al.toArray(values);
For your other concerns, LinkedList might be slightly better than ArrayList, given that you don't know the size of your result set in advance.
If performance is really a problem, you may be better off hand-managing an int[] yourself, and using System.arraycopy() each time it grows; the boxing/unboxing from int to Integer that you need for any Collection could hurt.
As with any performance-related question, of course, test and make sure it really matters before spending too much time optimizing.

Categories

Resources