我正在创建一个使用字典的拼字游戏。为了提高效率,而不是将整个字典(通过 txt 文件)加载到数据结构(集合、列表等),而是有任何内置的 java 类可以帮助我将文件的内容视为字符串。
具体来说,我想做的是通过做一些简单的事情来检查游戏中制作的单词是否是字典中的有效单词,例如 fileName.contains (word),而不是使用内存效率低下的巨大列表并使用 list.contains (word)。
你们知道我能做什么吗?如果字典文件必须位于 txt 文件(例如.xml文件)以外的其他文件中,我也愿意尝试。
注意:我不是在寻找 http://commons.apache.org/io/api-1.4/org/apache/commons/io/FileUtils.html#readFileToString%28java.io.File%29
此方法不是 java API 的一部分。
没有想到 HashSet,我陷入了所有包含 () 方法都使用 O(n) 时间的想法,感谢 Bozho 与我一起清除它,看起来我将使用 HashSet。
我认为您最好的选择是将它们全部加载到内存中,HashSet
.contains(word)
是O(1)。
如果您愿意将其保存在内存中,则将其作为调用contains(..)
的String
效率远低于HashSet
。
我不得不提到另一种选择 - 有一个数据结构来表示字典 - 它被称为 Trie
.但是,您无法在JDK中找到实现。
一个非常粗略的计算表明,对于所有英语单词(100万),您将需要~12兆字节的RAM。 这比JVM的默认内存设置少几倍。(100 万 * 平均 6 个字母 * 每个字母 2 个字节 = 12 百万字节,即 ~12 兆字节)。(好吧,也许存储哈希值更多)
如果您真的坚持不在内存中读取它,并且想扫描文件中的给定单词,那么您可以使用java.util.Scanner
及其scanner.findWithHorizon(..)
.但这将是低效的 - 我假设 O(n) 和 I/O 开销。
虽然HashSet可能是一个完全可以接受的解决方案(参见Bozho的答案),但还有其他数据结构可以使用,包括Trie或Heap。
Trie 的优点是,根据实现细节,可以共享起始前缀字母(毕竟,trie 也称为"前缀树")。根据实施结构和数据,这实际上可能是一种改进,也可能不是一种改进。
另一种选择,特别是如果需要基于文件的访问,是使用堆 - Java的PriorityQueue实际上是一个堆,但它不是基于文件的,所以这需要查找/进行实现。
所有这些数据结构(以及更多)都可以实现为基于文件的(每次查找使用更多的IO - 实际上可能总体上可能更少 - 但节省内存)或直接实现(例如使用SQLite并让它做它的B树的事情)。SQLite的优势在于它可以是一个"常用工具"(曾经常用过;-)在工具箱中;数据导入、检查和修改很容易,而且"它只是工作"。SQLite甚至用于功能较弱的系统,如Android。
HashSet随Java一起"免费"提供,但没有标准的Trie或基于文件的Heap实现。我会从一个哈希集开始 - 推理:
- 字典 = 5MB。
- 加载到 HashSet 中(假设开销很大)= 20MB。
- 与其他事物相关的内存使用量 = 最小(假设笔记本电脑/台式机)
- 使用 HashSet 实现的时间 = 2 分钟。
- 如果我决定哈希集不够好,我只会"失去"2 分钟:-)
快乐编码。
指向随机数据结构实现的链接(可能合适,也可能不合适):
- TernarySearchTrie 在平面文件中读取(必须专门构造?)
- TrieTree 支持从平面文件创建 Trie 文件。不确定此 Trie 是否在磁盘上工作。
- 使用文件备份的文件哈希哈希。 哈希
- 存储 另一个基于磁盘的哈希 WB B 树
- 简单 B 树实现/"数据库"
- SQLite Small embedded RDBMS.
- UTF8String 可用于显著降低使用拉丁词典时使用
HashSet<String>
的内存要求。(Java 中的字符串使用 UTF-16 编码,至少为两个字节/字符。
您需要压缩数据以避免存储所有这些单词。这样做的方法将是一棵树,其中节点是字母,叶子反映单词的结尾。这样,您就不会存储重复的数据,例如这些单词都具有相同前缀的the there these
。
有一种方法可以使此解决方案更加节省内存。(提示:字母顺序)
使用 java.io.BufferedReader 的 readline()。这将返回一个字符串。
String line = new BufferedReader (new FileReader (file) ).readline ();