Learn Flyweight patterns and ConcurrentHashMap in Java

I'm reading the third edition of Essential Java in an attempt to update my knowledge of Java, and the Flyweight pattern comes up. By the way, I've never done this pattern properly, so I decided to understand and try it.

Flyweight pattern

The Flyweight pattern is one of GoF's design patterns and is a pattern for solving the following problems.

Definition

I'm not sure if this is all.

image.png

Looking at the sequence, it looks like a cached pattern. Looking at the history item, it seems that it was used in the document editor, and if the font information, for example, the alphabet is 26 characters, if an instance of the character is created each time, a large number of instances will be generated. Therefore, it seems to be a mechanism to create only 26 instances and cache and return them.

Guitar fret sound sample

Let's make a sample and understand it. In short, it's a factory cache, so it's easy. This time the subject is a little brute force, but it is an application that returns the sound of the place when you enter the string number of the guitar and the fret number. Since there are only 12 sounds, 12 instances should be enough. Try using Sound as the Flyweight object in the design pattern diagram.

Sound.java : Flyweight

package com.company;

public interface Sound {
    void Play();
}

SoundImpl : Flywieght1

package com.company;

public class SoundImpl implements Sound{
    private String note;
    public SoundImpl(String note) {
        this.note = note;
    }
    @Override
    public void Play() {
        System.out.println(note + "- ♪");
    }
    public String getNote() {
        return note;
    }
}

SoundFactory : FlywieghtFactory

package com.company;

import java.util.HashMap;

public class SoundFactory {
    private static Map<String, Sound> sounds = new HashMap<String, Sound>();
    public static Sound getSound(String note) {
        if (sounds.containsKey(note)){
            return sounds.get(note);
        } else {
            Sound sound = new SoundImpl(note);
            sounds.put(note, sound);
            return sound;
        }
    }
}

The Main swelled up and wasn't cool, but anyway, I got something that worked. The simple point is to give Factory a Map and cache it. It's easy.

Main.java

package com.company;

import java.util.HashMap;

public class Main {
    private static HashMap<Integer, Integer> stringMap = new HashMap<Integer, Integer>();
    private static HashMap<Integer, String> notes = new HashMap<Integer, String>();
    private static void setup() {
        stringMap.put(1, 4);
        stringMap.put(2, 11);
        stringMap.put(3, 7);
        stringMap.put(4, 2);
        stringMap.put(5, 9);
        stringMap.put(6, 4);

        notes.put(0, "C");
        notes.put(1, "C#");
        notes.put(2, "D");
        notes.put(3, "D#");
        notes.put(4, "E");
        notes.put(5, "F");
        notes.put(6, "F#");
        notes.put(7, "G");
        notes.put(8, "G#");
        notes.put(9, "A");
        notes.put(10, "A#");
        notes.put(11, "B");

    }
    public static void main(String[] args) {
        setup();
	    while(true) {
	        java.io.Console con = System.console();
	        if (con != null) {
                String input = con.readLine("string:fret:");
                if (input.contains("exit")) {
                    System.out.println("Closing ...");
                    System.exit(0);
                } else {
                    String[] stringFret = input.split(":");
                    int openNote = stringMap.get(Integer.parseInt(stringFret[0]));
                    int fret = Integer.parseInt(stringFret[1]);
                    int note = openNote + fret;
                    if (note >= 12) {
                        note = note - 12;
                    }
                    String noteString =  notes.get(new Integer(note));
                    Sound sound = SoundFactory.getSound(noteString);
                    sound.Play();
                }
            }

        }
    }

}

What happens with Concurrent?

Since the main part was easy, I'm not very familiar with Java, so I decided to take steps. What should I do if it is in a concurrent state? HashMap doesn't seem to be Thread Safe. If you want to make it Thread Safe, you can use HashTable or ConcurrentHashMap. What's the difference?

HashTable and ConcurrentHashMap

Looking at these comparisons, it seems clear that there are many cases where it makes sense to use ConcurrentHashMap. Let's implement it.

Implemented using Micronaut

Sounds easy.

Micronaut install

In my environment, the source of chocolatey was rewritten, so I will restore it to the default. It can be installed by starting PowerShell with Administrator privileges.

$ choco isntall micronaut -s https://chocolatey.org/api/v2/

Generate project via template

You can generate a Maven project like this:

$ mn create-app flyweight-server --build maven

First of all, this will bring the server up. The point I was addicted to was that I wrote the code using IntelliJ, but the Terminal JAVA_HOME was not set and I got the error message Caused by: java.lang.IllegalArgumentException: invalid target release: 11. It came out. Originally, only the Java8 JDK was in the path, so that's a problem, but I reminded myself that the basics of Java are to set JAVA_HOME and pass% JAVA_HOME% / bin in the path. Otherwise maven will not work properly.

$ cd flywieght-server
$ mvn clean package
$ java -jar .\target\flyweight-server-0.1.jar

Add controller

I will add the controller. Micronaut seems to support various functions like a lightweight version of Spring. I just wanted an Http Server this time, so I'll look at the manual and write the code. To do ruling with a controller, it seems that you just have to write the controller. It's very easy.

*The HTTP Server

FlyweightController.java

package flyweight.server;

import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;

@Controller("/flyweight")
public class FlyweightController {
    @Get(value = "/{string}/{fret}", produces = MediaType.TEXT_PLAIN)
    public String index(Integer string, Integer fret){
        return "String: " + string + " Fret: "+ fret;
    }
}

Now that it works, I understand what the controller wants to know. Next, let's modify the Flyweight app. I think this works multithreaded, so let's use the ConcurrentHashMap earlier.

How shorter it was than before. The computeIfAbsent method now accepts functions and guarantees atomic behavior. This is the same atmosphere as C #'s ConcurrentDictionary. So, if it doesn't exist, it will make a new one and return it, otherwise it will return the existing one. The best!

SoundFactory.java

package flyweight.server;

import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class SoundFactory {
    private static ConcurrentMap<String, Sound> sounds = new ConcurrentHashMap<String, Sound>();
    public static Sound getSound(String note) {
        return sounds.computeIfAbsent(note, n -> new SoundImpl(n));
    }
}

Overall picture

FlyweightController.java

package flyweight.server;

import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;

@Controller("/flyweight")
public class FlyweightController {
    @Get(value = "/{string}/{fret}", produces = MediaType.TEXT_PLAIN)
    public String index(Integer string, Integer fret){
        Integer openNote = Constants.stringMap.get(string.intValue());
        int note = openNote.intValue() + fret.intValue();
        if (note >= 12) {
            note = note - 12;
        }
        Sound sound = SoundFactory.getSound(Constants.notes.get(note));
        return "String: " + string + " Fret: "+ fret + " Sound: " + sound.Play();
    }
}

Constants.java

package flyweight.server;

import java.util.HashMap;

public class Constants {
    static HashMap<Integer, Integer> stringMap = new HashMap<Integer, Integer>();
    static HashMap<Integer, String> notes = new HashMap<Integer, String>();
    static void setup() {
        stringMap.put(1, 4);
        stringMap.put(2, 11);
        stringMap.put(3, 7);
        stringMap.put(4, 2);
        stringMap.put(5, 9);
        stringMap.put(6, 4);

        notes.put(0, "C");
        notes.put(1, "C#");
        notes.put(2, "D");
        notes.put(3, "D#");
        notes.put(4, "E");
        notes.put(5, "F");
        notes.put(6, "F#");
        notes.put(7, "G");
        notes.put(8, "G#");
        notes.put(9, "A");
        notes.put(10, "A#");
        notes.put(11, "B");

    }
}

Sound.java

package flyweight.server;

public interface Sound {
    String Play();
}

SoundImpl.java

package flyweight.server;

public class SoundImpl implements Sound {
    private String note;

    public SoundImpl(String note) {
        this.note = note;
    }

    public String Play() {
        return this.note + "- note";
    }

    public String getNote() {
        return this.note;
    }
}

Application.java

package flyweight.server;

import io.micronaut.runtime.Micronaut;

public class Application {

    public static void main(String[] args) {
        Constants.setup();
        Micronaut.run(Application.class, args);
    }
}

Execution result

[INFO] Replacing original artifact with shaded artifact.
[INFO] Replacing C:\Users\tsushi\Code\java\spike\micronaut\flyweight-server\target\flyweight-server-0.1.jar with C:\Users\tsushi\Code\java\spike\micronaut\flyweight-server\target\flyweight-server-0.1-shaded.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  7.444 s
[INFO] Finished at: 2020-10-26T14:16:48-07:00
[INFO] ------------------------------------------------------------------------
PS C:\Users\tsushi\Code\java\spike\micronaut\flyweight-server> java -jar .\target\flyweight-server-0.1.jar
←[36m14:17:24.022←[0;39m ←[1;30m[main]←[0;39m ←[34mINFO ←[0;39m ←[35mio.micronaut.runtime.Micronaut←[0;39m - Startup completed in 1154ms. Server Running: http://localhost:8080

image.png

I'm glad I learned the Flyweight pattern and the ConcurrentHashMap lightly today.

Resource

Recommended Posts

Learn Flyweight patterns and ConcurrentHashMap in Java
Encoding and Decoding example in Java
How to learn JAVA in 7 days
StringBuffer and StringBuilder Class in Java
Understanding equals and hashCode in Java
Hello world in Java and Gradle
Java Dekon'nichiwa world (Tsu learn quickly grammar in Vim and Quickrun)
Difference between final and Immutable in Java
[Java] for Each and sorted in Lambda
Program PDF headers and footers in Java
Java Direction in C ++ Design and Evolution
Java to C and C to Java in Android Studio
Reading and writing gzip files in Java
Difference between int and Integer in Java
Discrimination of Enums in Java 7 and above
Regarding the transient modifier and serialization in Java
Create barcodes and QR codes in Java PDF
Detect similar videos in Java and OpenCV rev.2
Partization in Java
Difference between next () and nextLine () in Java Scanner
Changes in Java 11
Rock-paper-scissors in Java
Java Design Patterns
Detect similar videos in Java and OpenCV rev.3
Learn more about collections and members in routes.rb
Add, read, and delete Excel comments in Java
Check static and public behavior in Java methods
[Java] Understand in 10 minutes! Associative array and HashMap
Java and JavaScript
XXE and Java
Distinguish between positive and negative numbers in Java
Java adds and removes watermarks in word documents
Detect similar videos in Java and OpenCV rev.1
Pi in Java
Represents "next day" and "previous day" in Java / Android
Questions in java exception handling throw and try-catch
Upload and download notes in java on S3
Encrypt / decrypt with AES256 in PHP and Java
Generate OffsetDateTime from Clock and LocalDateTime in Java
FizzBuzz in Java
Convert JSON and YAML in Java (using Jackson and SnakeYAML)
I tried Mastodon's Toot and Streaming API in Java
Write ABNF in Java and pass the email address
Vectorize and image MNIST handwritten digit image data in Java
[Java] Difference between static final and final in member variables
Learn for the first time java # 3 expressions and operators
Java classes and instances to understand in the figure
Java joins and splitting table cells in Word documents
This and that for editing ini in Java. : inieditor-java
How to convert A to a and a to A using AND and OR in Java
Reverse Enum constants from strings and values in Java
~ I tried to learn functional programming in Java now ~
Gzip-compress byte array in Java and output to file
[java] sort in list
Read JSON in Java
Interpreter implementation in Java
Rock-paper-scissors app in Java
Constraint programming in Java
Put java8 in centos7
NVL-ish guy in Java
Combine arrays in Java