Java方法应该缓存结果

标签 java caching

我正在学习 java 语言编程。

我需要编写一个应用程序,它接受一个字符串并返回该字符串中唯一字符的数量。 预计具有相同字符序列的字符串可能会多次传递给该方法。 由于计数操作可能非常耗时,因此该方法应该缓存结果,以便当该方法给出一个以前遇到过的字符串时

现阶段,我的应用程序已经能够计算和显示字符

public class Main {
    public static void main(String[] args) {
        String[] testArray = new String[]{"Java", "is", "the", "best", "programming",
                "language", "in", "the", "world!"};
        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<Character, Integer> charCounter(String[] args) {
        LinkedHashMap<Character, Integer> elements = new LinkedHashMap();
        List<Character> 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 = "";
        for (String agr : args) {
            if (s == "") {
                s = agr;
            } else {
                s = s + " " + 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<Map.Entry<Character, Integer>> iterator
                = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<Character, Integer> charCounterEntry = iterator.next();
            System.out.printf("\"%c\" - %d\n", charCounterEntry.getKey(),
                    charCounterEntry.getValue());
        }
    }

    public void printArgs(String[] args) {
        for (String arg : args) {
            System.out.printf("%s ", arg);
        }
        System.out.println();
    }
}

申请结果

Java is the best programming language in the world! 
"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

现在我需要教我的应用程序缓存并检查输入数据是否已存在结果。

我认为 Guava 的 LoadingCache 会对我有所帮助

LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
   .maximumSize(1000)
   .expireAfterWrite(10, TimeUnit.MINUTES)
   .removalListener(MY_LISTENER)
   .build(
       new CacheLoader<Key, Graph>() {
         @Override
         public Graph load(Key key) throws AnyException {
           return createExpensiveGraph(key);
         }
       });

请帮助我将我的应用与 LoadingCache 配对。

对于所有愿意回复的人,非常感谢!

最佳答案

Please help me pair my app with LoadingCache.

圣诞快乐!就这样:

  1. 应用于我们代码的 LoadingCache<Key, Graph> 必须是 LoadingCache<String[],Map<Character, Integer>> 匹配我们的输入和输出类型...
  2. 应用于我们案例的 createExpensiveGraph 方法将是 charCounter
  3. 为了配对,我们不会直接调用 charCounter(...) ,而是通过(给定的)缓存实例调用,因此: graphs.get(...)

我重构了“little”(将 String[] 简化为 String ,删除了“一半”的类,使 main 方法具有交互性),结果如下:


pom.xml:

<project>
    <!-- only defaults ..., and: -->
    <dependencies>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>31.0.1-jre</version>
        </dependency>
    </dependencies>
    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>
</project>

Main.java

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<String, LinkedHashMap<Character, Integer>> CACHE = CacheBuilder.newBuilder()
      .build( // with defaults, and this loader:
          new CacheLoader<String, LinkedHashMap<Character, Integer>>() {
        @Override
        public LinkedHashMap<Character, Integer> load(String key) {
          System.out.format("Key: \"%s\" not cached.%n", key);
          return analyze(key); // invoking "expensive calculation"! 
        }
      });

  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 (!"bye".equalsIgnoreCase(word)) {// from Yoda, greetings! https://blog.codinghorror.com/new-programming-jargon/#1 ;)
        System.out.println(CACHE.get(word));// invoke cache, instead of "expensive" calculation!
        word = consoleScanner.nextLine().trim(); // line wise! (for word-wise: next())
      }
      System.out.println("Bye!");
    }
  }

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


输入和输出:

>Java is the best programming language in the world!
Key: "Java is the best programming language in the world!" 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}
>hello
Key: "hello" not cached.
{h=1, e=1, l=2, o=1}
>hello
{h=1, e=1, l=2, o=1}
>bye
Bye!

(“hello”的第二次计算被缓存。)


我们看到:一旦识别/理解/定义

  • “ key ”
  • “图表”和
  • “昂贵的图形操作”

,很容易将( Guava )缓存应用于给定的“操作”。

高级缓存配置请引用 CacheBuilder javadoc ,高级使用请引用 LoadingCache javadoc

相当高级和理论化,但与此主题/用例非常相关:Similarity Caching


要从命令行参数接收“单词”,我们可以使用 main() 方法,如下所示:

public static void main(String[] args) throws ExecutionException {
  for (String word : args) {
    System.out.println(CACHE.get(word)); // ...or custom "print" method
  }
}

为了使其完全“没有外部库”(即 Guava ),我们将(删除/清理该依赖项)然后使用它,如(已接受的答案)Easy, simple to use LRU cache in java中所述:

// limited version:
static final int MAX_ENTRIES = 100;
static final Map<String, Map<Character, Integer>> CACHE = new LinkedHashMap<>(
  MAX_ENTRIES + 1, // initial capacity
  1.0f, // load factor: better 1. than 0.75 (in this setup!?)
  true // "accessOrder" flag
) {
    // eviction: "...This method is invoked by put and putAll after inserting a new entry into the map"
    public boolean removeEldestEntry(Map.Entry eldest) {
        return size() > MAX_ENTRIES;
    }
};

对于“无限”缓存(对于导师来说可能就足够了;),只需:

// no limit, no "order", no evict, no outdate:
static final Map<String, Map<Character, Integer>> CACHE = new HashMap<>();

(对于线程安全版本,我们必须:)

 ... CACHE = Collections.synchronizedMap(new xxxMap...);

LinkedHashMap javadoc 17

我们可以包装我们的“缓存加载”,然后像:

static Map<Character, Integer> load(String key) {
  final Map<Character, Integer> result; 
  if (CACHE.containsKey(key)) { // cached!
    result = CACHE.get(key);
    // to "refresh" key, put it again (LRU):
    // CACHE.put(key, result); // here or outside the if-else
  } else { // "expensive" calculation:
    result = analyze(key); // ... and add to cache (unlimited, or removingEldest(imlicitely!!)):
    CACHE.put(key, result);
  }
  return result;
}

主要方法如下:

public static void main(String[] args) {
  for (String word : args) {
    System.out.println(load(word));
  }
}

;)#

关于Java方法应该缓存结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70478158/

相关文章:

java - 在失败之前自动重试所有测试 3 次

Java Swing 取消无限循环

java - 使用 Selenium 测试的 Vaadin 上传文件路径

ios - 下载多个图像以在 UI 上显示

java - @Cacheable 不拦截方法,缓存永远为空

caching - 如何缓存 Google map 图 block 以供离线使用?

caching - 在任何情况下,如何停止IIS缓存任何文件?

java - JUnit 测试杀死守护进程

java - 如何生成安全 URL

java - 安卓应用程序 : The Cache directory is empty but the device cache indicator shows that it is not empty