package algocraft.function; import java.util.HashMap; import java.util.Map; import static algocraft.function.ArrayUtil.intArray; import static algocraft.function.SixFlags.Header.*; import static algocraft.function.SixFlags.Initializer.init; import static algocraft.function.SixFlags.Initializer.setup; import static algocraft.function.StringUtil.*; import static com.google.common.base.Joiner.on; import static java.lang.Math.pow; public enum SixFlags { YYYYYY, _YYYYY, Y_YYYY, __YYYY, YY_YYY, _Y_YYY, Y__YYY, ___YYY, YYY_YY, _YY_YY, Y_Y_YY, __Y_YY, YY__YY, _Y__YY, Y___YY, ____YY, YYYY_Y, _YYY_Y, Y_YY_Y, __YY_Y, YY_Y_Y, _Y_Y_Y, Y__Y_Y, ___Y_Y, YYY__Y, _YY__Y, Y_Y__Y, __Y__Y, YY___Y, _Y___Y, Y____Y, _____Y, YYYYY_, _YYYY_, Y_YYY_, __YYY_, YY_YY_, _Y_YY_, Y__YY_, ___YY_, YYY_Y_, _YY_Y_, Y_Y_Y_, __Y_Y_, YY__Y_, _Y__Y_, Y___Y_, ____Y_, YYYY__, _YYY__, Y_YY__, __YY__, YY_Y__, _Y_Y__, Y__Y__, ___Y__, YYY___, _YY___, Y_Y___, __Y___, YY____, _Y____, Y_____, ______; /** * Format this class and its variables into a table. * * @return */ public static CharSequence display() { return Describer.describe(); } /** * @param description * @return the value of the boolean on the ordinal of the given description enum. */ public boolean valueOf(Enum description) { return flags[description.ordinal()]; } /** * @param index * @return the value of the boolean on the given index. */ public boolean valueAt(int index) { return flags[index]; } /** * @param description * @param flag * @return itself or the flipped instance which has the same value as the given value * on the ordinal of the given description enum. */ public SixFlags resolve(Enum description, boolean flag) { int ordinal = description.ordinal(); return flag == flags[ordinal] ? this : flippedFlags[ordinal]; } /** * @param index * @param flag * @return itself or the flipped instance which has the same value as the given value * on the given index. */ public SixFlags resolve(int index, boolean flag) { return flag == flags[index] ? this : flippedFlags[index]; } /** * Build the following line according to its value, * |YYYYYY|0b111111=63|Y|Y|Y|Y|Y|Y|_YYYYY |Y_YYYY |YY_YYY |YYY_YY |YYYY_Y |YYYYY_ | * * @return */ public CharSequence describe() { return Describer.describe(this); } /** * Description this instance according to the give description enum instances. * * @param descriptions * @return */ public CharSequence toString(Enum... descriptions) { return concatenate("[", Describer.describe(this.flags, descriptions), "]"); } private final boolean[] flags = init(this.name()); private final SixFlags[] flippedFlags = new SixFlags[flags.length]; public final CharSequence description = concatenate("[", Describer.describe("flag", flags), "]").toString(); { setup(this); } static class Header { static final CharSequence LINE = "+------+-----------+-----------+-----------------------------------------------+\n"; static final CharSequence HEADER = concatenate(LINE, "| | flags | flippedFlags |\n", LINE); static final CharSequence HEADER_2 = concatenate("|flag |key |", header("|"), header(" |"), "\n"); static CharSequence header(String filler) { return stringify(intArray(YYYYYY.flippedFlags.length), i -> i + filler); } } static class Describer { static CharSequence describe() { return concatenate(HEADER, HEADER_2, LINE, displayAll(), LINE, HEADER_2, HEADER); } static CharSequence describe(SixFlags flag) { return concatenate("|", flag.name(), "|0b", encodeAll(flag), "=", key(flag), "|", value(flag), "|", flippedFlags(flag), " |\n"); } static CharSequence displayAll() { return stringify(SixFlags.values(), SixFlags::describe); } static CharSequence flippedFlags(SixFlags flag) { return on(" |").join(flag.flippedFlags); } static CharSequence value(SixFlags flag) { return on("|").join(flag.name().split("")); } static CharSequence key(SixFlags flag) { int key = Initializer.encodeAll(flag.flags); return concatenate((key < 10 ? " " : ""), key); } static CharSequence encodeAll(SixFlags flag) { StringBuilder sb = new StringBuilder(); for (int i = flag.flags.length - 1; i >= 0; i--) { sb.append(flag.flags[i] ? "1" : "0"); } return sb; } static CharSequence describe(boolean flags[], Enum... indexes) { return removeLastChar(stringify(indexes, index -> concatenate(flags[index.ordinal()] ? "" : "!", index, ","))); } static CharSequence describe(String desc, boolean[] flags) { return removeLastChar(stringify(intArray(flags.length), i -> concatenate(flags[i] ? "" : "!", desc + i, ","))); } } static class Initializer { static void setup(SixFlags flag) { lookup.put(encodeAll(flag.flags), flag); if (lookup.size() == pow(2, flag.flags.length)) { lookup.keySet().forEach(key -> flipFlags(key)); lookup.clear(); lookup = null; } } public static void flipFlags(int key) { SixFlags original = lookup.get(key); int mask = 1; for (int i = 0; i < original.flags.length; i++) { int flippedKey = original.flags[i] ? key ^ mask : key | mask; mask <<= 1; SixFlags flipped = lookup.get(flippedKey); original.flippedFlags[i] = flipped; flipped.flippedFlags[i] = original; } } static boolean[] init(String name) { char[] chars = name.toCharArray(); boolean[] flags = new boolean[chars.length]; for (int i = 0; i < chars.length; i++) { flags[i] = chars[i] == 'Y'; } return flags; } static int encodeAll(boolean[] flags) { int code = 0; int mask = 1; for (boolean flag : flags) { if (flag) { code |= mask; } mask <<= 1; } return code; } static Map<Integer, SixFlags> lookup = new HashMap<>(); } }
Sunday, May 4, 2014
sixflags over java 8
I rewrote this using Java 8, if you see -> in the code, that's lambda expression from Java 8,
java 8 lambda expression and its applications
This is an example of using Lambda Expression in Java 8,
Without using Lambda Expression, the highlighted code would need to be like this,
We can use this SortedCounter to count the course selected by the classmates of a given student to recommend courses to the student, there are many occurrences of lambda expression in the above example,
in the above class, the parameter for SortedCounter and one forEach loop are the examples for Lambda Expression.
We can use interface to switch the cache evict policy, and use SortedCounter class in Least Frequently Used cache policy,
Least Frequently Used eviction policy,
and a self populating cache,
Award::new is called method reference in Java 8, the alternative is a lambda expression or Anonymous Inner Class,the following 3 code snippets have the same functionality.
and this is the code without it,
Without using Lambda Expression, the highlighted code would need to be like this,
We can use this SortedCounter to count the course selected by the classmates of a given student to recommend courses to the student, there are many occurrences of lambda expression in the above example,
in the above class, the parameter for SortedCounter and one forEach loop are the examples for Lambda Expression.
//Declare a Predicate using lambda expression Predicate<String> notContains = course -> !courses.contains(course); //Passing a lambda expression as a constructor parameter SortedCounter<String> sortedCounter = new SortedCounter<>( (course1, course2) -> course1.compareTo(course2)); //Both forEach calls take a lambda expression as parameter classmateService.getClassmates(student) .forEach( classmate -> courseService.getCources(classmate) .stream() .filter(notContains).forEach( purchase -> sortedCounter.count(purchase) ) );
We can use interface to switch the cache evict policy, and use SortedCounter class in Least Frequently Used cache policy,
Least Frequently Used eviction policy,
and a self populating cache,
Award::new is called method reference in Java 8, the alternative is a lambda expression or Anonymous Inner Class,the following 3 code snippets have the same functionality.
//Use method reference private final static SelfPopulatingCache<Integer, Award> AWARDS = create(Award::new);
//Using lambda expression private final static SelfPopulatingCache<Integer, Award> AWARDS = create(level -> new Award(level));
//Before Java 8, using Anonymous Inner Class private final static SelfPopulatingCache<Integer, Award> AWARDS = create( new SelfPopulatingCache.Creator<Integer, Award>() { @Override public Award create(Integer key) { return new Award(level); } }); }This test make sure the valueOf return the same instance even under load, the highlighted code is also an example of lambda expression,
and this is the code without it,
structure of the code
Let's start with some code, I believe most people are familiar with the style, these are the code similar to what can be found in most people's codebase.
The method is so long and with so much details. It is very difficult to understand the author's intention and how business requirement is mapped to code.
To better map business requirement into code, we need to have some smaller classes to form an easy to follow structure, or an application architecture as used by many people,
Impressed? You can find more code example here,
https://github.com/yujunliang/compalgo
The method is so long and with so much details. It is very difficult to understand the author's intention and how business requirement is mapped to code.
To better map business requirement into code, we need to have some smaller classes to form an easy to follow structure, or an application architecture as used by many people,
Impressed? You can find more code example here,
https://github.com/yujunliang/compalgo
dynamic enum
This is to illustrate how to use DynamicEnum to create singleton instances of value objects. Please pay attention to ActorCreator, it is different from AwardCreator.
package algocraft.function.singleton; import com.google.common.collect.HashBasedTable; import com.google.common.collect.Table; import java.util.Collection; public class DynamicEnum { private static Table<Class, Object, Object> map = HashBasedTable.create(); public static synchronized <T, K> T getOrCreate(Class<T> tClass, K key, Creator<K, T> creator) { T value = (T) map.get(tClass, key); if (value == null) { value = creator.create(key); map.put(tClass, key, value); } return value; } public static <T> Collection<T> values(Class<T> tClass) { return (Collection<T>) map.row(tClass).values(); } public static int hashCode(Object... objects) { int result = objects[0].hashCode(); for (int i = 1; i < objects.length; i++) { result = 31 * result + objects[i].hashCode(); } return result; } }
package algocraft.function.singleton; import java.util.Collection; import java.util.List; import static algocraft.function.singleton.Actor.ActorCreator.ACTOR_CREATOR; import static com.google.common.collect.Lists.newArrayList; import static org.apache.commons.lang.builder.ToStringBuilder.reflectionToString; import static org.apache.commons.lang.builder.ToStringStyle.SHORT_PREFIX_STYLE; public class Actor { public final String firstName; public final String lastName; private Actor(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public static Actor valueOf(String firstName, String lastName) { return DynamicEnum.getOrCreate(Actor.class, new Actor(firstName, lastName), ACTOR_CREATOR); } @Override public String toString() { return reflectionToString(this, SHORT_PREFIX_STYLE); } @Override public int hashCode() { return DynamicEnum.hashCode(firstName, lastName); } @Override public boolean equals(Object that) { return (that instanceof Actor) && equals((Actor) that); } public boolean equals(Actor that) { return this.firstName.equals(that.firstName) && this.lastName.equals(that.lastName); } public static Collection<Actor> values() { return DynamicEnum.values(Actor.class); } enum ActorCreator implements Creator<Actor>, Actor> { ACTOR_CREATOR; @Override public Actor create(Actor key) { return key; } } }
package algocraft.function.singleton; import java.util.Collection; import static algocraft.function.singleton.Award.AwardCreator.AWARD_CREATOR; import static algocraft.function.singleton.DynamicEnum.getOrCreate; import static org.apache.commons.lang.builder.ToStringBuilder.reflectionToString; import static org.apache.commons.lang.builder.ToStringStyle.SHORT_PREFIX_STYLE; public class Award { public final int level; private Award(int level) { this.level = level; } public static Award valueOf(int level) { return getOrCreate(Award.class, level, AWARD_CREATOR); } @Override public String toString() { return reflectionToString(this, SHORT_PREFIX_STYLE); } @Override public int hashCode() { return level; } @Override public boolean equals(Object that) { return (that instanceof Award) && equals((Award) that); } public boolean equals(Award that) { return that.level == this.level; } public static Collection<Award> values() { return DynamicEnum.values(Award.class); } enum AwardCreator implements Creator<Integer, Award> { AWARD_CREATOR; @Override public Award create(Integer key) { return new Award(key); } } }
package algocraft.function.singleton; import org.apache.commons.lang.time.StopWatch; import org.junit.Test; import java.util.concurrent.*; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class AwardTest { public static final int INT = 200; @Test public void testValueOf() throws Exception { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ExecutorService executorService = Executors.newFixedThreadPool(50); Future<Award>[] future = new Future[INT]; final CountDownLatch latch = new CountDownLatch(1); for (int i = 0; i < INT; i++) { future[i] = executorService.submit(new Callable<Award>() { @Override public Award call() throws Exception { latch.await(); Award.valueOf(1); Award.valueOf(2); Award.valueOf(3); Award.valueOf(4); return Award.valueOf(1); } }); } latch.countDown(); Award award = Award.valueOf(1); System.out.println(award); for (int i = 0; i < INT; i++) { assertTrue(award == future[i].get()); assertFalse(award.equals(future[i])); assertTrue(award.equals(future[i].get())); } System.out.println(Award.values()); } }
package algocraft.function.singleton; import org.apache.commons.lang.time.StopWatch; import org.junit.Test; import java.util.concurrent.*; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class ActorTest { public static final int INT = 200; @Test public void testValueOf() throws Exception { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ExecutorService executorService = Executors.newFixedThreadPool(50); Future<Actor>[] future = new Future[INT]; final CountDownLatch latch = new CountDownLatch(1); for (int i = 0; i < INT; i++) { future[i] = executorService.submit(new Callable<Actor>() { @Override public Actor call() throws Exception { latch.await(); Actor.valueOf("Tom", "Cruise"); Actor.valueOf("Tom", "Hanks"); Actor.valueOf("Sean", "Penn"); return Actor.valueOf("Tom", "Cruise"); } }); } latch.countDown(); Actor actor = Actor.valueOf("Tom", "Cruise"); System.out.println(actor); for (int i = 0; i < INT; i++) { assertTrue(actor == future[i].get()); assertFalse(actor.equals(future[i])); assertTrue(actor.equals(future[i].get())); } System.out.println(Actor.values()); } @Test public void whyNeedIt() { List<Actor> actorsForMovie1 = newArrayList( Actor.valueOf("Tom", "Hanks"), Actor.valueOf("Tom", "Cruise"), Actor.valueOf("Sean", "Penn") ); List<Actor> actorsForMovie2 = newArrayList( Actor.valueOf("Tom", "Hanks"), Actor.valueOf("Tom", "Cruise"), Actor.valueOf("Sean", "Penn") ); List<Actor> actorsForMovie3 = newArrayList( Actor.valueOf("Tom", "Hanks"), Actor.valueOf("Tom", "Cruise"), Actor.valueOf("Sean", "Penn") ); for (int i = 0; i < actorsForMovie1.size(); i++) { assertTrue(actorsForMovie1.get(i) == actorsForMovie2.get(i)); assertTrue(actorsForMovie2.get(i) == actorsForMovie3.get(i)); } } }Why do we need this DynamicEnum? From the about test whyNeedIt, you can see, no matter how many collections the instance is added to, only one copy of the object is created.
store multiple boolean variables without additional memory usage
Java doesn't specify the storage usage of boolean variables, here, but most popular JVM store boolean as an integer. If you have a class with multiple boolean variables, the memory usage will be linear to the number of instances of the collection. For large collections, this can be significant.
We can also solve this problem by using a multiple flag enum, SixFlags, which doesn't require additional memory usage so definitely is a huge advantage in Big Data age.
How can we find the correspondent enum instance when one flag changed? we can use the resolve(int index, boolean flag) method. Even it is quick to encode the boolean flags into an integer and then look up the enum instance in a lookup map, this approach still requires some computing. The most efficient approach is to calculate all the correspondent enum instances by flipping one flag each time and store the flipped instances in an array variable. The SixFlags class implemented this approach,
The array doesn't require more storage space since the values are all enum constants. Please go over the test to find out how to use it. It can be used for store 1 to 6 boolean variables.
For example, enum instance YYYYYY, all its flags are true, and it will be encoded as 63 if true is 1 for each flag. When flipping the first flag, we can get the correspondent flippedFlag from the first element of the flippedFlag array which is _YYYYY, and second flipped flag is Y_YYYY, and so on.
This is what it prints,
Why do I call it SixFlags? :)
We can also solve this problem by using a multiple flag enum, SixFlags, which doesn't require additional memory usage so definitely is a huge advantage in Big Data age.
How can we find the correspondent enum instance when one flag changed? we can use the resolve(int index, boolean flag) method. Even it is quick to encode the boolean flags into an integer and then look up the enum instance in a lookup map, this approach still requires some computing. The most efficient approach is to calculate all the correspondent enum instances by flipping one flag each time and store the flipped instances in an array variable. The SixFlags class implemented this approach,
/** * @param index * @param flag * @return itself or the flipped instance which has the same value as the given value * on the given index. */ public SixFlags resolve(int index, boolean flag) { return flag == flags[index] ? this : flippedFlags[index]; } ... static class Helper { static void flipFlags() { lookup.keySet().forEach(key -> { int mask = 1; SixFlags original = lookup.get(key); for (int i = 0; i < original.flags.length; i++) { int flippedKey = original.flags[i] ? key ^ mask : key | mask; SixFlags flipped = lookup.get(flippedKey); original.flippedFlags[i] = flipped; flipped.flippedFlags[i] = original; mask <<= 1; } }); lookup.clear(); lookup = null; } private static void flip(SixFlags original, int i, SixFlags flipped) { original.flippedFlags[i] = flipped; flipped.flippedFlags[i] = original; } ... static Map<Integer, SixFlags> lookup = new HashMap<>(); }
The array doesn't require more storage space since the values are all enum constants. Please go over the test to find out how to use it. It can be used for store 1 to 6 boolean variables.
package algocraft.function; import org.junit.Test; import static algocraft.function.SixFlags.*; import static org.junit.Assert.*; public class SixFlagsTest { @Test public void testToString() throws Exception { assertEquals("[flag0,flag1,flag2,flag3,flag4,flag5]", YYYYYY.toString()); assertEquals("[!flag0,!flag1,!flag2,!flag3,!flag4,!flag5]", SixFlags.______.toString()); } @Test public void testSet() { assertEquals(YYYYYY, YY_YYY.resolve(2, true)); assertEquals(YYYYYY, YYYYYY.resolve(2, true)); assertEquals(YY_YYY, YY_YYY.resolve(2, false)); assertEquals(YY_YYY, YYYYYY.resolve(2, false)); } @Test public void testIs() { assertTrue(YYYYYY.valueAt(0)); assertFalse(YY_YYY.valueAt(2)); } @Test public void testApplicationUsing2Flags() { Person john = new Person(); john.setMarried(false); john.setHasChildren(true); assertTrue(john.hasChildren()); assertFalse(john.isMarried()); assertEquals("[!Married,HasChilren]", john.toString()); } @Test public void testApplicationUsing3Flags() { Product product = new Product(); product.setImported(true); product.setPackaged(false); product.setTaxfree(true); assertTrue(product.isImported()); assertFalse(product.isPackaged()); assertTrue(product.isTaxfree()); assertEquals("[Imported,!Packaged,Taxfree]", product.toString()); } @Test public void testDisplay() { out.println(SixFlags.display()); } }
package algocraft.function; import static algocraft.function.Product.FlagDef2.*; import static algocraft.function.SixFlags.______; class Product { static enum FlagDef2 {Imported, Packaged, Taxfree} private SixFlags flags = ______; public boolean isImported() { return flags.valueOf(Imported); } public boolean isPackaged() { return flags.valueOf(Packaged); } public boolean isTaxfree() { return flags.valueOf(Taxfree); } public void setImported(boolean flag) { flags = flags.resolve(Imported, flag); } public void setPackaged(boolean flag) { flags = flags.resolve(Packaged, flag); } public void setTaxfree(boolean flag) { flags = flags.resolve(Taxfree, flag); } @Override public String toString() { return flags.toString(FlagDef2.values()); } }
package algocraft.function; import static algocraft.function.Person.FlagDef.HasChilren; import static algocraft.function.Person.FlagDef.Married; import static algocraft.function.SixFlags.______; public class Person { static enum FlagDef {Married, HasChilren} private SixFlags flags = ______; public boolean isMarried() { return flags.valueOf(Married); } public boolean hasChildren() { return flags.valueOf(HasChilren); } public void setMarried(boolean flag) { flags = flags.resolve(Married, flag); } public void setHasChildren(boolean flag) { flags = flags.resolve(HasChilren, flag); } @Override public String toString() { return flags.toString(FlagDef.values()); } }
+------+-----------+-----------+-----------------------------------------------+ | | flags | flippedFlags | +------+-----------+-----------+-----------------------------------------------+ |flag |key |0|1|2|3|4|5|0 |1 |2 |3 |4 |5 | +------+-----------+-----------+-----------------------------------------------+ |YYYYYY|0b111111=63|Y|Y|Y|Y|Y|Y|_YYYYY |Y_YYYY |YY_YYY |YYY_YY |YYYY_Y |YYYYY_ | +------+-----------+-----------+-----------------------------------------------+
For example, enum instance YYYYYY, all its flags are true, and it will be encoded as 63 if true is 1 for each flag. When flipping the first flag, we can get the correspondent flippedFlag from the first element of the flippedFlag array which is _YYYYY, and second flipped flag is Y_YYYY, and so on.
package algocraft.function; import java.util.HashMap; import java.util.Map; import static algocraft.function.SixFlags.Helper.*; import static com.google.common.base.Joiner.on; import static java.lang.Math.pow; public enum SixFlags { YYYYYY, _YYYYY, Y_YYYY, __YYYY, YY_YYY, _Y_YYY, Y__YYY, ___YYY, YYY_YY, _YY_YY, Y_Y_YY, __Y_YY, YY__YY, _Y__YY, Y___YY, ____YY, YYYY_Y, _YYY_Y, Y_YY_Y, __YY_Y, YY_Y_Y, _Y_Y_Y, Y__Y_Y, ___Y_Y, YYY__Y, _YY__Y, Y_Y__Y, __Y__Y, YY___Y, _Y___Y, Y____Y, _____Y, YYYYY_, _YYYY_, Y_YYY_, __YYY_, YY_YY_, _Y_YY_, Y__YY_, ___YY_, YYY_Y_, _YY_Y_, Y_Y_Y_, __Y_Y_, YY__Y_, _Y__Y_, Y___Y_, ____Y_, YYYY__, _YYY__, Y_YY__, __YY__, YY_Y__, _Y_Y__, Y__Y__, ___Y__, YYY___, _YY___, Y_Y___, __Y___, YY____, _Y____, Y_____, ______; public static String display() { return concatenate(header(), header2(), line(), displayAll(), line(), header2(), header()); } private final boolean[] flags = init(this); private final SixFlags[] flippedFlags = new SixFlags[flags.length]; private final String description = concatenate("[", describe("flag", flags), "]"); private SixFlags() { lookup.put(encodeAll(flags), this); if (lookup.size() == pow(2, flags.length)) { flipFlags(); } } /** * @param description * @return the value of the boolean on the ordinal of the given description enum. */ public boolean valueOf(Enum description) { return flags[description.ordinal()]; } /** * @param index * @return the value of the boolean on the given index. */ public boolean valueAt(int index) { return flags[index]; } /** * @param description * @param flag * @return itself or the flipped instance which has the same value as the given value * on the ordinal of the given description enum. */ public SixFlags resolve(Enum description, boolean flag) { int ordinal = description.ordinal(); return flag == flags[ordinal] ? this : flippedFlags[ordinal]; } /** * @param index * @param flag * @return itself or the flipped instance which has the same value as the given value * on the given index. */ public SixFlags resolve(int index, boolean flag) { return flag == flags[index] ? this : flippedFlags[index]; } /** * Build the following line according to its value, * |YYYYYY|0b111111=63|Y|Y|Y|Y|Y|Y|_YYYYY |Y_YYYY |YY_YYY |YYY_YY |YYYY_Y |YYYYY_ | * * @return */ private String describeThis() { return concatenate("|", name(), "|0b", encodeAll(this), "=", key(this), value(this), flippedFlags(this), "\n"); } /** * Description of this instance using default format. * * @return */ @Override public String toString() { return description; } /** * Description this instance according to the give description enum instances. * * @param descriptions * @return */ public String toString(Enum... descriptions) { return concatenate("[", describe(this.flags, descriptions), "]"); } static class Helper { static void flipFlags() { for (int key : lookup.keySet()) { SixFlags original = lookup.get(key); for (int i = 0; i < original.flags.length; i++) { int flippedKey = original.flags[i] ? key ^ 1 << i : key | 1 << i; SixFlags flipped = lookup.get(flippedKey); original.flippedFlags[i] = flipped; flipped.flippedFlags[i] = original; } } lookup.clear(); lookup = null; } static boolean[] init(SixFlags flag) { int length = flag.name().length(); boolean[] flags = new boolean[length]; for (int i = 0; i < length; i++) { flags[i] = flag.name().charAt(i) == 'Y'; } return flags; } static int encodeAll(boolean... flags) { int code = 0; int mask = 1; for (boolean flag : flags) { if (flag) { code |= mask; } mask <<= 1; } return code; } static CharSequence displayAll() { StringBuilder sb = new StringBuilder(); for (SixFlags flag : SixFlags.values()) { sb.append(flag.describeThis()); } return sb; } static CharSequence flippedFlags(SixFlags flag) { StringBuilder sb = new StringBuilder(); for (SixFlags flippedFlag : flag.flippedFlags) { sb.append(flippedFlag.name() + " |"); } return sb; } static CharSequence value(SixFlags flag) { StringBuilder sb = new StringBuilder(); for (boolean value : flag.flags) { sb.append(value ? "Y|" : "_|"); } return sb; } static CharSequence key(SixFlags flag) { int key = encodeAll(flag.flags); return concatenate((key < 10 ? " " : ""), key, "|"); } static CharSequence encodeAll(SixFlags flag) { StringBuilder sb = new StringBuilder(); for (int i = flag.flags.length - 1; i >= 0; i--) { sb.append(flag.flags[i] ? "1" : "0"); } return sb; } static CharSequence line() { return "+------+-----------+-----------+-----------------------------------------------+\n"; } static CharSequence header2() { return concatenate("|flag |key |", header("|"), header(" |"), "\n"); } static CharSequence header(String filler) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < YYYYYY.flippedFlags.length; i++) { sb.append(i + filler); } return sb; } static CharSequence header() { return concatenate(line(), "| | flags | flippedFlags |\n", line()); } static CharSequence describe(boolean flags[], Enum... indexes) { StringBuilder sb = new StringBuilder(); for (Enum index : indexes) { sb.append(concatenate(flags[index.ordinal()] ? "" : "!", index.name(), ",")); } return removeLastChar(sb); } static CharSequence describe(String desc, boolean[] flags) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < flags.length; i++) { sb.append(concatenate(flags[i] ? "" : "!", desc + i, ",")); } return removeLastChar(sb); } static String concatenate(Object... desc) { return on("").join(desc); } static CharSequence removeLastChar(StringBuilder sb) { return sb.deleteCharAt(sb.length() - 1).toString(); } static Map<Integer, SixFlags> lookup = new HashMap<Integer, SixFlags>(); } }
This is what it prints,
+------+-----------+-----------+-----------------------------------------------+ | | flags | flippedFlags | +------+-----------+-----------+-----------------------------------------------+ |flag |key |0|1|2|3|4|5|0 |1 |2 |3 |4 |5 | +------+-----------+-----------+-----------------------------------------------+ |YYYYYY|0b111111=63|Y|Y|Y|Y|Y|Y|_YYYYY |Y_YYYY |YY_YYY |YYY_YY |YYYY_Y |YYYYY_ | |_YYYYY|0b111110=62|_|Y|Y|Y|Y|Y|YYYYYY |__YYYY |_Y_YYY |_YY_YY |_YYY_Y |_YYYY_ | |Y_YYYY|0b111101=61|Y|_|Y|Y|Y|Y|__YYYY |YYYYYY |Y__YYY |Y_Y_YY |Y_YY_Y |Y_YYY_ | |__YYYY|0b111100=60|_|_|Y|Y|Y|Y|Y_YYYY |_YYYYY |___YYY |__Y_YY |__YY_Y |__YYY_ | |YY_YYY|0b111011=59|Y|Y|_|Y|Y|Y|_Y_YYY |Y__YYY |YYYYYY |YY__YY |YY_Y_Y |YY_YY_ | |_Y_YYY|0b111010=58|_|Y|_|Y|Y|Y|YY_YYY |___YYY |_YYYYY |_Y__YY |_Y_Y_Y |_Y_YY_ | |Y__YYY|0b111001=57|Y|_|_|Y|Y|Y|___YYY |YY_YYY |Y_YYYY |Y___YY |Y__Y_Y |Y__YY_ | |___YYY|0b111000=56|_|_|_|Y|Y|Y|Y__YYY |_Y_YYY |__YYYY |____YY |___Y_Y |___YY_ | |YYY_YY|0b110111=55|Y|Y|Y|_|Y|Y|_YY_YY |Y_Y_YY |YY__YY |YYYYYY |YYY__Y |YYY_Y_ | |_YY_YY|0b110110=54|_|Y|Y|_|Y|Y|YYY_YY |__Y_YY |_Y__YY |_YYYYY |_YY__Y |_YY_Y_ | |Y_Y_YY|0b110101=53|Y|_|Y|_|Y|Y|__Y_YY |YYY_YY |Y___YY |Y_YYYY |Y_Y__Y |Y_Y_Y_ | |__Y_YY|0b110100=52|_|_|Y|_|Y|Y|Y_Y_YY |_YY_YY |____YY |__YYYY |__Y__Y |__Y_Y_ | |YY__YY|0b110011=51|Y|Y|_|_|Y|Y|_Y__YY |Y___YY |YYY_YY |YY_YYY |YY___Y |YY__Y_ | |_Y__YY|0b110010=50|_|Y|_|_|Y|Y|YY__YY |____YY |_YY_YY |_Y_YYY |_Y___Y |_Y__Y_ | |Y___YY|0b110001=49|Y|_|_|_|Y|Y|____YY |YY__YY |Y_Y_YY |Y__YYY |Y____Y |Y___Y_ | |____YY|0b110000=48|_|_|_|_|Y|Y|Y___YY |_Y__YY |__Y_YY |___YYY |_____Y |____Y_ | |YYYY_Y|0b101111=47|Y|Y|Y|Y|_|Y|_YYY_Y |Y_YY_Y |YY_Y_Y |YYY__Y |YYYYYY |YYYY__ | |_YYY_Y|0b101110=46|_|Y|Y|Y|_|Y|YYYY_Y |__YY_Y |_Y_Y_Y |_YY__Y |_YYYYY |_YYY__ | |Y_YY_Y|0b101101=45|Y|_|Y|Y|_|Y|__YY_Y |YYYY_Y |Y__Y_Y |Y_Y__Y |Y_YYYY |Y_YY__ | |__YY_Y|0b101100=44|_|_|Y|Y|_|Y|Y_YY_Y |_YYY_Y |___Y_Y |__Y__Y |__YYYY |__YY__ | |YY_Y_Y|0b101011=43|Y|Y|_|Y|_|Y|_Y_Y_Y |Y__Y_Y |YYYY_Y |YY___Y |YY_YYY |YY_Y__ | |_Y_Y_Y|0b101010=42|_|Y|_|Y|_|Y|YY_Y_Y |___Y_Y |_YYY_Y |_Y___Y |_Y_YYY |_Y_Y__ | |Y__Y_Y|0b101001=41|Y|_|_|Y|_|Y|___Y_Y |YY_Y_Y |Y_YY_Y |Y____Y |Y__YYY |Y__Y__ | |___Y_Y|0b101000=40|_|_|_|Y|_|Y|Y__Y_Y |_Y_Y_Y |__YY_Y |_____Y |___YYY |___Y__ | |YYY__Y|0b100111=39|Y|Y|Y|_|_|Y|_YY__Y |Y_Y__Y |YY___Y |YYYY_Y |YYY_YY |YYY___ | |_YY__Y|0b100110=38|_|Y|Y|_|_|Y|YYY__Y |__Y__Y |_Y___Y |_YYY_Y |_YY_YY |_YY___ | |Y_Y__Y|0b100101=37|Y|_|Y|_|_|Y|__Y__Y |YYY__Y |Y____Y |Y_YY_Y |Y_Y_YY |Y_Y___ | |__Y__Y|0b100100=36|_|_|Y|_|_|Y|Y_Y__Y |_YY__Y |_____Y |__YY_Y |__Y_YY |__Y___ | |YY___Y|0b100011=35|Y|Y|_|_|_|Y|_Y___Y |Y____Y |YYY__Y |YY_Y_Y |YY__YY |YY____ | |_Y___Y|0b100010=34|_|Y|_|_|_|Y|YY___Y |_____Y |_YY__Y |_Y_Y_Y |_Y__YY |_Y____ | |Y____Y|0b100001=33|Y|_|_|_|_|Y|_____Y |YY___Y |Y_Y__Y |Y__Y_Y |Y___YY |Y_____ | |_____Y|0b100000=32|_|_|_|_|_|Y|Y____Y |_Y___Y |__Y__Y |___Y_Y |____YY |______ | |YYYYY_|0b011111=31|Y|Y|Y|Y|Y|_|_YYYY_ |Y_YYY_ |YY_YY_ |YYY_Y_ |YYYY__ |YYYYYY | |_YYYY_|0b011110=30|_|Y|Y|Y|Y|_|YYYYY_ |__YYY_ |_Y_YY_ |_YY_Y_ |_YYY__ |_YYYYY | |Y_YYY_|0b011101=29|Y|_|Y|Y|Y|_|__YYY_ |YYYYY_ |Y__YY_ |Y_Y_Y_ |Y_YY__ |Y_YYYY | |__YYY_|0b011100=28|_|_|Y|Y|Y|_|Y_YYY_ |_YYYY_ |___YY_ |__Y_Y_ |__YY__ |__YYYY | |YY_YY_|0b011011=27|Y|Y|_|Y|Y|_|_Y_YY_ |Y__YY_ |YYYYY_ |YY__Y_ |YY_Y__ |YY_YYY | |_Y_YY_|0b011010=26|_|Y|_|Y|Y|_|YY_YY_ |___YY_ |_YYYY_ |_Y__Y_ |_Y_Y__ |_Y_YYY | |Y__YY_|0b011001=25|Y|_|_|Y|Y|_|___YY_ |YY_YY_ |Y_YYY_ |Y___Y_ |Y__Y__ |Y__YYY | |___YY_|0b011000=24|_|_|_|Y|Y|_|Y__YY_ |_Y_YY_ |__YYY_ |____Y_ |___Y__ |___YYY | |YYY_Y_|0b010111=23|Y|Y|Y|_|Y|_|_YY_Y_ |Y_Y_Y_ |YY__Y_ |YYYYY_ |YYY___ |YYY_YY | |_YY_Y_|0b010110=22|_|Y|Y|_|Y|_|YYY_Y_ |__Y_Y_ |_Y__Y_ |_YYYY_ |_YY___ |_YY_YY | |Y_Y_Y_|0b010101=21|Y|_|Y|_|Y|_|__Y_Y_ |YYY_Y_ |Y___Y_ |Y_YYY_ |Y_Y___ |Y_Y_YY | |__Y_Y_|0b010100=20|_|_|Y|_|Y|_|Y_Y_Y_ |_YY_Y_ |____Y_ |__YYY_ |__Y___ |__Y_YY | |YY__Y_|0b010011=19|Y|Y|_|_|Y|_|_Y__Y_ |Y___Y_ |YYY_Y_ |YY_YY_ |YY____ |YY__YY | |_Y__Y_|0b010010=18|_|Y|_|_|Y|_|YY__Y_ |____Y_ |_YY_Y_ |_Y_YY_ |_Y____ |_Y__YY | |Y___Y_|0b010001=17|Y|_|_|_|Y|_|____Y_ |YY__Y_ |Y_Y_Y_ |Y__YY_ |Y_____ |Y___YY | |____Y_|0b010000=16|_|_|_|_|Y|_|Y___Y_ |_Y__Y_ |__Y_Y_ |___YY_ |______ |____YY | |YYYY__|0b001111=15|Y|Y|Y|Y|_|_|_YYY__ |Y_YY__ |YY_Y__ |YYY___ |YYYYY_ |YYYY_Y | |_YYY__|0b001110=14|_|Y|Y|Y|_|_|YYYY__ |__YY__ |_Y_Y__ |_YY___ |_YYYY_ |_YYY_Y | |Y_YY__|0b001101=13|Y|_|Y|Y|_|_|__YY__ |YYYY__ |Y__Y__ |Y_Y___ |Y_YYY_ |Y_YY_Y | |__YY__|0b001100=12|_|_|Y|Y|_|_|Y_YY__ |_YYY__ |___Y__ |__Y___ |__YYY_ |__YY_Y | |YY_Y__|0b001011=11|Y|Y|_|Y|_|_|_Y_Y__ |Y__Y__ |YYYY__ |YY____ |YY_YY_ |YY_Y_Y | |_Y_Y__|0b001010=10|_|Y|_|Y|_|_|YY_Y__ |___Y__ |_YYY__ |_Y____ |_Y_YY_ |_Y_Y_Y | |Y__Y__|0b001001= 9|Y|_|_|Y|_|_|___Y__ |YY_Y__ |Y_YY__ |Y_____ |Y__YY_ |Y__Y_Y | |___Y__|0b001000= 8|_|_|_|Y|_|_|Y__Y__ |_Y_Y__ |__YY__ |______ |___YY_ |___Y_Y | |YYY___|0b000111= 7|Y|Y|Y|_|_|_|_YY___ |Y_Y___ |YY____ |YYYY__ |YYY_Y_ |YYY__Y | |_YY___|0b000110= 6|_|Y|Y|_|_|_|YYY___ |__Y___ |_Y____ |_YYY__ |_YY_Y_ |_YY__Y | |Y_Y___|0b000101= 5|Y|_|Y|_|_|_|__Y___ |YYY___ |Y_____ |Y_YY__ |Y_Y_Y_ |Y_Y__Y | |__Y___|0b000100= 4|_|_|Y|_|_|_|Y_Y___ |_YY___ |______ |__YY__ |__Y_Y_ |__Y__Y | |YY____|0b000011= 3|Y|Y|_|_|_|_|_Y____ |Y_____ |YYY___ |YY_Y__ |YY__Y_ |YY___Y | |_Y____|0b000010= 2|_|Y|_|_|_|_|YY____ |______ |_YY___ |_Y_Y__ |_Y__Y_ |_Y___Y | |Y_____|0b000001= 1|Y|_|_|_|_|_|______ |YY____ |Y_Y___ |Y__Y__ |Y___Y_ |Y____Y | |______|0b000000= 0|_|_|_|_|_|_|Y_____ |_Y____ |__Y___ |___Y__ |____Y_ |_____Y | +------+-----------+-----------+-----------------------------------------------+ |flag |key |0|1|2|3|4|5|0 |1 |2 |3 |4 |5 | +------+-----------+-----------+-----------------------------------------------+ | | flags | flippedFlags | +------+-----------+-----------+-----------------------------------------------+
Why do I call it SixFlags? :)
comparing mockito and powermock
I changed job so I don't work on Selenium as much as the last project. Right now I am writing more junit tests than functional tests. I have observed this interesting behavior of some popular mocking libraries and I would like to share it with you.
I have written two tests for this class, one in Mockito, one in PowerMock,
Now let us add one line code to the Developer class,
This means, in my opinion which may not matter too much, test written in PowerMock is safer than Mockito. What plays the critical role?
I have written two tests for this class, one in Mockito, one in PowerMock,
package com.algocrafts.development; public class Developer { private final SVN svn; private final Build build; private final Activities activities; public Developer(SVN svn, Build build, Activities activities) { this.svn = svn; this.build = build; this.activities = activities; } public void work() { activities.standupMeeting(); activities.tdd(); if (build.isGreen()) { svn.checkin(); } else { activities.drinkCoffee(); } activities.lunch(); activities.tdd(); } }Test in Mockito
package com.algocrafts.development; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import static org.mockito.Mockito.*; @RunWith(MockitoJUnitRunner.class) public class DeveloperMockitoTest { @Mock private SVN svn; @Mock private Build build; @Mock private Activities activities; @Test public void whenBuildIsGreen() { when(build.isGreen()).thenReturn(true); new Developer(svn, build, activities).work(); verify(activities).standupMeeting(); verify(activities, times(2)).tdd(); verify(svn).checkin(); verify(activities, never()).drinkCoffee(); verify(activities).lunch(); } @Test public void whenBuildIsNotGreen() { when(build.isGreen()).thenReturn(false); new Developer(svn, build, activities).work(); verify(activities).standupMeeting(); verify(activities, times(2)).tdd(); verify(svn, never()).checkin(); verify(activities).drinkCoffee(); verify(activities).lunch(); } }Test in PowerMock,
package com.algocrafts.development; import org.easymock.EasyMock; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.api.easymock.PowerMock; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import static org.easymock.EasyMock.expect; import static org.powermock.api.easymock.PowerMock.expectLastCall; import static org.powermock.api.easymock.PowerMock.replayAll; import static org.powermock.api.easymock.PowerMock.verifyAll; @RunWith(PowerMockRunner.class) @PrepareForTest({Developer.class, SVN.class, Build.class, Activities.class}) public class DeveloperPowerTest { private SVN svn; private Build build; private Activities activities; @Test public void whenBuildIsGreen() { activities = PowerMock.createMock(Activities.class); activities.standupMeeting(); expectLastCall(); activities.tdd(); expectLastCall().times(2); build = PowerMock.createMock(Build.class); expect(build.isGreen()).andReturn(true); svn = PowerMock.createMock(SVN.class); svn.checkin(); expectLastCall(); activities.lunch(); expectLastCall(); replayAll(); new Developer(svn, build, activities).work(); verifyAll(); } @Test public void whenBuildIsNotGreen() { activities = PowerMock.createMock(Activities.class); activities.standupMeeting(); expectLastCall(); activities.tdd(); expectLastCall().times(2); build = PowerMock.createMock(Build.class); expect(build.isGreen()).andReturn(false); activities.drinkCoffee(); EasyMock.expectLastCall(); activities.lunch(); expectLastCall(); replayAll(); new Developer(svn, build, activities).work(); verifyAll(); } }You may notice that the test in Mockito is simpler, but that's not the point I am trying to make.
Now let us add one line code to the Developer class,
package com.algocrafts.development; public class Developer { private final SVN svn; private final Build build; private final Activities activities; public Developer(SVN svn, Build build, Activities activities) { this.svn = svn; this.build = build; this.activities = activities; } public void work() { activities.standupMeeting(); activities.tdd(); if (build.isGreen()) { activities.stareAtScreenForHalfHourDoingNothing(); //This line is added by mistake. svn.checkin(); } else { activities.drinkCoffee(); } activities.lunch(); activities.tdd(); } }and run both tests, the test written in Mockito still passes but the the test written in PowerMock fails. What does this mean?
This means, in my opinion which may not matter too much, test written in PowerMock is safer than Mockito. What plays the critical role?
PowerMock.verifyAll();
Subscribe to:
Posts (Atom)