Skip to content

Instantly share code, notes, and snippets.

@SadiinsoSnowfall
Last active July 17, 2019 15:08
Show Gist options
  • Save SadiinsoSnowfall/e4dd4366bf8f403ca298a4869d0b7029 to your computer and use it in GitHub Desktop.
Save SadiinsoSnowfall/e4dd4366bf8f403ca298a4869d0b7029 to your computer and use it in GitHub Desktop.
Functions to get the String representation of any Java Object
/**
* After some benchmarking with JMH, I found that the best way to implement the part that manage maps and collections was
* to use an Iterator and its built-in function that take a lambda (forEachRemaning).
* List of others solutions I tried:
* - Iterator (using for...in loop)
* - Iterator + while loop
* - Spliterator + Stream
* - Spliterator + lambda
*/
public class DeepToString {
/**
*
* @param o The object to scan
* @return A string representation of {@code o}
* @see #deepToString(Object, boolean)
*/
public static String deepToString(Object o) {
return deepToString(o, true);
}
/**
*
* @param o The object to scan
* @param preventLoop Whether or not to prevent reference loops when scanning the object
* @return A string representation of {@code o}
* @see #deepToString(Object)
*/
public static String deepToString(Object o, boolean preventLoop) {
if (o == null) {
return "null";
}
StringBuilder buf = new StringBuilder(64);
deepToString(o, buf, (preventLoop ? new HashSet<>() : null));
return buf.toString();
}
/**
* Internal function, scan an object and get its string representation
* @param o An object to scan
* @param buf A String buffer to prevent high alloc count
* @param dejaVu A Set of object already scanned to prevent reference loop
*/
private static void deepToString(Object o, StringBuilder buf, Set<Object> dejaVu) {
if (o == null) {
buf.append("null");
return;
}
if(dejaVu != null) {
if (dejaVu.contains(o)) {
buf.append("[...]");
return;
}
dejaVu.add(o);
}
Class<?> clazz = o.getClass();
if (clazz.isArray()) {
if (clazz == byte[].class) {
buf.append(Arrays.toString((byte[]) o));
} else if (clazz == short[].class) {
buf.append(Arrays.toString((short[]) o));
} else if (clazz == int[].class) {
buf.append(Arrays.toString((int[]) o));
} else if (clazz == long[].class) {
buf.append(Arrays.toString((long[]) o));
} else if (clazz == char[].class) {
buf.append(Arrays.toString((char[]) o));
} else if (clazz == float[].class) {
buf.append(Arrays.toString((float[]) o));
} else if (clazz == double[].class) {
buf.append(Arrays.toString((double[]) o));
} else if (clazz == boolean[].class) {
buf.append(Arrays.toString((boolean[]) o));
} else { // element is an array of object references
Object[] objs = (Object[]) o;
if(objs.length == 0) {
buf.append("[]");
} else {
int last = objs.length - 1;
buf.append('[');
for (int t = 0; t < last; t++) {
deepToString(objs[t], buf, dejaVu);
buf.append(", ");
}
deepToString(objs[last], buf, dejaVu);
buf.append(']');
}
}
} else if (o instanceof Map) {
Iterator<Map.Entry<?, ?>> iter = ((Set<Map.Entry<?, ?>>) ((Map) o).entrySet()).iterator();
if(iter.hasNext()) {
buf.append('{');
Map.Entry<?, ? > first = iter.next();
deepToString(first.getKey(), buf, dejaVu);
buf.append("->");
deepToString(first.getValue(), buf, dejaVu);
iter.forEachRemaining(entry -> {
buf.append(", ");
deepToString(entry.getKey(), buf, dejaVu);
buf.append("->");
deepToString(entry.getValue(), buf, dejaVu);
});
buf.append('}');
} else {
buf.append("{}");
}
} else if (o instanceof Collection) {
Iterator<Object> iter = ((Collection) o).iterator();
if(iter.hasNext()) {
buf.append('(');
deepToString(iter.next(), buf, dejaVu);
iter.forEachRemaining(elem -> {
buf.append(", ");
deepToString(elem, buf, dejaVu);
});
buf.append(')');
} else {
buf.append("()");
}
} else if (o instanceof String) {
buf.append('\"').append((String) o).append('\"');
} else if (clazz == Object.class) {
buf.append("obj");
} else {
buf.append(o.toString());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment