Monday, 29 September 2014

Java 8 : Null? no thanks!

You know, I know, they know, everybody knows, returning null from a public method is often a bad practice.
Reasons are multiple, from the risk of raising NullPointerException in runtime to the horrible == null check in your code.

To avoid returning nulls in Java 8 you can use java.util.Optional (if you are on previous version of Java, Guava has similar functionality).

package com.marco;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class NoNullsThanks {

        private final Map<String, Integer> nameAge = new HashMap<String, Integer>();

        public Integer getAge(String name) {
                return Optional.ofNullable(nameAge.get(name)).orElse(-1);
        }

        private final Map<String, List<String>> nameEmails = new HashMap<String, List<String>>();

        public List<String> getEmails(String name) {
                return Optional.ofNullable(nameEmails.get(name)).orElse(new ArrayList<String>());
        }
}


Nice & Clean :)

Friday, 19 September 2014

Tridimensional developers

Working in the industry for a while, I noticed different behaviors between developers.

These differences are mostly related to their personalities, their team spirit and finally their entrepreneurship skills.

I identified 3 types or better 3 dimensions in which I can fit all the people I work and worked with.
Before starting and describing these dimensions, I just want to point out couple of important things.
  1. This has nothing to do with how good you are with the code or design or testing. You can be the best kickass-bruce-lee-coder, but still entering in the mono-dimensional category.
  2. This has also nothing to do with your level of seniority, I saw Juniors that can perfectly fit in the tridimensional category and team leaders falling in the mono-dimensional one. 
  3. This is perfectly applicable to ANY workplace and ANY type of job and ANY industry.
  4. It is just my point of view in a boring Friday afternoon.
So lets start :)

Mono-Dimensional Developer

You work daily at your stories/tasks

You participate to meetings about process and technologies

You communicate with other team members about status
updates, risks and issues encountered

You create and/or maintain software applications 

You keep updated yourself about emerging technologies and trends











Bidimensional Developer 

You cover the Mono-dimensional points

You help team members without being asked directly

You share with your team what you learned using slides/blogs/etc.

You actively participate to process and technology meetings, asking questions and/or proposing solutions.





Tridimensional Developer

You cover the Bidimensional points

You create, propose and promote new processes and technologies

You take team building actions when needed without being asked directly

You actively participate to meetings other than process and technology

You create blogs/wikis/etc in order to facilitate communication without being asked directly

You see opportunities everywhere and you act on them.



So which developer are you?




Friday, 12 September 2014

Friday-Benchmarking Functional Java

Lets image your product owner goes crazy one day and he asks you to do the following :

From a set of Strings as follows :

"marco_8", "john_33", "marco_1", "john_33", "thomas_5", "john_33", "marco_4", ....

give me back a comma separated String with only the marco's numbers and numbers need to be in order. 

 Example of expected result :

"1,4,8"


I will implement this logic in 4 distinct ways and I will micro benchmark each one of them. The ways I'm going to implement the logic are :

  • Traditional java with loops and all.
  • Functional with Guava 
  • Functional with java 8 stream
  • Functional with java 8 parallelStream

Code is below or in gist


package com.marco.brownbag.functional;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
import com.google.common.collect.Ordering;
public class MicroBenchMarkFunctional {

        private static final int totStrings = 200000;

        public static void main(String[] args) {

                Set<String> someNames = new HashSet<String>();

                init(someNames);

                for (int i = 1; i < totStrings; i++) {
                        someNames.add("marco_" + i);
                        someNames.add("someone_else_" + i);
                }

                System.out.println("start");

                run(someNames);

        }

        private static void run(Set<String> someNames) {
                System.out.println("========================");
                long start = System.nanoTime();
                int totalLoops = 20;
                for (int i = 1; i < totalLoops; i++) {
                        classic(someNames);
                }
                System.out.println("Classic         : " + ((System.nanoTime() - start)) / totalLoops);

                start = System.nanoTime();
                for (int i = 1; i < totalLoops; i++) {
                        guava(someNames);
                }
                System.out.println("Guava           : " + ((System.nanoTime() - start)) / totalLoops);

                start = System.nanoTime();
                for (int i = 1; i < totalLoops; i++) {
                        stream(someNames);
                }
                System.out.println("Stream          : " + ((System.nanoTime() - start)) / totalLoops);

                start = System.nanoTime();
                for (int i = 1; i < totalLoops; i++) {
                        parallelStream(someNames);
                }
                System.out.println("Parallel Stream : " + ((System.nanoTime() - start)) / totalLoops);

                System.out.println("========================");
        }

        private static void init(Set<String> someNames) {
                someNames.add("marco_1");
                classic(someNames);
                guava(someNames);
                stream(someNames);
                parallelStream(someNames);
                someNames.clear();
        }

        private static String stream(Set<String> someNames) {
                return someNames.stream().filter(element -> element.startsWith("m")).map(element -> element.replaceAll("marco_", "")).sorted()
                                .collect(Collectors.joining(","));
        }

        private static String parallelStream(Set<String> someNames) {
                return someNames.parallelStream().filter(element -> element.startsWith("m")).map(element -> element.replaceAll("marco_", "")).sorted()
                                .collect(Collectors.joining(","));
        }

        private static String guava(Set<String> someNames) {
                return Joiner.on(',').join(
                                Ordering.from(String.CASE_INSENSITIVE_ORDER).immutableSortedCopy(
                                                Collections2.transform(Collections2.filter(someNames, Predicates.containsPattern("marco")), REPLACE_MARCO)));

        }

        private static Function<String, String> REPLACE_MARCO = new Function<String, String>() {
                @Override
                public String apply(final String element) {
                        return element.replaceAll("marco_", "");
                }
        };

        private static String classic(Set<String> someNames) {

                List<String> namesWithM = new ArrayList<String>();

                for (String element : someNames) {
                        if (element.startsWith("m")) {
                                namesWithM.add(element.replaceAll("marco_", ""));
                        }
                }

                Collections.sort(namesWithM);

                StringBuilder commaSeparetedString = new StringBuilder();

                Iterator<String> namesWithMIterator = namesWithM.iterator();
                while (namesWithMIterator.hasNext()) {
                        commaSeparetedString.append(namesWithMIterator.next());
                        if (namesWithMIterator.hasNext()) {
                                commaSeparetedString.append(",");
                        }

                }

                return commaSeparetedString.toString();

        }
}


Two points before we dig into performance :

  1. Forget about the init() method, that one is just to initialize objects in the jvm otherwise numbers are just crazy.
  2. The java 8 functional style looks nicer and cleaner than guava and than developing in a traditional way :).

Performance:



Running that program on my mac with 4 cores, the result is the following :


========================
Classic              : 151941400
Guava               : 238798150
Stream              : 151853850
Parallel Stream : 55724700
========================

Parallel Stream is 3 times faster. This is because java will split the job in multiple tasks (total of tasks depends on your machine, cores, etc) and will run them in parallel, aggregating the result at the end.
Classic Java and java 8 stream have more or less the same performance.
Guava is the looser.


That is amazing, so someone could think:
"cool, I can just always use parallelStream and I will have big bonus at the end of the year". 
But life is never easy. Here is what happens when you reduce that Set of strings from 200.000 to 20

========================
Classic              : 36950
Guava               : 69650
Stream              : 29850
Parallel Stream : 143350
========================

Parallel Stream became damn slow. This because parallelStream has a big overhead in terms of initializing and managing multitasking and assembling back the results.
Java 8 stream looks now the winner compare to the other 2.

Ok, at this point, someone could say something like :
"for collections with lots of elements I use parallelStream, otherwise I use stream"
That would be nice and simple to get, but what happens when I reduce that Set again from 20 to 2?
This :

========================
Classic              : 8500
Guava               : 20050
Stream              : 24700
Parallel Stream : 67850
========================

Classic java loops are faster with very few elements.

So at this point I can go back to my crazy product owner and ask how many Strings he thinks to have in that input collection. 20? less? more? much more?


Like the Carpenter says :

Measure Twice, Cut Once!!

 

 







Monday, 8 September 2014

Migrate your project from SVN to Git Stash in few steps

Step by step guide on how to migrate your SVN repository with all its history to the Stash, the Atlassian git manager.


Only once :

  1. add the ssh key
  2. open a terminal 
  3. create the authors.txt file in ~/Documents/ 
  4. git config svn.authorsfile ~/Documents/authors.txt 

authors.txt format :

username = Name LastName <email>

example :

gordof = Gordon Flash <gordon.flash@superhero.com>
marcoc = Marco Castigliego <marco.castigliego@superhero.com>



For each project :

For this example I will migrate a project called super-hero-service.

  1. Tell your team members to not commit on the project during the process.
  2. Open a terminal
  3. cd ~
  4. mkdir migration
  5. cd migration
  6. git svn clone svn+ssh://marcoc@svn.superhero.com/com/super/hero/Services/ --trunk=super-hero-service super-hero-service
  7. go to take a coffee
  8. cd super-hero-service
  9. git svn show-ignore (Which outputs everything in the SVN ignore property to the console. Then you can copy this to a new file called .gitignore at the root of your repository.Add and commit the file.)
  10. go to https://stash.superhero.com/projectsServices and create a repository called super-hero-service
  11. git remote add origin ssh://git@stash.suoperhero.com:2022/services/super-hero-service.git
  12. git push -u origin master