<blockquote>
<p>Please help me pair my app with LoadingCache.</p>
</blockquote>
<p>Merry X-Mas! There you go:</p>
<ol>
<li><code>LoadingCache&lt;Key, Graph&gt;</code> applied to our code must be <code>LoadingCache&lt;String[],Map&lt;Character, Integer&gt;&gt;</code> matching the input and output types of our ...</li>
<li><code>createExpensiveGraph</code> method applied to our case would be <code>charCounter</code>.</li>
<li>To pair up, we would not invoke <code>charCounter(...)</code> directly, but through a (given) cache instance, so: <code>graphs.get(...)</code>.</li>
</ol>
<hr />
<p>I refactored &quot;little&quot; (simplified <code>String[]</code> to <code>String</code>, removed &quot;half&quot; of your classes, made the main method interactive), this is what came  out:</p>
<hr />
<p>pom.xml:</p>
<pre class="lang-xml prettyprint-override"><code>&lt;project&gt;
    &lt;!-- only defaults ..., and: --&gt;
    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;com.google.guava&lt;/groupId&gt;
            &lt;artifactId&gt;guava&lt;/artifactId&gt;
            &lt;version&gt;31.0.1-jre&lt;/version&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;
    &lt;properties&gt;
        &lt;maven.compiler.source&gt;17&lt;/maven.compiler.source&gt;
        &lt;maven.compiler.target&gt;17&lt;/maven.compiler.target&gt;
    &lt;/properties&gt;
&lt;/project&gt;
</code></pre>
<hr />
<p>Main.java</p>
<pre class="lang-java prettyprint-override"><code>package com.stackoverflow.cache.test;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import java.util.LinkedHashMap;
import java.util.Scanner;
import java.util.concurrent.ExecutionException;

public class Main {
  
  static final LoadingCache&lt;String, LinkedHashMap&lt;Character, Integer&gt;&gt; CACHE = CacheBuilder.newBuilder()
      .build( // with defaults, and this loader:
          new CacheLoader&lt;String, LinkedHashMap&lt;Character, Integer&gt;&gt;() {
        @Override
        public LinkedHashMap&lt;Character, Integer&gt; load(String key) {
          System.out.format(&quot;Key: \&quot;%s\&quot; not cached.%n&quot;, key);
          return analyze(key); // invoking &quot;expensive calculation&quot;! 
        }
      });

  public static void main(String[] args) throws ExecutionException {
    try ( Scanner consoleScanner = new Scanner(System.in)) {
      String word = consoleScanner.nextLine().trim(); // line wise! (for word-wise: next())
      while (!&quot;bye&quot;.equalsIgnoreCase(word)) {// from Yoda, greetings! https://blog.codinghorror.com/new-programming-jargon/#1 ;)
        System.out.println(CACHE.get(word));// invoke cache, instead of &quot;expensive&quot; calculation!
        word = consoleScanner.nextLine().trim(); // line wise! (for word-wise: next())
      }
      System.out.println(&quot;Bye!&quot;);
    }
  }

  // basically your charCounter method with single parameter + stream:
  static LinkedHashMap&lt;Character, Integer&gt; analyze(String arg) {
    LinkedHashMap&lt;Character, Integer&gt; elements = new LinkedHashMap();
    arg.chars().forEach((num) -&gt; {
      Character c = (char) num;
      if (elements.containsKey(c)) {
        elements.put(c, elements.get(c) + 1);
      } else {
        elements.put(c, 1);
      }
    });
    return elements;
  }
}

</code></pre>
<hr />
<p>In- and Output:</p>
<pre class="lang-text prettyprint-override"><code>&gt;Java is the best programming language in the world!
Key: &quot;Java is the best programming language in the world!&quot; not cached.
{J=1, a=5, v=1,  =8, i=3, s=2, t=3, h=2, e=4, b=1, p=1, r=3, o=2, g=4, m=2, n=3, l=2, u=1, w=1, d=1, !=1}
&gt;hello
Key: &quot;hello&quot; not cached.
{h=1, e=1, l=2, o=1}
&gt;hello
{h=1, e=1, l=2, o=1}
&gt;bye
Bye!
</code></pre>
<p>(2nd calculation of &quot;hello&quot; is cached.)</p>
<hr />
<p>We see: Once identifying/understanding/defining</p>
<ul>
<li>&quot;key&quot;</li>
<li>&quot;graph&quot; and</li>
<li>&quot;expensive graph operation&quot;</li>
</ul>
<p>, it is easy to a apply a (guava) cache to a given &quot;operation&quot;.</p>
<p>For advanced cache configuration, please refer to <a href="https://javadoc.io/doc/com.google.guava/guava/latest/com/google/common/cache/CacheBuilder.html" rel="nofollow noreferrer">CacheBuilder javadoc</a>, for advanced usage to <a href="https://guava.dev/releases/31.0-jre/api/docs/com/google/common/cache/LoadingCache.html" rel="nofollow noreferrer">LoadingCache javadoc</a>.</p>
<p>Rather advanced and theoretical but very related to this topic/ use case: <a href="https://arxiv.org/abs/1912.03888" rel="nofollow noreferrer">Similarity Caching</a>.</p>
<hr />
<p>To receive &quot;words&quot; from command line arguments, we can use a <code>main()</code> method like this:</p>
<pre><code>public static void main(String[] args) throws ExecutionException {
  for (String word : args) {
    System.out.println(CACHE.get(word)); // ...or custom &quot;print&quot; method
  }
}
</code></pre>
<hr />
<p>To make it completely &quot;without external libs&quot; (i.e. guava)we would (<em>remove/clean up</em> that dependencies,) we would then use it, as outlined in (accepted answer of) <a href="https://stackoverflow.com/q/224868/592355">Easy, simple to use LRU cache in java</a> :</p>
<pre class="lang-java prettyprint-override"><code>// limited version:
static final int MAX_ENTRIES = 100;
static final Map&lt;String, Map&lt;Character, Integer&gt;&gt; CACHE = new LinkedHashMap&lt;&gt;(
  MAX_ENTRIES + 1, // initial capacity
  1.0f, // load factor: better 1. than 0.75 (in this setup!?)
  true // &quot;accessOrder&quot; flag
) {
    // eviction: &quot;...This method is invoked by put and putAll after inserting a new entry into the map&quot;
    public boolean removeEldestEntry(Map.Entry eldest) {
        return size() &gt; MAX_ENTRIES;
    }
};
</code></pre>
<p>for &quot;unlimited&quot; cache (might be sufficient for tutor;), just:</p>
<pre class="lang-java prettyprint-override"><code>// no limit, no &quot;order&quot;, no evict, no outdate:
static final Map&lt;String, Map&lt;Character, Integer&gt;&gt; CACHE = new HashMap&lt;&gt;();
</code></pre>
<p>(For a <em>thread-safe</em> version, we'd have to:)</p>
<pre class="lang-java prettyprint-override"><code> ... CACHE = Collections.synchronizedMap(new xxxMap...);
</code></pre>
<p><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/LinkedHashMap.html" rel="nofollow noreferrer">LinkedHashMap javadoc 17</a></p>
<p>We could wrap our &quot;cache loading&quot; then like:</p>
<pre class="lang-java prettyprint-override"><code>static Map&lt;Character, Integer&gt; load(String key) {
  final Map&lt;Character, Integer&gt; result; 
  if (CACHE.containsKey(key)) { // cached!
    result = CACHE.get(key);
    // to &quot;refresh&quot; key, put it again (LRU):
    // CACHE.put(key, result); // here or outside the if-else
  } else { // &quot;expensive&quot; calculation:
    result = analyze(key); // ... and add to cache (unlimited, or removingEldest(imlicitely!!)):
    CACHE.put(key, result);
  }
  return result;
}
</code></pre>
<p>and main method like:</p>
<pre><code>public static void main(String[] args) {
  for (String word : args) {
    System.out.println(load(word));
  }
}
</code></pre>
<p>;)#</p>
Java method should cache the results
<p>I'm learning programming in the language java.</p>
<p>I need to write an application that takes a string and returns the number of unique characters in the string.
It is expected that a string with the same character sequence may be passed several times to the method.
Since the counting operation can be time-consuming, the method should cache the results, so that when the method is given a string previously encountered</p>
<p>At this stage, my application is already able to count and display characters</p>
<pre><code>public class Main {
    public static void main(String[] args) {
        String[] testArray = new String[]{&quot;Java&quot;, &quot;is&quot;, &quot;the&quot;, &quot;best&quot;, &quot;programming&quot;,
                &quot;language&quot;, &quot;in&quot;, &quot;the&quot;, &quot;world!&quot;};
        CharCounter charCounter = new CharCounter();
        Print print = new Print();
        print.printArgs(testArray);
        print.print(charCounter.charCounter(testArray));
    }
}

/**
 *  CharCounter should takes a string and returns the number of unique
 *  characters in the string.
 */
public class CharCounter {
    public LinkedHashMap&lt;Character, Integer&gt; charCounter(String[] args) {
        LinkedHashMap&lt;Character, Integer&gt; elements = new LinkedHashMap();
        List&lt;Character&gt; chars = new ArrayList();
        for (char c : stringToCharArray(args)) {
            chars.add(c);
        }
        for (Character element : chars) {
            if (elements.containsKey(element)) {
                elements.put(element, elements.get(element) + 1);
            } else {
                elements.put(element, 1);
            }
        }
        return elements;
    }

    /**
     * stringToCharArray method - convert string array to character array     *
     */
    private char[] stringToCharArray(String[] args) {
        String s = &quot;&quot;;
        for (String agr : args) {
            if (s == &quot;&quot;) {
                s = agr;
            } else {
                s = s + &quot; &quot; + agr;
            }
        }
        return s.toCharArray();
    }
}

/**
 * The Print class is intended to output the result to the console
 */
public class Print {
    public void print(Map map) {
        Iterator&lt;Map.Entry&lt;Character, Integer&gt;&gt; iterator
                = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry&lt;Character, Integer&gt; charCounterEntry = iterator.next();
            System.out.printf(&quot;\&quot;%c\&quot; - %d\n&quot;, charCounterEntry.getKey(),
                    charCounterEntry.getValue());
        }
    }

    public void printArgs(String[] args) {
        for (String arg : args) {
            System.out.printf(&quot;%s &quot;, arg);
        }
        System.out.println();
    }
}
</code></pre>
<p>The result of the application</p>
<pre><code>Java is the best programming language in the world! 
&quot;J&quot; - 1
&quot;a&quot; - 5
&quot;v&quot; - 1
&quot; &quot; - 8
&quot;i&quot; - 3
&quot;s&quot; - 2
&quot;t&quot; - 3
&quot;h&quot; - 2
&quot;e&quot; - 4
&quot;b&quot; - 1
&quot;p&quot; - 1
&quot;r&quot; - 3
&quot;o&quot; - 2
&quot;g&quot; - 4
&quot;m&quot; - 2
&quot;n&quot; - 3
&quot;l&quot; - 2
&quot;u&quot; - 1
&quot;w&quot; - 1
&quot;d&quot; - 1
&quot;!&quot; - 1
</code></pre>
<p>Now I need to teach my application to cache and check the input data for an already existing result.</p>
<p>I think LoadingCache from Guava will help me</p>
<pre><code>LoadingCache&lt;Key, Graph&gt; graphs = CacheBuilder.newBuilder()
   .maximumSize(1000)
   .expireAfterWrite(10, TimeUnit.MINUTES)
   .removalListener(MY_LISTENER)
   .build(
       new CacheLoader&lt;Key, Graph&gt;() {
         @Override
         public Graph load(Key key) throws AnyException {
           return createExpensiveGraph(key);
         }
       });
</code></pre>
<p>Please help me pair my app with LoadingCache.</p>
<p>To all who will respond, thanks a lot!</p>