I am making a class that maps Strings to Integers. I want to be able to get the Integer associated with a particular String and iterate through the entries, which are defined as another class that implements Map.Entry<String, Integer>.
Currently I have this:
public class MyMap implements Iterable<MyEntry> {
private final Map<String, Integer> wrappedMap =
new HashMap<String, Integer>();
#Override
public Iterator<MyEntry> iterator() {
return wrappedMap.entrySet().iterator();
}
//more methods
}
It's not compiling because of a type mismatch even though MyEntry implements Map.Entry<String, Integer>.
Is there a way to make a custom implementation of Map.Entry? Is there an easier way to do this that I'm overlooking? Thanks in advanced!
It's not compiling because MyEntry is not a part of the hashmap at all. If you want to return a list of MyEntry then you need to copy the data elements into a MyEntry instance and load that into a collection. Which is going to be slow and consume a considerable amount of memory.
It should be:
#Override
public Iterator<Map.Entry<String,Integer>> iterator() {
return wrappedMap.entrySet().iterator();
}
The call to entrySet() returns a Set which contains the mappings in the hashmap. So the iterator needs to iterate over Entry objects
Why not use just a regular map?
Map<String, MyEntry> map = new HashMap<String, MyEntry>();
Then you iterator will be simply this:
Iterator<MyEntry> iter = map.values().iterator();
Even though MyEntry implements Map.Entry<K,V>, it is not the case that an Iterator<MyEntry> implements Iterator<Map.Entry<K,V>>. For a class like Iterator, that distinction doesn't make intuitive sense to a human being, so let's instead think of a Box<E> class, which has .put(E) and .contains(E) methods. Is a Box<Dinosaur> a subclass of Box<Animal>? You might think so, but that's not the case: in a Box<Animal> it's legal to call .put(someMammal), but in a Box<Dinosaur> that is clearly illegal. Since Box<Dinosaur> can't support all actions that are legal on a Box<Animal>, it is definitely not a subclass and cannot be substituted in at will.
From the compiler's point of view, the same concern might apply to iterators, and so you can't overload .iterator() to return an object which is not an instance of Iterator<K,V>.
Related
The internal implementation of HashSet
.......................................
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
//constructors
public HashSet() {
map = new HashMap<>();
}
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
public HashSet(Collection<? usnoextends E> c) {
map = new HashMap<>(Math.imax((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
//add method
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
}
Internally HashSet is using HashMap only and performance-wise HashMap is faster than HashSet
So why we are not using HashMap directly instead of going for HashSet.
Because HashSet is another type of collection - focused on the single object rather than pair of items. To make HashMap work like HashSet we would need to provide everywhere some artificial value object like
HashMap<MyItem, Object> set;
and then instead of e.g. set.add(new MyItem()) use something like set.put(new MyItem(), null) what makes no sense and can cause serious issues (when type of Object will be changed, when you will need to serialize etc)
Moreover internal implementation is nothing you should take care of - it can change in the next Java version (probably won't) and some another mechanism will be used underneath. The most important is Set interface and the fact HashSet is implementing this
What is the difference between Lists, ArrayLists, Maps, Hashmaps, Collections etc..?
As pointed by #m.antkowicz, though it internally uses HashMap, there is no guarantee.
Another major reason:
Generally in large projects, interfaces are defined independent of implementations.
If a business interface expects Set (or even Collection), it will define it as Set(or Collection)
The interface does not care about the underlying implementations(they assume the expected behavior will be maintained)
Any business concrete implementation of the interface should declare the method signature exactly(to override)
So, they also will use Set(or Collection)
Also, different implementations of Set use different Map
ConcurrentSkipListSet uses ConcurrentNavigableMap
HashSet uses HashMap
So, this is difficult to use exactly in interface contracts.
Map<Integer, String> map = new TreeMap<>();
map.put(1, "String1");
map.put(2, "String2");
map.put(3, "String3");
I wanted to convert the map's values to set. I know I could easily do
Set<String> set = new HashSet<>(map.values());
While thinking about this, I was curious what is map.values() by itself anyway? So I tried this
System.out.println("Set:"+ (map.values() instanceof Set));
System.out.println("List:"+ (map.values() instanceof List));
System.out.println("Queue:"+ (map.values() instanceof Queue));
System.out.println("SortedSet:"+ (map.values() instanceof SortedSet));
And the output surprisingly was
Set:false
List:false
Queue:false
SortedSet:false
This is all the documentation says.
a collection view of the values contained in this map
I then looked at the decompiled class file.
public Collection<V> values() {
if (values == null) {
values = new AbstractCollection<V>() {
public Iterator<V> iterator() {
return new Iterator<V>() {
private Iterator<Entry<K,V>> i = entrySet().iterator();
public boolean hasNext() {
return i.hasNext();
}
public V next() {
return i.next().getValue();
}
public void remove() {
i.remove();
}
};
}
public int size() {
return AbstractMap.this.size();
}
public boolean isEmpty() {
return AbstractMap.this.isEmpty();
}
public void clear() {
AbstractMap.this.clear();
}
public boolean contains(Object v) {
return AbstractMap.this.containsValue(v);
}
};
}
return values;
}
Why does java return an abstract implementation instead of List/Set/Queue which could be immediately compatible with use cases?
I don't know which Java version you are using, but I don't see an abstract class instance returned by values(). It returns an instance of TreeMap.Values, which is a class that extends AbstractCollection.
As to why it doesn't return a Set - that's simply due to the fact that the collection of values may contain duplicates, which a Set doesn't allow.
A List is also not ideal, since it would imply the values are ordered, which is not true for all Map implementations (such as HashMap).
BTW, instead of printing things like map.values() instanceof Set, you could have simply printed map.value().getClass().getName() to see the actual implementation class.
One reasons just from looking at the implementation: contains and iterator both look at the current contents of the map as opposed to the contents of the map when values() was called.
If you call values() and then change something in the map that change will be reflected in the Collection originally returned from values().
Map<String, String> map = new HashMap<String, String>();
map.put("some", "thing");
Collection<String> values = map.values();
System.out.println(values.size()); // 1
map.put("foo", "bar");
System.out.println(values.size()); // 2
Hashset internally uses HashMap hence there is not much copy of values occured in the scenario.
You can see related link to get more about it. Reference Link.
where has coming to abstract collection, Below are some of Related information fetched from Java Doc:
1] This class provides a skeletal implementation of the Collection
interface, to minimize the effort required to implement this interface.
2] To implement an unmodifiable collection, the programmer needs only to
extend this class and provide implementations for the <tt>iterator</tt> and
<tt>size</tt> methods. (The iterator returned by the <tt>iterator</tt>
method must implement <tt>hasNext</tt> and <tt>next</tt>.
3] To implement a modifiable collection, the programmer must additionally
override this class's <tt>add</tt> method (which otherwise throws an
UnsupportedOperationException), and the iterator returned by the
iterator method must additionally implement its remove
method.
Hope this was helpful.
If I have a data structure which looks like this:
ImmutableObj<MutableMap<String,Integer>> myObj;
Is the above enough to ensure immunity? Or do I have to make MutableMap immutable as well?
It's safe when you don't expose link to original mutable object like this (example with lists, same method for map is presents in Collections class):
List<String> strings = Arrays.asList("Foo", "Bar");
List<String> immutableStrings = Collections.unmodifiableList(strings);
because you may have to modify immutableStrings list through strings list.
Better approach is to don't have links to mutable list at all like:
List<String> immutableStrings = Collections.unmodifiableList(Arrays.asList("Foo", "Bar"));
Here we don't have link to inner mutable list with foo and bar, and not able to modify immutableStrings list.
It's synthetic example, from conversation under your question i understand that you may want something like this:
public Map<String, Integer> wordFruquencyIn(String book, String word) {
//do calculation here
Map<String, Integer> result = //result
return Collections.unmodifiableMap(result); // return unmodifiable map
}
Better to create custom class that represents result of calculation instead of map, because contracts of Map, List, etc... unclear in way you don't know if they mutable or not (lack of method isMutable() and lack of design in early java versions in general (see LSP principle in this case)). After refactor you code may be like that:
public Statistics wordFruquencyIn(String book, String word) {
//do calculation here
Map<String, Integer> result = //result
return new StatisticsImpl(result);
}
public interface Statistics {
public String word() {}
public int times() {}
}
You may create any implementation of Statistics interface that fulfill contract of this interface and have map inside or any mutable data, only one way to communicate with this class is ask about word() that returns already immutable string or times() that returns a primitive.
I have come across a piece of code where I found
public class MapImpl {
private static MapImpl mpl = new MapImpl();
Map<String,String> hm;
private MapImpl() {
hm = new HashMap<>();
}
public addContentsToMap(Map<String,String> m){
this.hm=m;
}
public Map returnMap(){
new HashMap<>(hm);
}
}
I like to know here that when the default constructor is called the map is initialized to hashmap, and when addContentsToMap is called a map is formed with values.
I see that the returnMap uses the constructor of the HashMap(Map m). I have gone through the source code of HashMap but was clueless.
It takes any implementation of Map interface and constructs a HashMap which also is an implementation of Map interface.
Developers like Hash-Collections (HashSet, HashMap etc.) including HashMap because they provide expected O(1) get and contains time.
It can be useful, once you have a Map which isn't a HashMap (e.g. Properties) and you know that it'll be large and you will read from it many times, it's useful to switch to a different implementation of a Map.
Documentation:
public HashMap(Map<? extends K,? extends V> m)
Constructs a new HashMap with the same mappings as the specified Map. The HashMap is created with default load factor (0.75) and an initial capacity sufficient to hold the mappings in the specified Map.
Parameters:
m - the map whose mappings are to be placed in this map
Throws:
NullPointerException - if the specified map is null
As far as I understand this, it seems that there is not a direct way of getting an Enumeration directly for the Keys of a HashMap. I can only get a keySet(). From that Set, I can get an Iterator but an Iterator seems to be something different than an Enumeration.
What is the best and most performant way to directly get an Enumeration from the Keys of a HashMap?
Background: I am implementing my own ResourceBundle (=>getKeys() Method), and I have to provide/implement a method that returns the Enumeration of all Keys. But my implementation is based on a HashMap so I need to somehow figure out how to best convert betweens these two "Iterator/Enumerator" techniques.
I think you can use the method enumeration from java.util.Collections class to achieve what you want.
The API doc of the method enumerate has this to say:
public static Enumeration enumeration(Collection c)
Returns an enumeration over the specified collection. This provides interoperability with legacy APIs that require an enumeration as input.
For example, the below code snippet gets an instance of Enumeration from the keyset of HashMap
final Map <String,Integer> test = new HashMap<String,Integer>();
test.put("one",1);
test.put("two",2);
test.put("three",3);
final Enumeration<String> strEnum = Collections.enumeration(test.keySet());
while(strEnum.hasMoreElements()) {
System.out.println(strEnum.nextElement());
}
and resulting the output is:
one
two
three
Apache commons-collections have an adapter that makes the Iterator available for use like an Enumeration. Take a look at IteratorEnumeration.
Adapter to make an Iterator instance appear to be an Enumeration instances
So in short you do the following:
Enumeration enumeration = new IteratorEnumeration(hashMap.keySet().iterator());
Alternatively, if you (for some reason) don't want to include commons-collections, you can implement this adapter yourself. It is easy - just make an implementation of Enumeration, pass the Iterator in a constructor, and whenever hasMoreElements() and nextElement() are called, you call the hasNext() and next() on the underlying Iterator.
Use this if you are forced to use Enumeration by some API contract (as I assume the case is). Otherwise use Iterator - it is the recommended option.
You can write a adapter to adapt to Enumeration.
public class MyEnumeration implements Enumeration {
private Iterator iterator;
public MyEnumeration(Iterator iterator){
this.iterator = iterator;
}
public MyEnumeration(Map map) {
iterator = map.keySet().iterator();
}
#Override
public boolean hasMoreElements() {
return iterator.hasNext();
}
#Override
public Object nextElement() {
return iterator.next();
}
}
And then you can use this custom enumeration :)