Search

Dark theme | Light theme

January 25, 2021

Java Joy: Partition Stream By Predicate

The Java Stream API has many useful methods. If we want to partition a stream of objects by a given predicate we can use the partitioningBy() method from the java.util.stream.Collectors package. We must use this method in the collect() method of the stream. The result is a Map with the keys true and false. The objects from the stream that are true for the predicate will end up in the true value list and if the result of the predicate is false the value will end up in the list of values for the false key. The partitionBy method accepts a collector as second parameter. This collector will be applied to the values before they are put in the true or false keys in the result.

In the following example we use the partitioningBy method with different streams:

package mrhaki.stream;

import java.util.stream.Stream;
import java.util.stream.Collectors;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

public class Main {
    public static void main(String[] args) {
        // Let's create an infinitive stream of integers.
        var range = Stream.iterate(0, i -> i + 1);

        // We can partition them by odd and even numbers
        // using a predicate i % 2 == 0, where i is integer from the stream.
        var oddEvenNumbers = 
            range.limit(10)
                  .collect(Collectors.partitioningBy(i -> i % 2 == 0));

        // Even numbers are assigned to the "true" key,
        // odd numbers to the "false" key.
        var odds = oddEvenNumbers.get(false);
        var evens = oddEvenNumbers.get(true);

        assert odds.size() == 5;
        assert odds.equals(List.of(1, 3, 5, 7, 9));
        assert evens.size() == 5;
        assert evens.equals(List.of(0, 2, 4, 6, 8));

        // We use a second argument to sum all odd and even numbers.
        var sumEvenOdd = 
            Stream.iterate(0, i -> i + 1)
                  .limit(100)
                  .collect(
                      Collectors.partitioningBy(i -> i % 2 == 0, 
                                                Collectors.reducing(0, (result, i) -> result += i)));

        assert sumEvenOdd.get(true) == 2450;
        assert sumEvenOdd.get(false) == 2500;
        

        // In the next exmample we start with an immutable map.
        var map = Map.of("language", "Java", "username", "mrhaki", "age", 47);

        // This time we partition on the type of the value where values
        // of type String are assigned to "true" and other types to "false".
        var partitionByStringValue = 
            map.entrySet()
               .stream()
               .collect(Collectors.partitioningBy(entry -> entry.getValue() instanceof String));

        var stringValueEntries = partitionByStringValue.get(true);
        var nonStringValueEntries = partitionByStringValue.get(false);

        assert stringValueEntries.size() == 2;

        var keys = stringValueEntries.stream().map(Map.Entry::getKey).collect(Collectors.toUnmodifiableList());
        var values = stringValueEntries.stream().map(Map.Entry::getValue).collect(Collectors.toUnmodifiableList());
        assert keys.containsAll(List.of("language", "username"));
        assert values.containsAll(List.of("Java", "mrhaki"));

        assert nonStringValueEntries.size() == 1;
        assert nonStringValueEntries.get(0).getKey().equals("age");
        assert nonStringValueEntries.get(0).getValue().equals(47);       
    }
}

Written with Java 15.0.1.

January 20, 2021

Java Joy: Turn Stream Into An Array

The Java Stream API has many useful methods. If we want to transform a Stream to a Java array we can use the toArray method. Without an argument the result is an object array (Object[]), but we can also use an argument to return an array of another type. The easiest way is to use the contructor of the array type we want as method reference. Then the result is an array of the given type with the elements of the stream.

This is very useful if we have a Java Stream and want to use the elements to invoke a method with a variable arguments parameter. In Java we can pass an array object as variable arguments argument to a method. So if we transform the Stream to an array we can invoke the method with that value.

In the following example we see how we transform a Stream with String values to a String[] object. We use this String[] object to invoke a method with a varargs parameter of type String....

package mrhaki.stream;

import java.util.stream.Stream;

public class Sample {

    public static void main(String[] args) {
        // With the .toArray method we can convert a Stream
        // to an array. 
        // We can use the constructur of the array type
        // we want to convert to as method reference to get 
        // the correct array result.
        final var result = 
            Stream.of("Java", "Clojure", "Groovy", "Kotlin")
                  .filter(language -> language.contains("o"))
                  .toArray(String[]::new);

        assert result[0].equals("Clojure");
        assert result[1].equals("Groovy");
        assert result[2].equals("Kotlin");

        // This is useful with varargs parameters as well,
        // as arrays can be used for a varargs parameter.
        assert "Clojure".equals(first(result));
        assert "Clojure".equals(first("Clojure", "Groovy", "Kotlin"));
    }

    private static String first(String... values) {
        return values[0];
    }
}

Written with Java 15.0.1

December 30, 2020

Java Joy: Optional orElse orElseGet That Is The Question

The Optional class has the orElse and orElseGet methods to return a value when the Optional object is empty. This is useful to return a default value for example. But there is a small difference between the two methods. The orElseGet method needs a Supplier argument that returns a value of the type of the Optional value. The Supplier is only invoked when the Optional value is empty. The statement passed as argument to the orElse method is always executed, even when the Optional value is not empty. Preferrably we should use orElseGet as it will only invoke statements if needed.

In the following example code we see when our method getDefaultGreeting is invoked by using orElse and orElseGet with an empty and non-empty Optional object:

package mrhaki.optional;

import java.util.Optional;

public class Sample {

    /** 
      * Keep track of total number of times method getDefaultGreeting is invoked.
      */
    private static int totalDefaultGreetingInvoked = 0;

    public static void main(String[] args) {
        // Define an empty optional.
        var name = Optional.ofNullable(null);

        // orElse returns value from argument when optional is empty.
        assert ("Hello " + name.orElse(getDefaultGreeting())).equals("Hello world");
        assert totalDefaultGreetingInvoked == 1;

        // orElseGet needs a Supplier that is executed when the optional is empty.
        assert ("Hello " + name.orElseGet(Sample::getDefaultGreeting)).equals("Hello world");
        assert totalDefaultGreetingInvoked == 2;


        // This time the optional is not empty.
        name = Optional.ofNullable("mrhaki");

        // orElse will always get the value, even when the optional is not empty.
        assert ("Hello " + name.orElse(getDefaultGreeting())).equals("Hello mrhaki");
        assert totalDefaultGreetingInvoked == 3;

        // orElseGet will not call the Supplier when the optional is not empty.
        assert ("Hello " + name.orElseGet(Sample::getDefaultGreeting)).equals("Hello mrhaki");
        assert totalDefaultGreetingInvoked == 3;
    }

    private static String getDefaultGreeting() {
        // Increase counter, so we can check the method is invoked.
        totalDefaultGreetingInvoked++;
        return "world";
    }
}

Written with Java 15.

November 2, 2020

Clojure Goodness: Getting Part Of A Vector With subvec

In Clojure we can get part of a vector collection using the subvec function. The function takes a vector as argument, a required begin index and optional end index. The returned value is a vector with part of the values of the original vector starting from the begin up to the end index. If we leave out the optional end index, the size of the vector is used as end index.

In the following example we use the subvec function with and without the end index:

(ns mrhaki.core.subvec
  (:require [clojure.test :refer [is]]))

;; Vector of some JVM languages.
(def languages ["Java" "Kotlin" "Clojure" "Groovy"])

;; Using only the start index argumnt we get all items
;; from the start index to the end.
(is (= ["Clojure" "Groovy"] (subvec languages 2)))

;; When we use the start and end index arguments
;; we get the items from start to the given end.
(is (= ["Clojure"] (subvec languages 2 3)))

Written with Clojure 1.10.1.

October 16, 2020

Enforce Encoding i18n Property Files In Keycloak Themes

Keycloak allows internationalization messages in themes. We can specify property files with messages for a specific locale. The fallback locale is always en, so we need at least the file messsages_en.properties. To use the messages from the property files we use the msg function in our Freemarker templates. We specify the message key as argument to this function and the corresponding value will be shown to the user. This function is added by Keycloak and reads the property files, finds the key and returns the value. Keycloak uses as default encoding for reading the property files ISO-8859-1. This gives issues when we use non-compliant characters in our property file, like letters with accents. We can force Keycloak to use anonther encoding by adding an extra line to our property files at the top with the encoding we want to be used. The line starts with a #, so it is a comment, followed by the text encoding: and the encoding we want, e.g. UTF-8.

In the following example we set the encoding to UTF-8 as first line in the property file, and we must make sure the file is also saved with the same encoding:

# encoding: UTF-8 
doSave=Mentés
doCancel=Mégsem
...

In the following Freemaker template we use the msg function to get the key doSave:

...
<button name="submitAction" value="Save">${msg("doSave")}<button>
...

Written with Keycloak 11.0.2.

Automatic Switching Of Java Versions With SDKMAN!

SDKMAN! is a very useful tool to manage versions of so-called software development kits. There are a lot of SDKs supported by SDKMAN!: Java, Groovy, Kotlin, Scala, Gradle, Maven, Leiningen, Micronaut, Grails, Vert.x, JBake, AsciidoctorJ and more. When we look at Java we can use a simple install java <version> command from the command-line to install a version of Java on our computer. SDKMAN! will take care of downloading the Java version and setting all the correct system variables to use that Java version. With the use command we can switch between version in the current shell we are working in. But we can even automatically switch to a specific installed Java version when we enter a directory. This is very useful when we have to work on multiple projects on our computer and each project requires a specific Java version to be used.

To support automatic switching of a Java version we must first run the env init command in the directory of our project. This creates a new file .sdkmanrc in the directory. The file contains the Java version that was active when we invoked the env init command. It is a text file so we can change the Java version in the file, or regenerate the file by running the env init command again, but with a different active Java version.

In the following shell output we have a Java 15 version set as default. We first change it to Java 11 and then run env init to set Java 11 as default version for our projec directory:

project-dir $ java -version
openjdk version "15" 2020-09-15
OpenJDK Runtime Environment AdoptOpenJDK (build 15+36)
Eclipse OpenJ9 VM AdoptOpenJDK (build openj9-0.22.0, JRE 15 Mac OS X amd64-64-Bit Compressed References 20200922_45 (JIT enabled, AOT enabled)
OpenJ9   - 1830b1927
OMR      - 73d5e7623
JCL      - 7e7613c015 based on jdk-15+36)
project-dir $ sdk use java 11.0.8.hs-adpt

Using java version 11.0.8.hs-adpt in this shell.
project-dir $ java -version
openjdk version "11.0.8" 2020-07-14
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.8+10)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.8+10, mixed mode)
project-dir $ sdk env init
.sdkmanrc created.
project-dir $ cat .sdkmanrc
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=11.0.8.hs-adpt
project-dir $

When a directory contains the .sdkmanrc file we can use the SKDMAN! env command and SDKMAN! will set the Java version based on the contents of the .sdkmanrc file. This is already useful, because we simply go to our project directory, invoke the env command and the Java version is set to the version we expect for our poject.

In the next example we start with Java 15 as default version, next we run the env command in the directory with the .sdkmanrc file:

~ $ java -version
openjdk version "15" 2020-09-15
OpenJDK Runtime Environment AdoptOpenJDK (build 15+36)
Eclipse OpenJ9 VM AdoptOpenJDK (build openj9-0.22.0, JRE 15 Mac OS X amd64-64-Bit Compressed References 20200922_45 (JIT enabled, AOT enabled)
OpenJ9   - 1830b1927
OMR      - 73d5e7623
JCL      - 7e7613c015 based on jdk-15+36
~ $ cd project-dir
project-dir $ sdk env

Using java version 11.0.8.hs-adpt in this shell.
project-dir $ java -version
openjdk version "11.0.8" 2020-07-14
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.8+10)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.8+10, mixed mode)
project-dir $

By changing a setting in the SDKMAN! configuration we can even let SDKMAN! run the env command when we change to our project directory in our shell. The default location of the configuration file is $HOME/.sdkman/etc/config and we must add the following line sdkman_auto_env=true. With this option we can simply change to our project directory and the correct Java version is set to active.

In our final example we have enabled the setting sdkman_auto_env, so when we change to the project directory the correct Java version is activated automatically:

~ $ java -version
openjdk version "15" 2020-09-15
OpenJDK Runtime Environment AdoptOpenJDK (build 15+36)
Eclipse OpenJ9 VM AdoptOpenJDK (build openj9-0.22.0, JRE 15 Mac OS X amd64-64-Bit Compressed References 20200922_45 (JIT enabled, AOT enabled)
OpenJ9   - 1830b1927
OMR      - 73d5e7623
JCL      - 7e7613c015 based on jdk-15+36
~ $ cd project-dir

Using java version 11.0.8.hs-adpt in this shell.
project-dir $ java -version
openjdk version "11.0.8" 2020-07-14
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.8+10)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.8+10, mixed mode)
project-dir $

Wrtten with SDKMAN 5.9.1+575.

October 14, 2020

Clojure Goodness: Split Collection With Predicate

To split a collection in Clojure we can use the split-with and split-at functions. The split-with function takes a predicate as first argument and a colletion as second argument. The function will return a vector with two items. The first item is the result of the function take-while with the given predicate. The second item in the result vector is the resul of the drop-while function with the same predicate.

We use the split-at function with a number as first argument followed by a collection to split based on a given number of items. Instead of using a predicate we can define the number of items that we want as the first item in the result vector. The first item in the result vector is the result of invoking the take function. The resulting number of items of the collection will be the second item in the result vector and is achieved by invoking the drop function.

In the following example we use both functions with different arguments:

(ns mrhaki.core.split
  (:require [clojure.test :refer [is]]))

;; The split-with function has a predicate and returns the result 
;; of the functions take-while and drop-while in a result vector.
(let [less-than-5? (partial > 5)
      numbers      (range 11)]
  (is (= ['(0 1 2 3 4) '(5 6 7 8 9 10)]
         (split-with less-than-5? numbers))
         [(take-while less-than-5? numbers) (drop-while less-than-5? numbers)]))

;; In this example we take while the value is a String value and 
;; drop while starting from first value that is not a String.
(letfn [(string-value? [[k v]] (instance? String v))]
  (is (= ['([:language "Clojure"] [:alias "mrhaki"]) '([:age 47] [:country "NL"])]
         (split-with string-value? {:language "Clojure" :alias "mrhaki" :age 47 :country "NL"}))))


;; Instead of splitting on a predicate we can just give the number
;; of elements we want to split on with the split-at function.
(is (= ['(0 1 2 3) '(4 5 6 7)]
       (split-at 4 (range 8))
       [(take 4 (range 8)) (drop 4 (range 8))]))

(is (= ['([:language "Clojure"] [:alias "mrhaki"] [:age 47]) '([:country "NL"])]
       (split-at 3 {:language "Clojure" :alias "mrhaki" :age 47 :country "NL"})))

Clojure 1.10.1.

October 11, 2020

Clojure Goodness: Shuffle A Collection

In Clojure we can use the shuffle function with a collection argument to get a new collection where the items of the input collection are re-ordered randomly. The function delegates to the Java java.util.Collections#shuffle method.

In the following example code we use the shuffle method:

(ns mrhaki.core.shuffle
  (:require [clojure.test :refer [is]]))

;; shuffle will return a new collection 
;; where the items are in a different order.
(shuffle (range 5)) ;; Possible collection [4 0 1 2 3]
(shuffle (range 5)) ;; Possible collection [1 3 4 2 0]

;; Define a deck of cards.
(def cards (for [suite  [\♥ \♠ \♣ \♦]
                 symbol (concat (range 2 11) [\J \Q \K \A])] 
             (str suite symbol)))

;; Some checks on our deck of cards.
(is (= 52 (count cards)))
(is (= (list "♥2" "♥3" "♥4" "♥5" "♥6" "♥7" "♥8" "♥9" "♥10" "♥J" "♥Q" "♥K" "♥A")
       (take 13 cards)))

;; Let's shuffle the deck. We get a new collection of cards ordered randomly.
(def shuffled-deck (shuffle cards))

;; Shuffled deck contains all items from the cards collection.
(is (true? (every? (set cards) shuffled-deck)))

;; We can take a number of cards.
(take 5 shuffled-deck) ;; Possible result: ("♦6" "♦10" "♥K" "♥4" "♥10")

;; We do a re-shuffle and get different cards now.
(take 5 (shuffle shuffled-deck)) ;; Possible result: ("♥10" "♥Q" "♦4" "♣8" "♠5")

Written with Clojure 1.10.1.

October 10, 2020

Clojure Goodness: Formatting With Java Format String

In Clojure we can format a string using Common Lisp format syntax or the Java format string syntax. In the post we will look at the how we can use the Java format string syntax. We must use the format function in the clojure.core namespace. The method delegates to the standard JDK String#format method. The first argument is a format string followed by one or more arguments that are used in the format string. We can look up the syntax of the format string in the Javadoc for the java.util.Formatter class.

In the following example code we use the format function with different format strings:

(ns mrhaki.string.format
  (:require [clojure.test :refer [is]])
  (:import (java.util Locale)))

;; Create new string with format string as template as first argument.
;; Following arguments are used to replace placeholders in the
;; format string.
;; Clojure will delegate to the java.lang.String#format method and
;; we can use all format string options that are defined for this method.
;; More details about the format string syntax can be found in
;; java.util.Formatter. In a REPL we can find the docs 
;; with (javadoc java.util.Formatter).
(is (= "https://www.mrhaki.com/"
       (format "https://%s/" "www.mrhaki.com")))

;; Format string with argument index to refer to one argument twice.
(is (= "clojure CLOJURE"
       (format "%1$s %1$S" "clojure")))

;; Format string to define fixed result lenght of 10 characers
;; with padding to get the given length.
(is (= "   Clojure"
       (format "%10s" "Clojure")))

;; Default Locale is used to determine how locale specific 
;; formats are applied. In the following example the default
;; decimal separator is . and group separator is , as specified
;; for the Canadian Locale.
(Locale/setDefault Locale/CANADA)
(is (= "Total: 42,000.00"
       (format "Total: %,.2f", 42000.0)))

(defn format-locale
  "Format a string using String/format with a Locale parameter"
  [locale fmt & args]
  (String/format locale fmt (to-array args)))

;; We can use a different Locale to apply different specific 
;; locale formats. In the next example we use the Dutch Locale
;; and the decimal seperator is , and the group separator is ..
(is (= "Totaal: 42.000,00"
       (format-locale (Locale. "nl") "Totaal: %,.2f" 42000.0)))

Written with Clojure 1.10.1.

October 6, 2020

Clojure Goodness: Finding The Maximum Or Minimum Value

To find the maximum or minimum value for numeric values we can use the max and min function. The functions accept one or more numeric arguments and the value that is maximum or minimum is returned. If the numbers are already in a sequence we can use apply max or apply min. If the values are not numbers we can use the max-key or min-key functions. These functions take as first argument a function that returns a number. So we can get the value that has the maximum or minimum return value for the function we pass as first argument.

In the next exmaple code we use the max, min, max-key and min-key functions:

(ns mrhaki.core.min-max
  (:require [clojure.test :refer [is]]))

;; We can use max to find the maximum number in the given arguments.
(is (= 20 (max 10 2 3 1 20)))

;; If we have a collection we can use apply max to find the maximum number.
(is (= 20 (apply max [10 2 3 1 20])))


;; We can use min to find the minimum number in the given arguments.
(is (= 1 (min 10 2 3 1 20)))

;; And also use apply min when we have collection with numbers.
(is (= 1 (apply min [10 2 3 1 20])))


;; When the arguments are not numbers we can provide a function to get
;; back numbers and use that function with max-key to find the maximum.
(is (= "Clojure" (max-key count "Java" "Groovy" "Clojure")))
(is (= "Clojure" (apply max-key count ["Java" "Groovy" "Clojure"])))


;; And to find the minimum for non-numbered arguments we can use min-key
;; with a function to get back numbers.
(is (= "Java" (min-key count "Java" "Groovy" "Clojure")))
(is (= "Java" (apply min-key count ["Java" "Groovy" "Clojure"])))

Written with Clojure 1.10.1.