Scenario: Creating a server which has Room objects which contain User objects.
I want to store the rooms in a Map of some sort by Id (a string).
Desired Behavior:
When a user makes a request via the server, I should be able to look up the Room by id from the library and then add the user to the room, if that's what the request needs.
Currently I use the static function in my Library.java class where the Map is stored to retrieve Rooms:
public class Library {
private static Hashtable<String, Rooms> myRooms = new Hashtable<String, Rooms>();
public static addRoom(String s, Room r) {
myRooms.put(s, r);
}
public static Room getRoomById(String s) {
return myRooms.get(s);
}
}
In another class I'll do the equivalent of myRoom.addUser(user);
What I'm observing using Hashtable, is that no matter how many times I add a user to the Room returned by getRoomById, the user is not in the room later.
I thought that in Java, the object that was returned was essentially a reference to the data, the same object that was in the Hashtable with the same references; but, it isn't behaving like that. Is there a way to get this behavior? Maybe with a wrapper of some sort? Am I just using the wrong variant of map?
Help?
It's very peculiar that you declare myRooms as Hashtable<String, Rooms> (plural Rooms), but you put a singular Room r in addRoom.
Are you trying to do some sort of multimap, where a key can map to a multiple values? If so, then either us the Guava implementation, or implement your own as a Map<K,Set<V>> (map a key to a set of values).
I'm not sure if this your fundamental issue, though.
It is correct that the returned value by get should be the same object, as defined by reference equality, as the one used in put.
Object someObject = new Object();
Map<String,Object> map = new HashMap<String,Object>();
map.put("key1", someObject);
System.out.println(map.get("key1") == someObject); // prints "true"
someObject = "something else";
System.out.println(map.get("key1") == someObject); // prints "false"
The above is expected behavior.
Sometimes people have trouble with a Map because their key objects do not implement hashCode and equals contract properly, but if you're using String as keys, this should not be an issue.
By the way, Hashtable has a newer, sexier cousin in HashMap. I noticed the multithreading tag in the question, so if you actually need the synchronized feature, you can use Collections.synchronizedMap
In either case, whether you use a HashMap or a Hashtable, you want declare myRooms simply as a Map (see Effective Java 2nd Edition, Item 52: Refer to objects by their interfaces).
Related questions
Overriding equals and hashCode in Java
Understanding the workings of equals and hashCode in a HashMap
Java hashmap vs hashtable
Difference between a Deprecated and Legacy API?
Does your code even compile?
public class Library {
private static Hashtable<String, Rooms> myRooms = new Hashtable<String, Rooms>();
public static addRoom(String s, Room r) { // Your hashtable holds Rooms yet you add a Room
myRooms.put(s, r);
}
public static Room getRoomById(String s) { // Again, returning a Room, instead of Rooms
return myRooms.get(s);
}
}
Just by looking at this, it shouldn't compile. Now I can only assume that it was a typo, and if that's the case then show us the Room code where you're trying to add a user to the room.
Related
I'm new to Java and currently stuck on an assignment question.
I need to create a method for the LeagueAdmin class called addTeam(), which takes two arguments and returns no value. I'm given the header: public void addTeam(String division, Team team)
The method should check to see if a list for that division already exists in the teams map.
If the list exists, then the new Team should be added to the existing list of teams for that division.
If the list does not exist, then a new empty list of Team should be created and the new Team should be added to it, then a new key-value pair should be created in teams with division as the key and the new list as the value.
My code so far is as follows:
import java.util.*;
public class LeagueAdmin
{
public Map<String, List<Team>> teams;
/**
* Constructor for objects of class LeagueAdmin
*/
public LeagueAdmin()
{
this.teams = new HashMap<String, List<Team>>();
}
}
I have separate class as follows:
public class Team
{
private String name;
private String division;
private int won;
private int drew;
private int lost;
// no need to record points as = 3*won + drew
/**
* Constructor for objects of class Team
*/
public Team(String aName, String aDivision)
{
name = aName;
division = aDivision;
// no need to set won, drew and lost to 0
}
}
If anyone can give me some pointers that would be great,
Thanks, Kat
Before answering your question, a couple of suggestions.
always declare local variables and fields as final when possible
always format your code. Seems superfluous but it's something I don't see done often
initialize final fields in-line, not inside the constructor, if possible
don't expose fields, which are part of implementation details
Edit: being that maybe you need a simplified version, I'll also add that, but keep reading all my answer, it's fun!
Follow the comments inside the code to understand the flow.
public class LeagueAdmin {
private final Map<String, List<Team>> teams = new HashMap<String, List<Team>>();
public void addTeam(final String division, final Team team) {
// We retrieve the team list, if any
List<Team> list = map.get(division);
// We check if the list was already there, or we have to create a new one
if (list == null) {
// The list is null, we need to insert a new one!
list = new ArrayList<>();
map.put(division, list);
}
// We add the team to the list
list.add(team);
}
}
Now, for a more "new" and simplified version, which does the exact same thing as the one above, the implementation would be
public class LeagueAdmin {
private final Map<String, List<Team>> teams = new HashMap<String, List<Team>>();
public void addTeam(final String division, final Team team) {
teams.computeIfAbsent(division, __ -> new ArrayList<>()).add(team);
}
}
Since Java 8, the Map interface exposes a new method
computeIfAbsent(Key, Function)
What this does is
try to get the value associated with the inputted key
if no value found, use the Function argument to associate a new one
return the associated value
Also, you'll ask yourself what __ -> is.
Well, __ is just a valid variable name, used to say "hey, I don't need that"
The -> (arrow) is part of a lambda expression, which is basically an in-line function
The Answer by LppEdd suggesting computeIfAbsent is correct, but perhaps not what your course instructor is looking for. She/he probably wants you to more coding than that, as we did do in real work before that new method was added recently.
The old-fashioned of the addTeam(String division, Team team) method you need to add to LeagueAdmin would ask the map if it has a key for the division string. If no such key, add one, and put as its value a new empty List of teams to which you have added the team in question. If there is such a key, retrieve its value, a List, and then add the team in question to that list.
As a homework assignment, to do the learning you need to struggle with this a bit rather than copy some existing code. Study the JavaDoc for the Map and List interfaces. Follow my prose above as pseudo-code to walk you through the logic. Be sure you understand the ideas behind each class, especially the key-value pairing of a map (also known as a dictionary or an associative array). Draw a diagram on paper, imagining each step I outlined above.
So a league object holds a Map object. The map holds a collection of keys, each key being a division name (a String in your situation, more likely a Division class in real work). Being a key in a map means each division name will lead you to a List object, a collection of teams. A List keeps items in the order in which they are added. A Set would like work just as well here, by the way.
Note in this diagram how one of the List objects is empty, meaning it has not yet had any teams assigned. An empty list is not nothing; an empty list is something. The empty list is still a valid object, just like a fruit basket without any fruit in it is still a basket. If no list, empty or otherwise, has yet to be assigned to a key in a map, the key points to null where null really does mean “nothing at all”. We see in this diagram that the “division-z” key in the map has not yet been assigned any List object at all, so it points to nothing, null.
just tried to do something like:
public class GameMap {
protected HashMap<Sector, Integer[]> mapping;
protected void loadMapFromFile(String fileLocation, int numPlayers) {
.
//Other stuff
.
.
case "ALIENSECT":
Integer[] someValue = {5};
mapping.put(new AlienSector(row, col), someValue);
break;
}
public justATestMethod() {
System.out.println(mapping.containsKey(new Sector(6, 'L')));
}
Where AlienSector is a subclass of Sector.
But when I try to do this in another class:
mappa.justATestMethod();
The result is "false".
Instead if I rewrite the method "justATestMethod()" like this:
System.out.println(mapping.containsKey(new AlienSector(6, 'L')));
Result is "true".
I obtain "true" also changing this lines of "loadMapFromFile" method:
case "ALIENSECT":
Integer[] someValue = {5};
mapping.put(new AlienSector(row, col), someValue);
break;
This way:
case "ALIENSECT":
mapping.put(new Sector(row, col), new Integer[1]);
Integer[] aCazzo = {5};
mapping.put(new AlienSector(row, col), aCazzo);
break;
That is first filling the HashMap with Sector objects keys and then assigning keys of AlienSector objects.
Someone could explain me why this happens? AlienSector is a subclass of Sector, why Java doesn't recognize the presence of a Sector in the HashMap keys if I simply instantiate a subclass of it without first instantiate the key with an istance of the superclass "Sector" itself?
You are storing an AlienSector in the HashMap, and then trying to retrieve it with another Sector created using the same parameters. When you try to retrieve an object from a HashMap it is looking for an object that is 'equal' to the one you stored. But by default Java does not recognize two objects as 'equal' just because they have the same members. By default they are only equal if they are the same object (Strings, Integers etc. are special cases).
What you need to do is tell Java that two objects with the same parameters are 'equal' by overriding the 'equals' method. From your test results it looks like yo uhave done this for AlienSector. But you will need to do this for both Sector and AlienSector, and arrange it so the objects are considered equal even if they have different classes i.e. an AlienSector is considered equal to a Sector with the same members, and a Sector is considered equal to an AlienSector with the same members. There are tutorials on how to do this.
You will also need to override the hashCode() method to make sure that any two objects that would be considered 'equal' also return the same hashCode. HashMap uses hashCode a filter, deciding that things with different hashCodes can never be equal.
The details of all this are too long to put in an answer like this.
by the way, if you used the same object in the containsKey call, instead of creating a new one, you would find it worked.
You can use the class below to perform this behaviour.
One caveat to note is that if it is a large map, it may not be particularly performant, but for most cases the size of the map will be small so there is no real performance impact with this.
NOTE: JDK8+ code
Essentially we override the regular hashmap class methods for containsKey and get to do the appropriate searching.
import java.util.HashMap;
import java.util.Optional;
public class PolymorphicHashMap<K extends Class<?>,V> extends HashMap<K,V> {
#Override
public boolean containsKey(Object key) {
return findEntry((K)key).isPresent();
}
#Override
public V get(Object key) {
var entry = findEntry((K)key);
return entry.map(Entry::getValue).orElse(null);
}
private Optional<Entry<K,V>> findEntry(K key) {
return entrySet().stream()
.filter(e -> e.getKey().isAssignableFrom(key))
.findFirst();
}
}
HashMap uses the hashCode() function in order to lookup and store key/value pairs.
I believe you need to have both the superclass/subclass return the same hashcode in order to be able to lookup the keys of subclass in the HashMap.
public class AlienSector {
public int hashcode() {
//
// Generate a hashcode unique to this AlienSector object here
//
}
}
public class Sector {
public int hashCode() {
return super.hashCode(); // Return the same hashcode as the super class
}
}
As pointed out in the comment, the rule is that if you override the hashCode() function, you need to override the equals() function as well (and vice-versa)
I am getting confused with one concept. Can someone please throw some light on it.
Question: If the key of Hashmap is Immutable Object(create by developer) then do we need to override hashcode() and equals()? Or having immutable field as key solves the problem of overriding hashcode() and equals()?
Thanks.
Yes. I'll cite the example of java.lang.Integer here. If we wish to have a (sparse) mapping of integers to objects, we'd use something along the lines of HashMap<Integer, Object>. If we add an entry Integer.valueOf(2)=>"foo", and try to retrieve it with new Integer(2) then the overridden hashcode and equals are required.
These are slightly different categories of issues.
As in hexafraction's answer, having immutable instances is not sufficient to let you skip the step of writing an equals and hashCode, if two different instances could ever be considered to be the same. new Integer(2) should always be equal to every other new Integer(2), even though the objects are immutable and the instances are different.
That said, there are examples of "instance-controlled classes" where the default behavior of instance identity is enough:
Enum instances are created at compile time, one per value. There is (theoretically) no way to produce any other instance. If no two instances are equal, the default implementation of equals and hashCode is sufficient. Enum instances aren't compiler-guaranteed to be immutable, but you should treat them as such.
If your class's instances are guaranteed to be different from one another, regardless of whether they're immutable, you can skip equals and hashCode. One could imagine a Car object, where every Car that a CarFactory produces is different.
As a variation of the above, if you control object instantiation tightly enough that equal representations are always given the exact same instance, then that could be considered sufficient:
public class MyClass {
private MyClass(int foo) { /* ... */ }
private static final Map<Integer, MyClass> instanceCache = new HashMap<>();
/** Returns an existing MyClass(foo) if present; otherwise, creates one. */
public synchronized static MyClass create(int foo) {
// Neither leak-proof or thread-safe. Just demonstrating a concept.
if (instanceCache.contains(foo)) {
return instanceCache.get(foo);
}
MyClass newMyClass = new MyClass(foo);
instanceCache.put(foo, newMyClass);
return newMyClass;
}
}
Try it and see:
public class OverrideIt
{
static class MyKey
{
public final int i; // immutable
public MyKey(int i)
{
this.i = i;
}
}
public static void main(String[] args)
{
Map<MyKey, String> map = new HashMap<MyKey, String>();
map.put(new MyKey(1), "Value");
String result = map.get(new MyKey(1));
System.out.println(result); // null
}
}
which prints out null, showing that we failed to look up our value. This is because the two copies of MyKey are not equal and don't have the same hashcode, because we did not override .equals() and hashcode().
Facts
Basic data structure for hashMap is Entry[] (Entry is kind of LinkedList).
Key's hashcode used to locate the position in this Array
Once the Entry retried using hashcode then Key's Equal used to pick the correct Entry (By Iterating hasNext)
Default hashcode returns unique Integer value for that instance.
And you Agreed in comment section
"Is it possible that you'll store an object using one key,
and then try to retrieve it using a key which is an identical object,
but not the same object"
Even though both keys having same value the instances are different. then you might have different hashcode for both keys as per contract (Fact 4) . Thus you will have different position in array (Rule 2)
map.put(new key(1),"first element");
Here key Object does not override so it will return hashcode unquie per instance. (to avoid too complication assume the hashcode returned as 000025 . So Entry[25] is "First Element" )
map.get(new key(1))
Now this new key may return hashcode value as 000017 , So it would try to get value from Entry[17] and return null ( which is not excepted) .
Note I just gave sample as 000025 and 000017 for simplicity , actually hashmap would revisit the hashcode and change it based on array size
So far we have not discussed weather the Key is Mutable or Immutable . Irrespective of the key is Mutable or Immutable
If you store an object using one key,and then try to retrieve it using a key
which is an identical object,but not the same object
You need to override the hashcode and make sure it returns the same Integer , so that it would locate same bucket (position in Array) and get the element. Same applies to equals to get the correct element from Entry
Ok, this might be a little hard to explain. I'm looking for a way to build a Hashmap of some sort, which will be populated with keys that I already know (because they are properties of the object I am creating). Each of these known keys will have values that are either a String, an int or float (which will be autoboxed), or - later - some kind of object/ function (I still don't know how I'll do that part, but it'll be further along the way anyway). I also want to set which type accepts each of the key (say key "x" will only accept an Integer type of value, key "equation" will only accept a string, etc.).
Just to give more context, my goal is to build a Tweener utility, as I found no such library in Java, except for SumoTween, that doesn't fit my needs at all. I'm kind of trying to build my classes the way Caurina is. If you have a better alternative that would save me time and trouble, please feel free to share (be it about Tweeners or about my Hashmap question). By the way, I'm doing this Tweener to use it in an Android game I'm building, in order to do animations on bitmap/drawable objects (can't use the animation classes for those afaik).
PS : This is my first question on this website, I hope I don't sound too confused; Please bear with me.
Why are you using a hashmap for this? If you have known "keys", just make a class with those members and then you can type them however you want.
If you really must use a hashmap for some reason, you can just extend it and override the put() method to check for your magic values. But I strongly recommend against that, it's poor design.
If I understand you correctly, you are basically looking for a HashMap. Is that correct?
PS Yes, I know that using "Object" that way isn't very pretty.
I'm not entirely sure if this is what you're asking for, but it sounds like you could use some kind of interface/abstract class for the key and implement/extend it appropriately for each key type, e.g.:
public interface Key {}
private static final class StringKey implements Key {
private final String value;
public StringKey(String value) {
this.value = value;
}
// hashCode, equals here
}
// IntegerKey, FloatKey, etc similarly
Then have something like:
HashMap<Key, Integer> map = new HashMap<Key, Integer>();
I'd say create a custom class that inherits from say HashMap, on insert you check the specified key/value if it's valid.
public static final HashMap<String, Class<?>> allowedTypes = new HashMap<String, Class<?>>() {{
put("key1", String.class);
put("key2", Integer.class);
// etc
}};
public class CustomHashMap extends HashMap {
public Object put(Object key, Object value) {
Class<?> type = allowedTypes(key);
if(type != null && !type.isInstance(value))
throw new IllegalArgumentException("Invalid type for key " + key);
return put(key, value);
}
private void PutAll(Map m) {
for(Entry<?, ?> entry : m.entrySet())
put(entry.getKey(), entry.getValue());
}
}
You could have separate maps, one for the Strings; another for the Integers, etc. And when adding to any of these maps, you could add the key to a (global) set, to ensure that no key is duplicated across maps. I suspect that wouldn't meet your needs very well (and of course the lookup is a little more involved), but if it works for you, great.
Maybe a better option is to add another level of indirection. Make your map a Hashmap where YourThing has subclasses for each of the types you want to hold. So, a StringYourThing has a String data member; an IntegerYourThing has an int data member, etc. I wouldn't be surprised to find that YourThing starts to take on functionality that is currently a switch statement somewhere inside another class that is trying to deal consistently with all these different data types. If that class could simply interact with a YourThing, it could get simpler. But it's hard to know without seeing your code.
I would rather avoid using map directly. What you are looking for is probably something more application specific:
Assume the whole thing you are building is for bunch of settings. Your "Setting" will have a predefined set of key. Each key is predefined to accept corresponding setting value of specific value. If incorrect type is provided, exception will be thrown.
I think something roughly like this is reasonable:
enum SettingValueType {
public boolean isCorrectType(Object obj) {
return true if obj is correct type represented by this enum
}
STRING, INT, FLOAT // add support to other type if u want
};
clsss SettingValue {
SettingValueType type;
Object value;
}
class SettingRepo { // a repository of setting entries
private Map<String, SettingValueType> allowedKeyAndType;
private Map<String, Object> settings;
SettingRepo() {
// setup allowedKeyAndType programmatically or from config etc, depends on your design
}
public SettingValue setSetting(String key, Object value) {
SettingValueType valueType = allowedKeyAndType.get(key);
if (valueType == null) {
throw new KeyNotAllowedException();
}
if (v!alueType.isCorrectType(value) {
throw new ValueIncorectTypeException();
}
return settings.put(key, new SettingValue(valueType, value));
}
}
public SettingValue getSetting(String key) {
// u may throw exception if key is not in predefined set
return settings.get(key);
}
// u may consider adding some convinient methods too:
public String setStringSetting(String key, String value) {
if alllowedKeyAndType do not contains key {
throw KeyNOtAllowedException
}
if type is not STRING {
throw IncorrectTypeExceptin
}
settings.put(key, new SettingValue(STRING, value))
}
public String getStringSetting(String key) {
// should be straight forward now, right?
}
}
There are lots of place that can be improved according to your usage:
if ur types are very dynamic, u may make SettingValueType something like a bunch of strategies, or use Class directly. setSetting() can made generic and take Class as extra parameter etc. But at least, this should give u a starting point.
This question was asked to me in MS interview. I wanna know the exact design issue in this piece of code. Code was already given, needed to find the design issue.
I have class MyHashMap which extends java HashMap class. In MyHashMap class I have to keep some information of employees. Key in this map will be firstName+lastName+Address .
public MyHashMap extends HashMap<Object, Object> {
//some member variables
//
public void put(String firstName, String lastName, String Address, Object obj) {
String key = firstName + lastName+ Address;
put(key, obj);
}
public Object get(String firstName, String lastName, String Address) {
String key = firstName + lastName+ Address;
return get(key);
}
public void remove(Strig key) {
put(key, "");
}
//some more methods
}
What is wrong with this design? Should I subclass HashMap or should I declare
HashMap as member variable of this class? Or should I have implemented hashCode/equals methods?
There are quite a few problems, but the biggest problem I can see it that you're using a concatenated String as a key. The following two calls are different, but equivalent:
final MyHashMap map = new MyHashMap();
map.put("foo", "", "baz", new Object());
map.put("", "foo", "baz", new Object()); // Overwrites the previous call
There's also an issue that you're declaring that the key type as an Object, but always using String and are therefore not taking advantage of the type safety that comes with generics. For example, if you wanted to loop through the keySet of your Map, you'd have to cast each Set entry to a String, but you couldn't be sure that someone didn't abuse you Map by using an Integer key, for example.
Personally, I would favour composition over inheritance unless you have a good reason not to. In your case, MyHashMap is overloading the standard Map methods of put, get and remove, but not overriding any of them. You should inherit from a class in order to change its behaviour, but your implementation does not do this, so composition is a clear choice.
To act as an example, overloading rather than overriding means that if you make the following declaration:
Map<Object, Object> map = new MyHashMap();
none of your declared methods will be available. As recommended by some of the other answers, it would be far better to use an object composed of firstName, lastName and address to act as your map key, but you must remember to implement equals and hashCode, otherwise your values will not be retrievable from the HashMap.
What's wrong with that design is primarily that is claims to be a HashMap<Oject, Object>, but isn't really. The overloaded methods "replace" the Map methods, but those are still accessible - now you're supposed to use the class in a way that is incompatible with the Map interface and ignore the (still technically possible) compatible way to use it.
The best way to do this would be to make an EmployeeData class with name and address fields and hashCode() and equals() methods based on those (or, better yet, a unique ID field). Then you don't need a non-standard Map subclass - you can simply use a HashMap<EmployeeData, Object>. Actually, the value type should be more specific than Object as well.
I would go for declaring HashMap as member variable of your class.
I don't remember if a great explanation is given in Clean Code or Effective Java, but basically, it's simplier to do, requires less work if the API changes.
generally, extending such a class means you want to change its behavior.
The map uses an internal key based on first name, last name and address. So the remove method should (1) be implemented as remove(String firstName, String lastName, String Address) and (2) it should not change the behaviour of the original remove method (which really deleted the entry) by just changing the value. A better implementation of remove would be:
public void remove(String firstName, String lastName, String address) {
String key = firstName + lastName + address;
return remove(key);
}
My first take would be that:
public MyHashMap extends HashMap<Oject, Object>
is wrong. No type safety.
If a Map is required then at least make it
public MyHashMap extends HashMap<NameAddress, Employee>
and adjust the put and get the same. A NameAddress use firstname, lastname and address
in the equals and hashCode.
I personally feel that a Set interface would match better, with possibly a HashMap as a member. Then you could define the interface like it should be:
public EmployeeSet implements Set<Employee> {
private static class NameAddress {
public boolean equals();
public int hashCode();
}
private HashMap employees = new HashMap<NameAddress, Employee>();
public add(Employee emp) {
NameAddress nad = new NameAddress(emp);
empoyees.add(nad, emp);
}
public remove(Employee emp) {
}
}
and extract name and address info within the implementation to create the key.
EDIT David pointed out that NameAddress doesn't need to be Comparable and I removed that part of the interface. Added hashCode for correctness.
Two other "heinous crimes" against good practice are that the remove method:
overloads Map.remove(<K>) rather than overriding it (so you have a leaky abstraction), and
implements a behavior that is incompatible with the behavior of the method it overrides!
Setting the value associated with a key to an empty string is NOT THE SAME as removing the entry for the key. (Even setting it to null is subtly different ...)
This is not a violation of the Liskov substitutability principle, but it could be really confusing for someone trying to use the type. (Depending on whether you called the method via the Map type or the MyHashMap type you could end up using different methods with different semantics ....)
I think it depends a lot on the context and what they exactly asked. For example, is a highly concurrent context, you should have used Hashtable instead. Also, you should specify the types (String, Object) instead of (Object,Object) to get compiler support on the keys.
I think a better design would have been to implement the Map interface and keep the HashMap/HashTable as an internal parameter of the object.