for example:
public static LinkedList<String, Double> ll = new LinkedList<String, Double>;
from your question, I think (not 100% sure) you are looking for
java.util.LinkedHashMap<K, V>
in your case, it would be LinkedHashMap<String, Double>
from java doc:
Hash table and linked list implementation of the Map interface, with
predictable iteration order. This implementation differs from HashMap
in that it maintains a doubly-linked list running through all of its
entries.
if you do want to get element by list.get(5), you could :
LinkedList<Entry<String, Double>>
so you can get Entry element by Entry entry = list.get(5), then entry.getKey() gives you the STring, and entry.getValue() gives you the Double.
Reading all your comments, I suggest you do something like this:
public class StringAndDouble {
private String str;
private double dbl;
// add constructor
// add getters, setters and other methods as needed.
// override equals() and hashCode()
}
Now you can use:
List<StringAndDouble> list = new LinkedList<>(); // or
List<StringAndDouble> list = new ArrayList<>(); // better in most cases
Now you can access your objects by index.
This answer creates a new class, to fit your needs. The class has two fields, one String, one double. This doesn't make the class two dimensional. I think you have a misunderstanding there. When there are n dimensions, you need n indexes to access an element. You were talking of accessing by index, so I assume you're looking for a one dimensional list holding the objects, that have more than one field.
Do you mean like this?
HashMap<String, Double> hm = new HashMap<String, Double>();
Since OP in a comment to #Kent says he wants to be able to get items by index...
Note that a LinkedList (and LinkedHashMap) are inefficient at that. He may prefer an ArrayList. So I would suggest that his "2D" implementation be a
ArrayList<Map.Entry<String, Double>>
which will efficiently support a get by index.
As for the normal get(String key), you'd have to do a linear search of all the entries, which would be inefficient.
So, you have a decision: which way of accessing (by a key or by an index) is more important?
You can actually use Linked Lists within eachother...
For Example:
public LinkedList<LinkedList<Integer>> twoDimLinkedList = new LinkedList<LinkedList<Integer>>();
Then:
////////////////
int value = twoDimLinkedList.get(3).get(4);
/////////////////
or (If you were planning on using it for iterative purposes):
/////////////////
for (int i = 0; i < twoDimLinkedList.size(); i++) {
LinkedList<Integer> twoDimLinkedListRow = new LinkedList<Integer>();
for (int m = 0; m < twoDimLinkedList.get(i).size(); m++) {
twoDimLinkedListRow.add(value);
}
twoDimLinkedList.add(twoDimLinkedListRow);
}
////////////////
Related
I have a fully working version of MineSweeper implemented in Java. However, I am trying to add an additional feature that updates a Map to store the indexes of the locations of the mines within a 2D array. For example, if location [x][y] holds a mine, I am storing a linked list containing x and y, which maps to a boolean that is true to indicate that the space holds a mine. (This feature is seemingly trivial, but I am just doing this to practice with Collections in Java.)
My relevant private instance variables include:
public Class World{ ...
private LinkedList<Integer> index;
private Map<LinkedList<Integer>, Boolean> revealed;
"index" is the list to be stored in the map as the key for each boolean.
In my constructor I have:
public World(){ ...
tileArr = new Tile[worldWidth][worldHeight];
revealed = new TreeMap<LinkedList<Integer>, Boolean>();
index = new LinkedList<Integer>();
... }
Now, in the method in which I place the mines, I have the following:
private void placeBomb(){
int x = ran.nextInt(worldWidth); //Random stream
int y = ran.nextInt(worldHeight); //Random stream
if (!tileArr[x][y].isBomb()){
tileArr[x][y].setBomb(true);
index.add(x); //ADDED COMPONENT
index.add(y);
revealed.put(index, true);
index.remove(x);
index.remove(y); //END OF ADDED COMPONENT
} else placeBomb();
}
Without the marked added component my program runs fine, and I have a fully working game. However, this addition gives me the following error.
Exception in thread "main" java.lang.ClassCastException: java.util.LinkedList
cannot be cast to java.lang.Comparable
If anyone could help point out where this error might be, it would be very helpful! This is solely for additional practice with collections and is not required to run the game.
There are actually about 3 issues here. One that you know about, one that you don't and a third which is just that using LinkedList as a key for a map is clunky.
The ClassCastException happens because TreeMap is a sorted set and requires that every key in it implement the Comparable interface, or else you have to provide a custom Comparator. LinkedList doesn't implement Comparable, so you get an exception. The solution here could be to use a different map, like HashMap, or you could write a custom Comparator.
A custom Comparator could be like this:
revealed = new TreeMap<List<Integer>, Boolean>(
// sort by x value first
Comparator.comparing( list -> list.get(0) )
// then sort by y if both x values are the same
.thenComparing( list -> list.get(1) )
);
(And I felt compelled to include this, which is a more robust example that isn't dependent on specific elements at specific indexes):
revealed = new TreeMap<>(new Comparator<List<Integer>>() {
#Override
public int compare(List<Integer> lhs, List<Integer> rhs) {
int sizeComp = Integer.compare(lhs.size(), rhs.size());
if (sizeComp != 0) {
return sizeComp;
}
Iterator<Integer> lhsIter = lhs.iterator();
Iterator<Integer> rhsIter = rhs.iterator();
while ( lhsIter.hasNext() && rhsIter.hasNext() ) {
int intComp = lhsIter.next().compareTo( rhsIter.next() );
if (intComp != 0) {
return intComp;
}
}
return 0;
}
});
The issue that you don't know about is that you're only ever adding one LinkedList to the map:
index.add(x);
index.add(y);
// putting index in to the map
// without making a copy
revealed.put(index, true);
// modifying index immediately
// afterwards
index.remove(x);
index.remove(y);
This is unspecified behavior, because you put the key in, then modify it. The documentation for Map says the following about this:
Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map.
What will actually happen (for TreeMap) is that you are always erasing the previous mapping. (For example, the first time you call put, let's say x=0 and y=0. Then the next time around, you set the list so that x=1 and y=1. This also modifies the list inside the map, so that when put is called, it finds there was already a key with x=1 and y=1 and replaces the mapping.)
So you could fix this by saying something like either of the following:
// copying the List called index
revealed.put(new LinkedList<>(index), true);
// this makes more sense to me
revealed.put(Arrays.asList(x, y), true);
However, this leads me to the 3rd point.
There are better ways to do this, if you want practice with collections. One way would be to use a Map<Integer, Map<Integer, Boolean>>, like this:
Map<Integer, Map<Integer, Boolean>> revealed = new HashMap<>();
{
revealed.computeIfAbsent(x, HashMap::new).put(y, true);
// the preceding line is the same as saying
// Map<Integer, Boolean> yMap = revealed.get(x);
// if (yMap == null) {
// yMap = new HashMap<>();
// revealed.put(x, yMap);
// }
// yMap.put(y, true);
}
That is basically like a 2D array, but with a HashMap. (It could make sense if you had a very, very large game board.)
And judging by your description, it sounds like you already know that you could just make a boolean isRevealed; variable in the Tile class.
From the spec of a treemap gives me this:
The map is sorted according to the natural ordering of its keys, or by a Comparator provided at map creation time, depending on which constructor is used.
The Java Linkedlist can not be compared just like that. You have to give it a way to compare them or just use another type of map, that does not need sorting.
I'm currently trying to create a method that determine if an ArrayList(a2) contains an ArrayList(a1), given that both lists contain duplicate values (containsAll wouldn't work as if an ArrayList contains duplicate values, then it would return true regardless of the quantity of the values)
This is what I have: (I believe it would work however I cannot use .remove within the for loop)
public boolean isSubset(ArrayList<Integer> a1, ArrayList<Integer> a2) {
Integer a1Size= a1.size();
for (Integer integer2:a2){
for (Integer integer1: a1){
if (integer1==integer2){
a1.remove(integer1);
a2.remove(integer2);
if (a1Size==0){
return true;
}
}
}
}
return false;
}
Thanks for the help.
Updated
I think the clearest statement of your question is in one of your comments:
Yes, the example " Example: [dog,cat,cat,bird] is a match for
containing [cat,dog] is false but containing [cat,cat,dog] is true?"
is exactly what I am trying to achieve.
So really, you are not looking for a "subset", because these are not sets. They can contain duplicate elements. What you are really saying is you want to see whether a1 contains all the elements of a2, in the same amounts.
One way to get to that is to count all the elements in both lists. We can get such a count using this method:
private Map<Integer, Integer> getCounter (List<Integer> list) {
Map<Integer, Integer> counter = new HashMap<>();
for (Integer item : list) {
counter.put (item, counter.containsKey(item) ? counter.get(item) + 1 : 1);
}
return counter;
}
We'll rename your method to be called containsAllWithCounts(), and it will use getCounter() as a helper. Your method will also accept List objects as its parameters, rather than ArrayList objects: it's a good practice to specify parameters as interfaces rather than implementations, so you are not tied to using ArrayList types.
With that in mind, we simply scan the counts of the items in a2 and see that they are the same in a1:
public boolean containsAllWithCounts(List<Integer> a1, List<Integer> a2) {
Map<Integer,Integer> counterA1 = getCounter(a1);
Map<Integer,Integer> counterA2 = getCounter(a2);
boolean containsAll = true;
for (Map.Entry<Integer, Integer> entry : counterA2.entrySet ()) {
Integer key = entry.getKey();
Integer count = entry.getValue();
containsAll &= counterA1.containsKey(key) && counterA1.get(key).equals(count);
if (!containsAll) break;
}
return containsAll;
}
If you like, I can rewrite this code to handle arbitrary types, not just Integer objects, using Java generics. Also, all the code can be shortened using Java 8 streams (which I originally used - see comments below). Just let me know in comments.
if you want remove elements from list you have 2 choices:
iterate over copy
use concurrent list implementation
see also:
http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#synchronizedList-java.util.List-
btw why you don't override contains method ??
here you use simple Object like "Integer" what about when you will be using List< SomeComplexClass > ??
example remove with iterator over copy:
List<Integer> list1 = new ArrayList<Integer>();
List<Integer> list2 = new ArrayList<Integer>();
List<Integer> listCopy = new ArrayList<>(list1);
Iterator<Integer> iterator1 = listCopy.iterator();
while(iterator1.hasNext()) {
Integer next1 = iterator1.next();
Iterator<Integer> iterator2 = list2.iterator();
while (iterator2.hasNext()) {
Integer next2 = iterator2.next();
if(next1.equals(next2)) list1.remove(next1);
}
}
see also this answer about iterator:
Concurrent Modification exception
also don't use == operator to compare objects :) instead use equal method
about use of removeAll() and other similarly methods:
keep in mind that many classes that implements list interface don't override all methods from list interface - so you can end up with unsupported operation exception - thus I prefer "low level" binary/linear/mixed search in this case.
and for comparison of complex classes objects you will need override equal and hashCode methods
f you want to remove the duplicate values, simply put the arraylist(s) into a HashSet. It will remove the duplicates based on equals() of your object.
- Olga
In Java, HashMap works by using hashCode to locate a bucket. Each bucket is a list of items residing in that bucket. The items are scanned, using equals for comparison. When adding items, the HashMap is resized once a certain load percentage is reached.
So, sometimes it will have to compare against a few items, but generally it's much closer to O(1) than O(n).
in short - there is no need to use more resources (memory) and "harness" unnecessary classes - as hash map "get" method gets very expensive as count of item grows.
hashCode -> put to bucket [if many item in bucket] -> get = linear scan
so what counts in removing items ?
complexity of equals and hasCode and used of proper algorithm to iterate
I know this is maybe amature-ish, but...
There is no need to remove the items from both lists, so, just take it from the one list
public boolean isSubset(ArrayList<Integer> a1, ArrayList<Integer> a2) {
for(Integer a1Int : a1){
for (int i = 0; i<a2.size();i++) {
if (a2.get(i).equals(a1Int)) {
a2.remove(i);
break;
}
}
if (a2.size()== 0) {
return true;
}
}
return false;
}
If you want to remove the duplicate values, simply put the arraylist(s) into a HashSet. It will remove the duplicates based on equals() of your object.
I have a Map to sort as follows:
Map<String, String> map = new HashMap();
It contains the following String keys:
String key = "key1.key2.key3.key4"
It contains the following String values:
String value = "value1.value2"
where the key and value can vary by their number of dot sections from key1/value1 to key1.key2.key3.key4.key5/value1.value2.value3.value4.value5 non-homogeneously
I need to compare them according to the number of dots present in keys or in values according to the calling method type key / value :
sortMap(Map map, int byKey);
or
sortMap(Map map, int byValue);
The methods of course will return a sorted map.
Any help would be appreciated.
There is no way to impose any sort of order on HashMap.
If you want to order elements by some comparison on the keys, then use a TreeMap with some Comparator on the keys, or just use their default Comparable ordering.
If you want to order by the values, the only real option is to use a LinkedHashMap, which preserves the order that entries were put into the map, and then to sort the entries before inserting them into the map, or perhaps some non-JDK Map implementation. There are dirty hacks that make a key comparator that actually secretly compares the values, but these are dangerous and frequently lead to unpredictable behavior.
For starters, you will need to be using an instance of SortedMap. If the map doesn't implement that interface, then it has an undefined/arbitrary iteration order and you can't control it. (Generally this is the case, since a map is a way of associating values with keys; ordering is an auxiliary concern.)
So I'll assume you're using TreeMap, which is the canonical sorted map implementation. This sorts its keys according to a Comparator which you can supply in the constructor. So if you can write such a comparator that determines which is the "lower" of two arbitrary keys (spoiler alert: you can), this will be straightforward to implement.
This will, however, only work when sorting by key. I don't know if it makes much sense to sort a map by value, and I'm not aware of any straightforward way to do this. The best I can think of is to write a Comparator<Map.Entry> that sorts on values, call Map.getEntrySet and push all the entries into a list, then call Collections.sort on the list. It's not very elegant or efficient but it should get the job done if performance isn't your primary concern.
(Note also that if your keys aren't immutable, you will run into a lot of trouble, as they won't be resorted when externally changed.
You should use a TreeMap and implement a ValueComparator or make the key and value objects that implement Comparable.
Must be a duplicate here...
edit: duplicate of (to name just one) Sort a Map<Key, Value> by values (Java)
I did it by the following:
#SuppressWarnings({ "unchecked", "rawtypes" })
public static Map sortMap(Map unsortedMap) {
List list = new LinkedList(unsortedMap.entrySet());
// sort list based on comparator
Collections.sort(list, new Comparator() {
public int compare(Object o1, Object o2) {
String value1 = (String)((Map.Entry) (o1)).getValue();
String value2 = (String)((Map.Entry) (o2)).getValue();
// declare the count
int count1 = findOccurances(value1, '.');
int count2 = findOccurances(value2, '.');
// Go to thru the comparing
if(count1 > count2){
return -1;
}
if(count1 < count2){
return 1;
}
return 0;
}
});
// put the sorted list into map again
Map sortedMap = new LinkedHashMap();
for (Iterator it = list.iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
sortedMap.put(entry.getKey(), entry.getValue());
}
return sortedMap;
}
With the following helper method:
private static int findOccurances(String s, char chr) {
final char[] chars = s.toCharArray();
int count = 0;
for (int i = 0; i < chars.length; i++) {
if (chars[i] == chr) {
count++;
}
}
return count;
}
Here, I can put some switch on the comparing part with an additional int argument to change between asc/desc.
I can change between values and keys through a switch of another int argument value to get my answer.
How do I sort hash table elements alphabetically? For example, my elements are:
cijfercode, Zweeds, Doorloper, Kruizword, Crypto, Woordzoker
edit: I also got a solution for sorting the hashtable elements. Here is the solution:
java.util.Vector vec = new java.util.Vector(hashtableList.keySet());
Collections.sort(vec);
If these "elements" are keys you can store them in a TreeMap, which will produce a consistent order based on the natural ordering of the keys. Note you don't need to do much except create a new map with the old map passed to the constructor:
Map<String,?> map = ?
Map<String,?> orderedMap = new TreeMap<String,?>(map);
Then, iterate like normal:
for(String key : orderedMap.keys()){
}
If your "elements" are values, then you can insert them as keys into a TreeMap keeping track of the original keys, read the sorted order of values as before (basically creating an inverted index):
Map<?,String> map = ?
Map<String,List<?>> orderedVals = new TreeMap<String,List<?>>();
for(Entry<?,String> map : map.entrySet()){
List<?> keys = orderedVals.get(map.getValue());
if(keys == null){
keys = new ArrayList<?>();
orderedVals.put(map.getValue(), keys);
}
keys.add(map.getKey());
}
// now orderedVals has keys in sorted order
for(String val : orderedVals.keys()){
}
Of course, if you're not actually using anything related to the fact these things are in a "hashtable" (I read this as something implementing Map), then you can load up a List of your choosing, and sort it:
List<String> list = new ArrayList<String>(map.values()); // or use map.keys()
Collections.sort(list);
If you're not happy with the default sort order for String, feel free to write your own comparator:
Collections.sort(list, new Comparator<String>(){
public int compare(String left, String right){
return // your impl
}
});
compare must return a negative integer when the left comes first, 0 if left and right are the same, and a positive integer if right comes first.
Mark Elliot's idea is correct. I don't like the whole Map<?, List<?>> idea though; I've been far too spoilt on Guava. So here's a Guava version of the same idea:
SortedSetMultimap<String, ?> sorted = Multimaps.invertFrom(
Multimaps.forMap(map), TreeMultimap.create());
for (Map.Entry<String, ?> entry : sorted.entries()) {
// ...
}
This is, like, a third of the size of Mark's code. :-)
java.util.Vector vec =new java.util.Vector(hashtableList.keySet());
Collections.sort(vec);
Please check http://discuss.joelonsoftware.com/default.asp?joel.3.19588.13 for an interesting discussion on this.
Consider http://download.oracle.com/javase/1.4.2/docs/api/java/util/TreeMap.html too.
I'm storing my wordcount into the value field of a HashMap, how can I then get the 500 top words in the text?
public ArrayList<String> topWords (int numberOfWordsToFind, ArrayList<String> theText) {
//ArrayList<String> frequentWords = new ArrayList<String>();
ArrayList<String> topWordsArray= new ArrayList<String>();
HashMap<String,Integer> frequentWords = new HashMap<String,Integer>();
int wordCounter=0;
for (int i=0; i<theText.size();i++){
if(frequentWords.containsKey(theText.get(i))){
//find value and increment
wordCounter=frequentWords.get(theText.get(i));
wordCounter++;
frequentWords.put(theText.get(i),wordCounter);
}
else {
//new word
frequentWords.put(theText.get(i),1);
}
}
for (int i=0; i<theText.size();i++){
if (frequentWords.containsKey(theText.get(i))){
// what to write here?
frequentWords.get(theText.get(i));
}
}
return topWordsArray;
}
One other approach you may wish to look at is to think of this another way: is a Map really the right conceptual object here? It may be good to think of this as being a good use of a much-neglected-in-Java data structure, the bag. A bag is like a set, but allows an item to be in the set multiple times. This simplifies the 'adding a found word' very much.
Google's guava-libraries provides a Bag structure, though there it's called a Multiset. Using a Multiset, you could just call .add() once for each word, even if it's already in there. Even easier, though, you could throw your loop away:
Multiset<String> words = HashMultiset.create(theText);
Now you have a Multiset, what do you do? Well, you can call entrySet(), which gives you a collection of Multimap.Entry objects. You can then stick them in a List (they come in a Set), and sort them using a Comparator. Full code might look like (using a few other fancy Guava features to show them off):
Multiset<String> words = HashMultiset.create(theWords);
List<Multiset.Entry<String>> wordCounts = Lists.newArrayList(words.entrySet());
Collections.sort(wordCounts, new Comparator<Multiset.Entry<String>>() {
public int compare(Multiset.Entry<String> left, Multiset.Entry<String> right) {
// Note reversal of 'right' and 'left' to get descending order
return right.getCount().compareTo(left.getCount());
}
});
// wordCounts now contains all the words, sorted by count descending
// Take the first 50 entries (alternative: use a loop; this is simple because
// it copes easily with < 50 elements)
Iterable<Multiset.Entry<String>> first50 = Iterables.limit(wordCounts, 50);
// Guava-ey alternative: use a Function and Iterables.transform, but in this case
// the 'manual' way is probably simpler:
for (Multiset.Entry<String> entry : first50) {
wordArray.add(entry.getElement());
}
and you're done!
Here you can find a guide how to sort a HashMap by the values. After the sorting you can just iterate over the first 500 entries.
Take a look at the TreeBidiMap provided by the Apache Commons Collections package. http://commons.apache.org/collections/api-release/org/apache/commons/collections/bidimap/TreeBidiMap.html
It allows you to sort the map according to both the key or the value set.
Hope it helps.
Zhongxian