如何在应用程序启动时将文本文件加载到HashMap?

时间:2013-09-06 14:20:43

标签: java

我正在开发一个小项目,一个Web应用程序,我根据用户文本输入对数据进行一些分析。

为此,我需要使用一张地图,其中我有单词和相应的分数。

这就是我的尝试:

public class EnDict {
    private static Map<String, Integer> map = new HashMap<String, Integer>() {
        {
            put("abandon", -2);
            put("ability", 2);
            put("abuse", -3);
            //and so on, thousands of pairs
        }
    }
}

它有效,但这样我需要在我的班级中对键/值对进行硬编码。所以,如果我想添加更多对,我必须编写代码,而不是只添加对执行文本文件。看起来不太好。

所以我想从文本文件中获取这些对。此外,我需要在应用程序启动时创建此Map,因此当任何用户发出请求时,Map已经加载,并且可以由分析逻辑使用。我的意思是,Map必须在第一个请求发生之前在内存中,并在之后的内存中,在后续请求中使用。它需要在我的应用程序的任何地方都可见(也许这部分不是很清楚,但我不知道如何更好地解释它。)

我已经尝试了一些研究,但是从应用程序启动以来,没有找到将Map保留在内存中的这一特定部分的答案。它类似于Application_Start类中的ASP.NET Global方法。

我非常喜欢编程,特别是Java,所以也许我完全被误导了如何才能成为完成这项任务的最佳方式。如果是这种情况,我们将不胜感激。

我正在使用Servlets,JSP和Tomcat。

编辑: 实际上,它不只是一张地图。将有几个地图,这些地图可以有一些相同的键。

6 个答案:

答案 0 :(得分:3)

将此地图定义为静态 - 它将在内存中,直到加载此类的类加载器不被垃圾回收。

我在上面提到:http://docs.oracle.com/javase/specs/jls/se5.0/html/execution.html#12.7

静态成员链接到类,并且上面的规范说明在类加载器到位之前不会卸载类。

而对象确实会收集垃圾。因此建议使地图静态(如果需要从外部访问,也将其公开)。

将文件加载到地图

将其存储在文件中

键1 =值 键2 =值 .... ....

现在使用BufferedReader,如下所示

BufferedReader reader = new BufferedReader(new FileReader(new File("pathname")));
        String line = null;
        Map<String, Integer> map = new HashMap<String, Integer>();// it should be static - whereever you define
        while ((line = reader.readLine()) != null) {
            if (line.contains("=")) {
                String[] strings = line.split("=");
                map.put(strings[0], Integer.parseInt(strings[1]));
            }
        }

类加载器是在启动应用程序时在内存中加载类的东西。 Tomcat还有它的类加载器,它在内存中加载所需的类(Classes而不是对象)。现在我们知道静态变量与类而不是对象相关联。因此,静态成员与类一起加载到内存中。在许多其他情况下,您将创建类的对象并使用它。如果你在内存中加载了数百万个对象 - 你很快就会缺少它。所以java有一个叫垃圾收集器的东西。此垃圾收集器从内存中删除不需要的/旧对象以回收它。垃圾收集器删除对象而不是类,因此静态成员仍然保留在内存中。

答案 1 :(得分:2)

您可以静态初始化静态块中的静态变量,如下所示:

private static Map<String, Integer> map = new HashMap<String,Integer>();

static {
    fillMap(map, "filename.txt");
}

private static void fillMap(Map<String, Integer> map, String fileName) {
   // here comes file reading code with loop
}

如何阅读文件,请参阅此Reading a plain text file in Java

只要在应用程序启动时初始化其所有静态映射。

答案 2 :(得分:1)

尝试将文本文件加载到应用程序中:Read from a Text File into a hash map or list

当我刚刚开始编程时,我知道我很想使用很多全局变量。事实证明,这通常不是最好的策略(见http://c2.com/cgi/wiki?GlobalVariablesAreBad)。

也许您可以在main方法中首先加载字典,然后将其传递给稍后需要的其他方法。

答案 3 :(得分:1)

您可以在web.xml文件中定义监听器:

<listener>
    <listener-class>my.Listener</listener-class>
</listener>

你实现了这个类:

package my;

public class Listener implements javax.servlet.ServletContextListener {

   public void contextInitialized(ServletContext context) {
       File file = new File();
       fileEntries = ... // load your entries
       for (Object[] line : fileEntries) {
           YourClass.get().addElement((String) line[0], Integer.parseInt(line[1].toString());
       }
   }
}

如果你想访问你的Map应用程序范围,只需创建一个单例或使用Spring来管理类,如果单例执行类似的操作:

public class YourClass {
     private static final YourClass INSTANCE = new YourClass();

     private Map<String, Integer> yourMap;

     private YourClass() {
         yourMap = new HashMap<>();
     }

     public static final YourClass get() {
         return INSTANCE;
     }

     public void addElement(String key, Integer value) {
         yourMap.put(key, value);
     }

     public Integer getValueForKey(String key) {
         return yourMap.get(key);
     }
}

因此您可以通过以下方式从应用程序的任何位置访问元素:

YourClass.get().getValueForKey("yourKey");

答案 4 :(得分:1)

我建议您使用Properties来存储/加载键/值对,并实现Singleton模式以访问这些属性。像这样:

public class EnDict {

    private Properties properties;
    private static EnDict enDictInstance;

    private EnDict {
        properties = new Properties();
        FileInsputStream fis = null;
        try{
            fis = new FileInputStream("yourPropertiesFile.properties");
            properties.load(fis);
            fis.close();
        } catch(IOException ex) {
            /* log the exception */
        } finally {
            try {
                fis.close();
            } catch (IOException ignored) {}
        }
     }

     public static EnDict getEnDictInstance(){
         if(enEdictInstance == null) {
             enEdictInstance = new EnEdict();
          }
          return enEdictInstance;
     }

     public Integer getValue(String key){
         String value = properties.getProperty(key);
         return Integer.valueOf(value);
     }

     public void setNewWord(String word, Integer value){
         properties.setProperty(word, value.toString());
     }

     public void saveProperties() {
         FileOutputStream fos = null;
         try {
             fos = new FileOutputStream("yourPropertiesFile.properties");
             properties.store(fos, "Some comments");
             fos.close();
         } catch (IOException ex) {
               /* log the exception */ 
         } finally {
             try{
                fos.close();
            } catch(IOException ignored){}
         }
     }
}

正如@Mauren指出的那样,请记住Properties不允许null值。 您也可以使用.properties文件代替XML个文件。见Loading Properties from XML

答案 5 :(得分:0)

要将来自不同资源的常量加载到不同的java类,可以使用apache commons配置库http://commons.apache.org/proper/commons-configuration/

对于应用程序启动,您可以使用

<servlet>
    <servlet-name>StartUp</servlet-name>
    <display-name>StartUp Servlet</display-name>
    <servlet-class>foo.bar.YourStartUpServlet</servlet-class>
    <load-on-startup>0</load-on-startup>
</servlet>