NULL tolerant sorting with Java 8

How to sort null tolerant, in reverse order, by created date.

Comparator<? super Item> comparator = 
          Comparator.comparing(Item::getCreated,
                Comparator.nullsLast(Comparator.reverseOrder()));

List<Item> sorted = list
                   .stream()
                   .sorted(comparator)
                   .collect(Collectors.toList());
Advertisements

Too lazy to use Regex

Task: parse given string after ‘:’ into a collection by given pattern “string:string,string,string,..”

e.g. “str_0:str_1 ,   str_2 ,str_3  ” -> a collection with 3 members “str_1”, “str_2”, “str_3”

 private static List<String> parseRow(final String row){
    final List<String> ret = new ArrayList<String>();

    final int length = row.length();
    int count = 0;
    char[] word = new char[length];
    boolean start = false;
    for (int i = 0; i < length; ++i){
       if ((Character.isWhitespace(row.charAt(i)))){
       // check the end of the row
          if (i == length - 1){
            // collect the last word
             ret.add(new String(word, 0, count));
          }
      // ignore whitespace
        continue;
       }
       if (row.charAt(i) == ':'){
         // start collecting after :
         start = true;
         count = 0;
         // ignore :
         continue;
       }

       if (start){
          if (row.charAt(i) == ','){
             // collect the word
             ret.add(new String(word, 0, count));
             // initiate the new one
             count = 0;
             word = new char[length];
           } else {
             // collect characters
             word[(count++)] = row.charAt(i);
             if (i == length - 1){
                // collect the last word
                ret.add(new String(word, 0, count));
             }
           }
        }
    }
    return ret;
}

How to transform List with List of references into a Map by reference

class Media {
        String name;
        Set<String> refs;

        public Media(String name, String... refs)
        {
            this.name = name;
            this.refs = new HashSet<>();
            if (refs != null)
                this.refs.addAll(Arrays.asList(refs));
        }

        public Set<String> getRefs()
        {
            return refs;
        }

        @Override
        public String toString()
        {
            return this.name;
        }
    }

Step 1. Flatten the List of references

Collection<Media> input = new ArrayList<Test.Media>();
input.add(new Media("media 1", new String[] { "1", "2" }));
input.add(new Media("media 2", new String[] { "1", "4" }));
input.add(new Media("media 3", new String[] { "2", "3", "4" }));
input.add(new Media("media 4", new String[] { "4" }));
input.add(new Media("media 5", new String[] {}));
input.add(new Media("media 6", new String[] { "6", "7" }));
input.add(new Media("media 7", new String[] { "7", "6" }));
input.add(new Media("media 8", new String[] { "8" }));
        
Collector<Media, ?, Map<Set<String>, Set<Media>>> collector = 
Collectors.groupingBy((Media asset) -> asset.getRefs(),Collectors.toSet());
Map<Set<String>, Set<Media>> grouped = input.stream().collect(collector);

OUTPUT:

for (Entry<Set<String>, Set<Media>> entry : grouped.entrySet()){
    System.out.println(entry.getKey() + " " + entry.getValue());
}
[] [media 5]
[1, 2] [media 1]
[4] [media 4]
[1, 4] [media 2]
[8] [media 8]
[2, 3, 4] [media 3]
[6, 7] [media 6, media 7]

Step 2. Collect into the Map

Map<String, Set<Media>> groupedAssets = input
    .stream()
    .flatMap(w -> w.getRefs().stream()
        .map(s -> new AbstractMap.SimpleEntry<String, Media>(s, w)))
    .collect(
        Collectors.groupingBy(
           e -> ((SimpleEntry<String, Media>) e).getKey(),
        Collectors.mapping(
           e -> ((SimpleEntry<String, Media>) e).getValue(), 
                  Collectors.toSet())));

OUTPUT

1 [media 1, media 2]
2 [media 1, media 3]
3 [media 3]
4 [media 4, media 3, media 2]
6 [media 7, media 6]
7 [media 7, media 6]
8 [media 8]

 

How to write beautiful JavaDoc

You know well documented API. You know how good it is to have the important information right at a glance. Although to write nice comments, is not really hard ..

Here is a example inspired by Methods in org.apache.commons.lang

   /**
     * <p>Your Method description....</p>
     * 
     * <pre>
     * yourMethod(null)                       = [null, null]
     * yourMethod('')                         = ['', null]
     * yourMethod(...)                        = [..., ...]
     * </pre>
     * 
     * @param string ...
     * @return [0] ...
     */
    private String[] yourMethod(final String string) {
        ...
    }

Not all logger are the same

The fact that the logging is important, it’s clear for everyone. But the fact, that the logging can cost performance, isn’t that clear. Writing of sensible log output also costs a lot of time and is more assiduity work (for us programmers and we are so lazy 🙂 That’s why I’m always grateful for the frameworks that relieve me of some work.

So a few examples

System.out.println("This is a log output for var "+var+" .. ");

Let’s see.. System.out.println is always executed and is a console output. It has practically no influence when and where the log messages are stored. The second point is the String concatenation. There are better options to do it for e.g. StringBuilder.

Let us take one of the logging frameworks, for example log4j, which allow to set the warning level and to write the log files in a specific format.

org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(SomeClass.class);

if (logger.isInfoEnabled()){
   logger.info("This is a log output for var "+var+" .. ");
}
Without “if (logger.isInfoEnabled())”, the logging-line is always executed, even if the message is not logged into the log file.
But do you really want always ask if INFO level is turned on? For this purpose there are already suitable solution.
org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(SomeClass.class);

logger.info("This is a log output for var {} .. ", var);

Let’s look inside the slf4j. Beautiful, isn’t it?

   public void warn(String format, Object arg)  {
        if(logger.isEnabledFor(Level.WARN)) {
            FormattingTuple ft = MessageFormatter.format(format, arg);
            logger.log(FQCN, Level.WARN, ft.getMessage(), ft.getThrowable());
        }
    }

Wicket custom message for a default validator

Do you use the standard Wicket validator or converter and want to override the default error message for a single field, then try the following.

E.g. for the Wicket-ID “wicketid23” and the standard IConverter put the resource key in the .properties file of the page with the field.

wicketid23.IConverter=My custom message for the field withe the wicket-ID 23

Some other standard messages:

 DateValidator.maximum = '${input}' muss kleiner sein als ${maximum}.
 DateValidator.minimum = '${input}' muss grösser sein als ${minimum}.
 DateValidator.range = '${input}' muss zwischen ${minimum} und ${maximum} liegen
 IConverter = '${input}' ist kein gültiger Wert für '${type}'.
 NumberValidator.maximum = '${input}' muss kleiner sein als ${maximum}.
 NumberValidator.minimum = '${input}' muss grösser sein als ${minimum}.
 NumberValidator.range = ${input} muss zwischen ${minimum} und ${maximum} liegen.
 Required = Bitte tragen Sie einen Wert im Feld '${label}' ein.
 StringValidator.exact = '${input}' ist nicht exakt ${exact} Zeichen lang.
 StringValidator.maximum = '${input}' darf maximal ${maximum} Zeichen lang sein.
 StringValidator.minimum = '${input}' muss mindestens ${minimum}
 Zeichen lang sein.