どのデータ構造を使用しますか:TreeMapまたはHashMap? (Java)
-
08-07-2019 - |
質問
説明| テキストファイルを読み取り、テキスト内の単語の出現回数とともにアルファベット順で各一意の単語を印刷するJavaプログラム。
プログラムは、Map<String, Integer>
型の変数を宣言して、単語と対応する出現頻度を保存する必要があります。しかし、どの具体的なタイプですか? TreeMap<String, Number>
またはHashMap<String, Number>
?
入力は小文字に変換する必要があります。
単語には次の文字は含まれていません:\t\t\n]f.,!?:;\"()'
出力例|
Word Frequency
a 1
and 5
appearances 1
as 1
.
.
.
備考| 知っている、約2行のコードでPerlのエレガントなソリューションを見てきました。ただし、Javaで見たいです。
編集:そうそう、これらの構造の1つを使用した実装を(Javaで)示すと役立ちます。
解決
TreeMap は簡単です私にとって-単に<!> quot;アルファベット順<!> quot;要件。 HashMapを反復処理するとき、順序はありません。 TreeMapは、自然なキーの順序で反復します。
編集:Konradのコメントは、<!> quot; HashMapを使用してからソートすることを提案していた可能性があると思います。<!> quot;最初はN回の反復がありますが、重複によりK <!> lt; = N個のキーが終了するため、これは良いことです。同様に、キーが少なくなったときに最後まで、高価なビット(ソート)を保存することもできます。
それを言って、私は今のところ私の答えに固執しています:それは目標を達成するための最も簡単な方法だからです。 OPがパフォーマンスを特に心配していることはあまりわかりませんが、質問は彼が優雅さと簡潔さを心配していることを意味します。 TreeMapを使用すると、これが非常に簡単になります。パフォーマンスが本当に問題である場合、TreeMapまたはHashMapのいずれかよりもそれを攻撃するより良い方法があるかもしれないと思う:)
他のヒント
TreeMapはすでにソートされているため、TreeMapはHashMapに勝ります。
ただし、より適切なデータ構造であるバッグの使用を検討することもできます。見る Commonsコレクション-および TreeBag クラス:
これには、最適化された内部構造とAPIが最適化されています:
bag.add("big")
bag.add("small")
bag.add("big")
int count = bag.getCount("big")
編集:HashMapとTreeMapのパフォーマンスの問題はJonによって回答されました-HashMapとソートはより高速かもしれません(試してみてください!)が、TreeBagの方が簡単です。バッグにも同じことが言えます。 HashBagとTreeBagがあります。実装に基づいて(可変整数を使用)、バッグはIntegerの同等のプレーンマップよりも優れている必要があります。確実に知る唯一の方法は、パフォーマンスに関する質問と同様にテストすることです。
<!> quot; TreeMapのルックアップにはO(n log n)
<!> quot; !!どうして?
どのように実装されているのかわかりませんが、私の頭の中ではO(log n)
が必要です。
これは、ツリー内の検索をO(n + k log k)
で実行できるためです。アイテムを挿入するたびにツリー全体をソートするわけではありません。これがツリーを使用するという考え方です!
したがって、元の質問に戻ると、比較用の数字は次のようになります。
HashMapアプローチ: O(k + n log k)
平均ケース、最悪ケースははるかに大きくなる可能性があります
TreeMapアプローチ: <=>最悪の場合
n =テキスト内の単語数、k =テキスト内の個別の単語数。
ハッシュマップはずっと速くなるはずです。最終的にアイテムの配置方法に基づいてコンテナを選択しないでください。最後に(単語、頻度)ペアのリストを並べ替えるだけです。通常、ファイル内の単語よりもソートされるペアの数が少ないため、ハッシュマップを使用した漸近(および実際の)パフォーマンスが向上します。
TreeMap<String,Number>
型の変数にMap<String,Integer>
を割り当てることはできません。 Double
、Long
などは<!> quot; put <!> quot; Integer
に。私が<!> quot; get <!> quot; <=>の値、<=>でなければなりません。
国際化の問題、メモリの制約、エラー処理を完全に無視します。
class Counter {
public static void main(String... argv)
throws Exception
{
FileChannel fc = new FileInputStream(argv[0]).getChannel();
ByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
CharBuffer cb = Charset.defaultCharset().decode(bb);
Pattern p = Pattern.compile("[^ \t\r\n\f.,!?:;\"()']+");
Map<String, Integer> counts = new TreeMap<String, Integer>();
Matcher m = p.matcher(cb);
while (m.find()) {
String word = m.group();
Integer count = counts.get(word);
count = (count == null) ? 1 : count + 1;
counts.put(word, count);
}
fc.close();
for (Map.Entry<String, Integer> e : counts.entrySet()) {
System.out.printf("%s: %d%n", e.getKey(), e.getValue());
}
}
}
<!> quot;キーが既に存在する場合、HashMapと同じパフォーマンスを持ちます。<!> quot; -それは単に間違っています。 HashMapにはO(1)挿入とTreeMap O(n log n)があります。テーブルにあるかどうかを確認するには、少なくともn log n回のチェックが必要です!
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream.GetField;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TreeMap;
public class TreeMapExample {
public static void main (String args[]){
Map<String,Integer> tm = new TreeMap<String,Integer>();
try {
FileInputStream fis = new FileInputStream("Test.txt");
DataInputStream in = new DataInputStream(fis);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String line;
int countValue = 1;
while((line = br.readLine())!= null ){
line = line.replaceAll("[-+.^:;,()\"\\[\\]]","");
StringTokenizer st = new StringTokenizer(line, " ");
while(st.hasMoreTokens()){
String nextElement = (String) st.nextElement();
if(tm.size()>0 && tm.containsKey(nextElement)){
int val = 0;
if(tm.get(nextElement)!= null){
val = (Integer) tm.get(nextElement);
val = val+1;
}
tm.put(nextElement, val);
}else{
tm.put(nextElement, 1);
}
}
}
for(Map.Entry<String,Integer> entry : tm.entrySet()) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
この方法では、私の意見では、 HashBag の Apache Commonsコレクションまたは HashMultiset Guava または HashBag Eclipseコレクション(以前の GSコレクション)または次のクラス:
Order | Guava | Apache | Eclipse(GS) | JDK analog
─────────────┼──────────────────┼───────────┼─────────────┼─────────────
Not define | HashMultiset | HashBag | HashBag | HashMap<String, Integer>
─────────────┼──────────────────┼───────────┼─────────────┼─────────────
Sorted | TreeMultiset | TreeBag | TreeBag | TreeMap<String, Integer>
─────────────┼──────────────────┼───────────┼─────────────┼─────────────
Linked |LinkedHashMultiset| - | - | LinkedHashMap<String, Integere>
─────────────┼──────────────────┼───────────┼─────────────┼─────────────
Concurrent & | ConcurrentHash- |Synchroniz-|Synchroniz- | Collections.synchronizedMap(
not define | Multiset | edBag | edBag | HashMap<String, Integer>)
─────────────┼──────────────────┼───────────┼─────────────┼─────────────
Concurrent | - |Synchroniz-|Synchroniz- | Collections.synchronizedSorted-
and sorted | |edSortedBag| edSortedBag | Map(TreeMap<>))
─────────────┼──────────────────┼───────────┼─────────────┼─────────────
Immutable and| ImmutableMultiset|Unmodifiab-|Unmodifiab- | Collections.unmodifiableMap(
not define | | leBag | leBag | HashMap<String, Integer>)
─────────────┼──────────────────┼───────────┼─────────────┼─────────────
Immutable and| ImmutableSorted- |Unmodifiab-|Unmodifiab- | Collections.unmodifiableSorted-
sorted | Multiset |leSortedBag| leSortedBag | Map(TreeMap<String, Integer>))
────────────────────────────────────────────────────────────────────────
例:
1。 使用ApacheのSynchronizedSortedBag :
// Parse text to separate words
String INPUT_TEXT = "Hello World! Hello All! Hi World!";
// Create Multiset
Bag bag = SynchronizedSortedBag.synchronizedBag(new TreeBag(Arrays.asList(INPUT_TEXT.split(" "))));
// Print count words
System.out.println(bag); // print [1:All!,2:Hello,1:Hi,2:World!]- in natural (alphabet) order
// Print all unique words
System.out.println(bag.uniqueSet()); // print [All!, Hello, Hi, World!]- in natural (alphabet) order
// Print count occurrences of words
System.out.println("Hello = " + bag.getCount("Hello")); // print 2
System.out.println("World = " + bag.getCount("World!")); // print 2
System.out.println("All = " + bag.getCount("All!")); // print 1
System.out.println("Hi = " + bag.getCount("Hi")); // print 1
System.out.println("Empty = " + bag.getCount("Empty")); // print 0
// Print count all words
System.out.println(bag.size()); //print 6
// Print count unique words
System.out.println(bag.uniqueSet().size()); //print 4
2。 Eclipse(GC)からTreeBagを使用する:
// Parse text to separate words
String INPUT_TEXT = "Hello World! Hello All! Hi World!";
// Create Multiset
MutableSortedBag<String> bag = TreeBag.newBag(Arrays.asList(INPUT_TEXT.split(" ")));
// Print count words
System.out.println(bag); // print [All!, Hello, Hello, Hi, World!, World!]- in natural order
// Print all unique words
System.out.println(bag.toSortedSet()); // print [All!, Hello, Hi, World!]- in natural order
// Print count occurrences of words
System.out.println("Hello = " + bag.occurrencesOf("Hello")); // print 2
System.out.println("World = " + bag.occurrencesOf("World!")); // print 2
System.out.println("All = " + bag.occurrencesOf("All!")); // print 1
System.out.println("Hi = " + bag.occurrencesOf("Hi")); // print 1
System.out.println("Empty = " + bag.occurrencesOf("Empty")); // print 0
// Print count all words
System.out.println(bag.size()); //print 6
// Print count unique words
System.out.println(bag.toSet().size()); //print 4
3。 使用グアバからのLinkedHashMultiset :
// Parse text to separate words
String INPUT_TEXT = "Hello World! Hello All! Hi World!";
// Create Multiset
Multiset<String> multiset = LinkedHashMultiset.create(Arrays.asList(INPUT_TEXT.split(" ")));
// Print count words
System.out.println(multiset); // print [Hello x 2, World! x 2, All!, Hi]- in predictable iteration order
// Print all unique words
System.out.println(multiset.elementSet()); // print [Hello, World!, All!, Hi] - in predictable iteration order
// Print count occurrences of words
System.out.println("Hello = " + multiset.count("Hello")); // print 2
System.out.println("World = " + multiset.count("World!")); // print 2
System.out.println("All = " + multiset.count("All!")); // print 1
System.out.println("Hi = " + multiset.count("Hi")); // print 1
System.out.println("Empty = " + multiset.count("Empty")); // print 0
// Print count all words
System.out.println(multiset.size()); //print 6
// Print count unique words
System.out.println(multiset.elementSet().size()); //print 4
他の可能な例githubプロジェクトで見つける
必ずTreeMapを選択します:
- TreeMapは挿入時に新しいキーを自動的にソートします。その後のソートは不要です。
- キーがすでに存在する場合、HashMapと同じパフォーマンスがあります。
TreeSetは内部でTreeMapを使用するため、TreeMapを直接使用しないでください。
速度要件に応じて、 Trie を使用することもできます。ただし、TreeMapが十分に速い場合、それらのいずれかを実装しても意味がありません。
データ構造への追加または削除の頻度を考慮してください。 TreeMapは、高ければ理想的ではありません。既存のエントリnLnの検索とは別に、頻繁にリバランスが行われます。
一方、ハッシュ構造はメモリ上で少々派手です(過剰割り当て)。その弾丸を噛むことができれば、ハッシュ構造に進み、必要に応じてソートします。
これは、テキストファイルを読み取り、キーに基づいてソートし、次に値に基づいてソートするJavaの例です。ファイル内の単語の出現回数によって異なります。
public class SortFileWords {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<String, Integer>();
ValueCompare vc = new ValueCompare(map);
TreeMap<String, Integer> sorted_map = new TreeMap<String, Integer>(map);
List<String> list = new ArrayList<>();
Scanner sc;
try {
sc = new Scanner(new File("c:\\ReadMe1.txt"));
while (sc.hasNext()) {
list.add(sc.next());
}
sc.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
for (String s : list) {
if (map.containsKey(s)) {
map.put(s, map.get(s) + 1);
} else
map.put(s, 1);
}
System.out.println("Unsorted map: " + map);
sorted_map.putAll(map);
System.out.println("Sorted map on keys: " + sorted_map);
TreeMap<String, Integer> sorted_value_map = new TreeMap<>(vc);
sorted_value_map.putAll(map);
System.out.println("Sorted map on values: " + sorted_value_map);
}
}
class ValueCompare implements Comparator<String> {
Map<String, Integer> map;
public ValueCompare(Map<String, Integer> map) {
this.map = map;
}
@Override
public int compare(String s1, String s2) {
if (map.get(s1) >= map.get(s2))
return -1;
else
return 1;
}
}
TreeSet ?
TreeMapと同じ順序付けの概念ですが、Setである点を除き、定義は<!> quot;重複要素を含まないコレクション<!> quot;です。
問題の説明から、セットが必要なように聞こえますが、どのキーと値を一緒にマッピングしているかわかりません。
このクラスは、TreeMapインスタンスによってサポートされるSetインターフェースを実装します。このクラスは、使用されるコンストラクターに応じて、ソートされたセットが要素の自然順序(Comparableを参照)、またはセット作成時に提供されたコンパレーターに従ってソートされた昇順のエレメント順序になることを保証します。
基本的には要件に依存します。時にはハッシュマップが良いこともあれば、ツリーマップが良いこともあります。ただし、ハッシュマップを使用する方が、ソートのオーバーヘッドの制約のみです。