Beschreibung | Ein Java-Programm zum Lesen einer Textdatei und Drucken der einzelnen Wörter in alphabetischer Reihenfolge zusammen mit der Häufigkeit, mit der das Wort im Text vorkommt.
Das Programm sollte eine Variable vom Typ Map<String, Integer>
deklarieren, um die Wörter und die entsprechende Häufigkeit des Auftretens zu speichern. Welcher konkrete Typ aber? TreeMap<String, Number>
oder HashMap<String, Number>
?
Die Eingabe sollte in Kleinbuchstaben umgewandelt werden.
Ein Word enthält keines der folgenden Zeichen: \t\t\n]f.,!?:;\"()'
Beispielausgabe |
Word Frequency
a 1
and 5
appearances 1
as 1
.
.
.
Bemerkung | Ich weiß, ich habe in Perl elegante Lösungen mit ungefähr zwei Codezeilen gesehen. Ich möchte es jedoch in Java sehen.
Edit: Ach ja, es ist hilfreich, eine Implementierung mit einer dieser Strukturen (in Java) zu zeigen.
TreeMap scheint mir ein Kinderspiel zu sein - einfach wegen der Anforderung "in alphabetischer Reihenfolge". HashMap hat keine Reihenfolge, wenn Sie es durchlaufen. TreeMap iteriert in der natürlichen Schlüsselreihenfolge.
BEARBEITEN: Ich denke, Konrads Kommentar deutete darauf hin, dass man "HashMap verwenden und dann sortieren" sollte. Das ist gut, denn obwohl wir anfangs N Iterationen haben, haben wir am Ende K <= N Schlüssel aufgrund von Duplikaten. Wir könnten genauso gut das teure Stück (Sortieren) bis zum Ende speichern, wenn wir weniger Schlüssel haben, als den kleinen, aber nicht konstanten Treffer zu nehmen, es sortiert zu halten, während wir gehen.
Trotzdem halte ich im Moment an meiner Antwort fest: weil es der einfachste Weg ist, das Ziel zu erreichen. Wir wissen nicht wirklich, dass das OP besonders um die Leistung besorgt ist, aber die Frage impliziert, dass er sich um die Eleganz und Kürze sorgt. Die Verwendung einer TreeMap macht dies unglaublich kurz, was mich anspricht. Ich vermute, wenn die Leistung wirklich ein Problem darstellt, gibt es möglicherweise eine bessere Möglichkeit, sie anzugreifen als TreeMap oder HashMap :)
TreeMap schlägt HashMap, da TreeMap bereits für Sie sortiert ist.
Möglicherweise möchten Sie jedoch die Verwendung einer geeigneteren Datenstruktur, einer Tasche, in Betracht ziehen. Siehe Commons Collections - und die TreeBag class:
Dies hat eine Nice-optimierte interne Struktur und API:
bag.add("big")
bag.add("small")
bag.add("big")
int count = bag.getCount("big")
BEARBEITEN: Die Frage nach der Leistung von HashMap vs. TreeMap wurde von Jon - HashMap beantwortet und die Sortierung ist möglicherweise schneller (versuchen Sie es!), Aber TreeBag ist einfacher. Gleiches gilt für Taschen. Es gibt ein HashBag sowie ein TreeBag. Basierend auf der Implementierung (verwendet eine veränderliche Ganzzahl) sollte ein Beutel die entsprechende einfache Karte von Ganzzahl übertreffen. Der einzige Weg, um sicher zu wissen, ist wie bei jeder Leistungsfrage das Testen.
Ich sehe ziemlich viele Leute, die sagen, "TreeMap-Lookup braucht O(n log n)
" !! Woher?
Ich weiß nicht, wie es implementiert wurde, aber in meinem Kopf braucht es O(log n)
.
Dies liegt daran, dass die Suche in einem Baum in O(log n)
erfolgen kann. Sie sortieren nicht jedes Mal den gesamten Baum, wenn Sie ein Element einfügen. Das ist die ganze Idee eines Baumes!
Um auf die ursprüngliche Frage zurückzukommen, erweisen sich die Vergleichszahlen als:
HashMap-Ansatz: O(n + k log k)
Durchschnittsfall, der schlimmste Fall könnte viel größer sein
TreeMap-Ansatz: O(k + n log k)
Worst-Case
dabei steht n für die Anzahl der Wörter im Text und k für die Anzahl der verschiedenen Wörter im Text.
Hash Map sollte viel schneller sein. Sie sollten keinen Container auswählen, je nachdem, wie die Elemente schließlich angeordnet werden sollen. Sortieren Sie einfach die Liste der (Word-, Frequenz-) Paare am Ende. In der Regel müssen weniger solche Paare sortiert werden als Wörter in den Dateien. Daher ist die asymptotische (und reelle) Leistung mit einer Hash-Map besser.
"Wenn ein Schlüssel bereits vorhanden ist, hat er dieselbe Leistung wie eine HashMap." - Das ist einfach falsch. HashMap hat O(1) Einfügung und TreeMap O (n log n). Es wird mindestens n log n Überprüfungen benötigt, um herauszufinden, ob es in der Tabelle steht!
Sie können einer Variablen vom Typ TreeMap<String,Number>
keinen Map<String,Integer>
zuweisen. Double
, Long
usw. können in einen TreeMap<String,Number>
"eingefügt" werden. Wenn ich einen Wert aus einem Map<String,Integer>
"erhalte", muss es eine Integer
sein.
I18n-Probleme, Speicherbeschränkungen und Fehlerbehandlung werden vollständig ignoriert.
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());
}
}
}
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();
}
}
}
Verwenden Sie dazu meiner Meinung nach besser HashBag aus Apache Commons Collections oder HashMultiset aus Guava oder HashBag aus Eclipse Collections (formal) GS Collections ) oder eine der folgenden Klassen:
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>))
────────────────────────────────────────────────────────────────────────
Beispiele:
// 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
// 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
// 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
Ich würde definitiv eine TreeMap wählen:
Ein TreeSet verwendet intern eine TreeMap, also verwenden Sie TreeMap nicht direkt.
Abhängig von den Geschwindigkeitsanforderungen können Sie auch eine Trie verwenden. Es ist jedoch sinnlos, eine davon zu implementieren, wenn eine TreeMap schnell genug ist.
berücksichtigen Sie die Häufigkeit des Hinzufügens oder Löschens zur Datenstruktur. TreeMap wäre nicht ideal, wenn es hoch ist. Abgesehen von der Suche nach bestehenden Einträgen nLn wird auch häufig ein Rebalancing durchgeführt.
auf der anderen Seite sind Hash-Strukturen im Speicher etwas flamboyant (over allocates). Wenn Sie diese Kugel beißen können, wählen Sie die Hash-Struktur und sortieren Sie sie, wenn nötig.
Hier ist das Java-Beispiel zum Lesen einer Textdatei, zum Sortieren nach Schlüssel und dann nach Werten. abhängig von der Anzahl der Vorkommen eines Wortes in der Datei.
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;
}
}