16

I would like to have a HashMap with only one key-value object.

I have created the following HashMap:

    HashMap <Integer,String>DocsCollection = new HashMap <Integer,String>();

In the HashMap I would like to have only one entry/object. The key type is an Integer. The value type is a String.

e.g. = <1,"foo.txt">

Every time I find a specific word in a file I would like to

  1. Increment the counter in key

  2. Add the new file in the value

e.g. Let's say that I'm searching for the word "Hello" in a DocsCollection, I have to store for every appearance of the word "Hello" the term frequency and concatenate the new file to the previous value.

<3,"foo.txt,hello.txt,test.txt">

3 means that I've found the word "Hello" in three files.

and the Value consists of the files where the word was found

If I use the method put, a new entry is created in the HashMap cause the key changes. It's not stable. It begins with "1" but when I find the word second time , the key increments and then the put method inserts a new entry with a new key But i would like to have only one entry and modify the key. Can this be done? How can i have only one object in a HashMap and modify the key every time?

   DocsCollection.put(2,"foo.txt,hello.txt"); 

Thanks, in advance

Rohan Kushwaha
  • 738
  • 6
  • 18
programmer
  • 4,571
  • 13
  • 49
  • 59

11 Answers11

40

Try this way:

DocsCollection = Collections.singletonMap(2, "foo.txt,hello.txt");

this Map can't be modified, if you want to to that just do this:

DocsCollection = Collections.singletonMap(3, "foo.txt,hello.txt");
Nikolay Nikiforchuk
  • 1,998
  • 24
  • 20
7

The Map approach might not be the best. The issue is you are changing your key value.

Note it might be better to just have a List<String>, and everytime you match the word, just add the file to the list. You can get the count easily with list.size()

hvgotcodes
  • 118,147
  • 33
  • 203
  • 236
  • Is the List going to be quick?Cause i'm having about 60 files and i need the work to be done quickly – programmer Jan 12 '12 at 20:13
  • After i have created the Lists for each word, i have to iterate through the lists and concatenate every file of the list for each word.Let's say that i have 10000 words and each of the word appears in 10 files that means that i'm going to create 10000 Lists of 10 entries each. After that i have to write to a random access file for each word the files every word appears in. – programmer Jan 12 '12 at 20:23
  • e.g. "Hello" 5 "file1,file2,file3,file4,file5" etc. In order to find the files for each word i have to iterate through the whole list.Isn't that going to be a bit slow? Isn't that better to have only a pair <5,"file1,file2,file3,file4,file5" – programmer Jan 12 '12 at 20:24
  • 5
    For 60 entries, you could copy the list thrice and do cartwheels each time you add an element and still spend so little time it would appear instantaneous to you. Ever heard that Knuth quote about optimization? Your bottleneck is whatever you do with those strings, not how you store them (if you want to find all files containing that word, you still have to check all files regardless of how you stored the files). Why do you think it could even begin to matter? –  Jan 12 '12 at 20:24
  • 1
    Actually, his bottleneck is almost certainly the IO and the grep for the Strings. But I like the suggestion of doing cartwheels, so +1. – user949300 Jan 12 '12 at 20:28
6

I'll try to suggest a bit different solution to what I think your task is:

  • you have words (hello, etc)
  • you want to count how much files it is found in
  • you want to know the files

You can use a MultiMap (guava) for that:

  • map.put("hello", "file1.txt"); map.put("hello", "file2.txt");
  • map.keys().count("hello") - gets you the number of times each word is found
  • map.get("hello") returns a Collection<String> containing all the files for that word

And you can have as many words in that map as you like. If you needed one-entry-per-map, you'd need X maps for X words.

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • 2
    this is a good solution if he is searching on more than one word. But for one word, a List or array will do... – hvgotcodes Jan 12 '12 at 20:10
2

Is there a reason you need to use a HashMap? You could just have an int (for the count) and a String or StringBuffer (for the filenames) and update them.

Alternatively, you could have a List into which you add the filename each time something is found. To get the count, use List.size(). But I see that @hvgotcodes already beat me to the punch with that idea.

user949300
  • 15,364
  • 7
  • 35
  • 66
2

You are not really using a HashMap so to speak : your counter is not really a key.

What you seem to need according to your explanation is an Object representing the result of your search, such as:

public class SearchResult {
     private String searchedWord;
     private long counter;
     private List<String> containingFiles;
     // ...
}
Jean Logeart
  • 52,687
  • 11
  • 83
  • 118
  • Well i need to store for each word the frequency and the files it appears in.Let's say that i have 10000 words, then i need to create 10000 classes. Isn't that going to cost me a lot in time? – programmer Jan 12 '12 at 20:16
  • 2
    No. What is going to cost you time is searching the words. Keep your program simple and elegant and you will certainly end up having better performances than using data structures that do no meet your initial needs. – Jean Logeart Jan 12 '12 at 20:24
1

Here's an idea for having a map with a single key/value: create the map, add the single key-value pair, and then make it unmodifiable using Collections.unmodifiableMap(). In that way, no other elements can be added to the map. Like this:

HashMap<Integer, String> docsCollection = new HashMap<Integer, String>();
docsCollection.put(2, "foo.txt,hello.txt");
docsCollection = Collections.unmodifiableMap(docsCollection);

This works only if you know the key/value beforehand; after the call to unmodifiableMap the map is effectively frozen and you won't be able to add/remove further elements from it.

Now, what you ask in the question is not suitable for using a map, that's not the correct data structure for using in this case. You'd be better off having an ArrayList<String>, adding to it the file names where the word was found, and using the list's size() method for determining the number of files where the word was found.

Óscar López
  • 232,561
  • 37
  • 312
  • 386
1

To ensure that what you want works:

  1. Declare the value to be a List<String>.
  2. Remove the original key/value pair and replace it with the new contents every time you found a word.

Something like:

HashMap<Integer, List<String>> map = new HashMap<Integer, List<String>>();
// some loop
if(/* new word found */) {
   Integer key = (Integer)map.keySet().toArray()[0];
   List<String> value = (List<String>)map.get(key);
   value.add(word);
   map.remove(key);
   map.put((key + 1), value);
}
Tudor
  • 61,523
  • 12
  • 102
  • 142
1

Not the good way to go.

Try instead a Map<String, Set<String>> where the key is the keyword and the value is the set of files you found the keyword into. Adding to it will then look like:

//further up
final Map<String, Set<String>> map = new HashMap<String, Set<String>>();

//then:
public void addRef(final String keyword, final String filename)
{
    if (!map.containsKey(keyword)) // keyword not encountered yet
        map.put(keyword, new HashSet<String>());

    map.get(keyword).add(filename);
}

Then you'll be able to collect information from this map when the need arises. In particular, to collect the number of files a keyword was found into, you would:

for (final String keyword: map.keySet())
    System.out.printf("%s was encountered %d time(s)\n",
        keyword, map.get(keyword).size());
fge
  • 119,121
  • 33
  • 254
  • 329
1
public class YourClass {
    private HashMap<Integer, String> occurrences = new HashMap<Integer, String>(1);

    public void addFile(String name) {
        int count = 0;
        String names = "";

        if(occurrences.size() > 0) {
            count = (int)(occurrences.keySet().toArray()[0]);
            names = occurrences.get(count);
            names += ",";
        }

        count++;
        names += name;
        occurrences.remove(count);
        occurrences.put(count, names);
    }
}

when you find a file (let's call it hello.txt), and let's say you're in YourClass when you find it, you do:

addFile("hello.txt");

do note that this is utterly retarded >.<

go with vakimshaar's solution ;)

davogotland
  • 2,718
  • 1
  • 15
  • 19
  • What does the (1) mean here: private HashMap occurrences = new HashMap(1); <--? – programmer Jan 12 '12 at 20:26
  • @programmer http://docs.oracle.com/javase/1.5.0/docs/api/java/util/HashMap.html#HashMap%28int%29 op asked for hashmap with one entry. no point in getting 15 empty ones ;) – davogotland Jan 12 '12 at 20:32
0

There was historically a paucity with the standard provided data structures of standard Java. Apache Collections has been used hand-in-hand on many occasions to add in these additional data structures we felt we were missing. I think the OP case here is one of those holes. And yes the HashMap is not the correct approach and the problem was modeled wrong by the OP.

For the case of one key, value pair in a Map, often that is exactly what we need: a Pair or more generally a Tuple. This works really well if both "the key" and "the value" were to generally have the same data type for each instance of the data structure we need.

I will refer to this SO question (which itself is a duplicate although has a lot of good information) how historically one might use tuples in Java. An example of a supplemental class which can be used is org.apache.commons.lang3.tuple

demongolem
  • 9,474
  • 36
  • 90
  • 105
0

These days one would probably use Map.of(key, value)

    /**
 * Returns an unmodifiable map containing a single mapping.
 * See <a href="#unmodifiable">Unmodifiable Maps</a> for details.
 *
 * @param <K> the {@code Map}'s key type
 * @param <V> the {@code Map}'s value type
 * @param k1 the mapping's key
 * @param v1 the mapping's value
 * @return a {@code Map} containing the specified mapping
 * @throws NullPointerException if the key or the value is {@code null}
 *
 * @since 9
 */
static <K, V> Map<K, V> of(K k1, V v1) {
    return new ImmutableCollections.Map1<>(k1, v1);
}
GedankenNebel
  • 2,437
  • 5
  • 29
  • 39