I need to create a Hallway class which will have inside 2 ArrayLists of Stand objects , one for the stands on the right, and the other for the ones on the left.
My intention is to put these ArrayLists inside another collection in this class.
I don't know if I should use a Hashtable, a Map, etc.
More importantly, my intention is to then access these ArrayLists using a method like:
TheHashTable["Right"].add(standObject); // Add a Stand to the Right Stands ArrayList which is inside a Hashtable.
Example:
public class Hallway {
private Hashtable< String, ArrayList<<Stand> > stands;
Hallway(){
// Create 2 ArrayList<Stand>)
this.stands.put("Right", RightStands);
this.stands.put("Left", LeftStands);
}
public void addStand(Stand s){
this.stands["Right"].add(s);
}
}
Would this be possible?
It is possible, but I would advise against it. If you have only two stand locations, it would be much and clearer to simply have two variables of type List<Stand>: leftStands and rightStands, and to have corresponding methods: addLeftStand(Stand), addRightStand(Stand), etc. The code would be much clearer, simpler and safer.
If you really want to go your way, the keys of the map shouldn't be Strings. The caller wouldn't know which key to pass to your methods (there are an infinity of Strings), and ven if he knows that the keys are "Right" and "Left", he could make a typo which would go unnoticed by the compiler. You should use an enum instead, which would make the code self-documented and safer:
public enum Location {
LEFT, RIGHT
}
private Map<Location, List<Stand>> stands = new HashMap<Location, List<Stand>>();
public Hallway() {
for (Location location : Location.values()) {
stands.put(location, new ArrayList<Stand>());
}
}
public void addStand(Location location, Stand stand) {
stands.get(location).add(stand);
}
if you only have right and left, you could for example just create 2 array lists.
private ArrayList<Stand> rightStands;
private ArrayList<Stand> leftStands;
If I understood your question clearly, then this is what you want:
public void addStand(Stand s){
this.stand.get("Right").add(s);
}
But a better approach would be to use Map instead of Hashtable.
public class Hallway {
private Map< String, ArrayList<<Stand> > stands;
private List<Stand> RightStands;
private List<Stand> LeftStands;
Hallway(){
stands = new HashMap();
RightStands = new ArrayList();
LeftStands = new ArrayList();
this.stands.put("Right", RightStands);
this.stands.put("Left", LeftStands);
}
public void addStand(Stand s){
this.stands.get("Right").add(s);
}
}
You need a multi-map, for example from Commons Collections or Guava.
Those will let you map multiple values (Stand1, Stand2, ...) to a single key (e.g. "right").
For example (with Commons Collections):
MultiMap stands = new MultiHashMap();
stands.put("left", new Stand());
stands.put("left", new Stand());
stands.put("right", new Stand());
stands.put("right", new Stand());
stands.put("right", new Stand());
Collection standsOnLeftSide = (Collection) stands.get("left");
I think though that Guava is preferrable because it supports Generics.
Don't use HashTable. It has been deprecated long back. Use TreeMap or HashMap.
List<Stand> right=new ArrayList<Stand>(),left=new ArrayList<Stand>();
Map<String,List<Stand> > stands= new HashMap<String, List<Stand> >();
stands.put("right",right);
stands.put("left",left);
To learn about maps and to decide which Map suits you best read Precisely Concise: Java Maps
Related
I am trying to create a method that, when given a key and a value, can access the map via the key and then either add or replace that specific value.
My Hash Map is created like so:
public Band(){
musicians = new HashMap<>();
}
And I can add new entries like so, with band acting as the key:
public void addMapEntry(String band, String name, String instrument, int experience){
musicians.put(band, new Musician(name, instrument, experience));
}
My new method header look like this:
public void addValue(){ }
I have tried using the put method but I can't get it work as I'd like.
I have also tried iterating through the map, but I've only ever used that method to return map objects in a list, whereas I don't want to return anything in this method. I want to be able to send it two arguments (the key and the value) and have it only replace that specific value.
I hope I have been clear in my explanation.
Java Map is single value for each key.
If you need multiple values for a single key, you should make the type a collection of the appropriate type and add your own logic for adding a new value. Your Band class should have methods to add/remove a Musician and handle the details in the private implementation.
public class Band {
private Map<String, List<Musician>> members = new HashMap<String, List<Musician>>();
public void addMusician(String key, Musician musician) {
if (this.members.containsKey(key) {
List<Musician> musicians = this.members.get(key);
if (musician != null) {
musicians.add(musician);
this.members.put(key, musicians);
}
}
}
public void removeMusician(String key, Musician musician) {
// should be clear enough from the add method.
}
}
I think the most suitable for you is to use Guava Multimap
ListMultimap<String, String> musicianMap = ArrayListMultimap.create();
Then add as many musicians to your band
musicianMap.put("Beatles", new Musician("Jhon Lennon"));
musicianMap.put("Beatles", new Musician("Paul McCartney"));
musicianMap.put("Beatles", new Musician("Ringo Starr"));
musicianMap.put("Beatles", new Musician("George Harrison"));
And you can pull them all using just key.
musicianMap.get("Beatles")
This will return a list of ["John Lenon", "Paul McCartney", "Ringo Starr"] of course these will objects of class Musician.
I have two maps:
Map<Date, List<Journey>> journeyMap = new TreeMap<Date, List<Journey>>
Map<Date, List<Job>> jobMap = new TreeMap<Date, List<Job>>
I used TreeMap because that means they're sorted by date but I want to go through both maps at the same time, get the values of Journey/Job, then do some work.
I think i could use generics, storing the Job/Journey as an Object, then checking the instanceOf but I'm not sure if thats the solution?
Thanks.
Even though the others are right, that there are better, safer and more comfortable ways to achive whatever you want, it is possible to iterate over (the entries of) two Maps (aka Collections) at the same time.
//replace keySet() with your favorite method in for-each-loops
Iterator<Date> journeyIterator = journeyMap.keySet().iterator()
Iterator<Date> jobIterator = jobMap.keySet().iterator();
while(journeyIterator.hasNext() && jobIterator.hasNext()){
Date journeyDate = journeyIter.next()
Date jobDate = jobIterator.next();
//... do whatever you want with the data
}
This code does explicitly, what a for-each-loop can do implicitly for one Collection. It retrieves the Iterator and gets the element from the Collection from it, much like reading a file.
You're making an assumption that these maps are having values sorted in the very same way, but this is definitely not correct. At least if you want to write a logic like this you need to declare the same implementing class as a reference:
TreeMap<Date, List<Journey>> journeyMap = new TreeMap<Date, List<Journey>>
TreeMap<Date, List<Job>> jobMap = new TreeMap<Date, List<Job>>
but believe me you don't want to do it.
You're right! Instead doing 2 maps create 1, holding pair of Job/Journey objects - create a JobJourneyHolder class which holds both objects, this will be a good solution.
Yes, defining a new class for that is definitely the solution, because it composes related objects together, which is very welcomed in OOP. And you should not forget to implement hashCode() and equals() methods to make such classes work properly in Java collections:
public final class JourneyJob {
final Journey journey;
final Job job;
public JourneyJob(Journey journey, Job job) {
if (journey == null || job == null)
throw new NullPointerException();
this.journey = journey;
this.job = job;
}
public int hashCode() {
return Objects.hash(journey, job);
}
public boolean equals(JourneyJob other) {
return other.job.equals(job) && other.journey.equals(journey);
}
}
To add elements to common Map:
Map<Date, List<JourneyJob>> map = new TreeMap<>();
...
if (map.contains(date)) {
map.get(date).add(new JourneyJob(journey, job));
} else {
map.put(date, new ArrayList<>(Arrays.asList(new JourneyJob(journey, job)));
}
...
To retrieve JourneyJob objects:
for (List<JourneyJob> jjList : map.values()) {
for (JourneyJob jj : jjList) {
journey = jj.journey;
job = jj.job;
//... do your work here
}
}
Or, if you use Java 8, this can be done using nested forEach():
map.values().stream().forEach(list ->
list.stream().forEach(jj -> {
Journey journey = jj.journey;
Job job = jj.job;
//... do your work here
})
);
EDIT
I've tried this HashMap with multiple values under the same key, and my hashMap now looks like this HashMap<String, List<Place>> placeMap = new HashMap<>();
Also tried to put Object instead of Place(place is my superclass). But when I now create my subclasses and wants to add them to the HashMap I get:
The method put(String, List) in the type HashMap<String,List<Place>> is not applicable for the arguments (String, NamedPlace)
and
The method put(String, List) in the type HashMap<String,List<Place>> is not applicable for the arguments (String, DescPlace)
here is my adding which created the error:
NamedPlace p = new NamedPlace(x,y,answer,col,cat);
placeMap.put(answer, p);
DescPlace dp = new DescPlace(x,y,answer, desc, col, cat);
mp.add(dp);
placeMap.put(answer, dp);
NamedPlace and DescPlace are both subclasses to Place, and I want them both in the same HashMap..
OP
I'm working on a little project here. The thing is that I need to use a HashMap instead of a ArrayList on this part of the project because HashMap is alot faster for searching. I've created a HashMap like this:
HashMap<String, Object> placeMap = new HashMap<>();
The String is the name of the Object, but the thing is that more than one object can have the same name. So I search for a object in my searchfield and I want to store all those objects that has that name into an ArrayList so I can change info in just them.
The object have alot of different values, like name, position, some booleans etc.
Do I need to create a HashCode method into my object class which shall create a unique hashcode?
When using a standard Map<String, List<YourClassHere>> instance, it is important to remember that the map's values for each entry will be a List<YourClassHere>, and will not handle it in any special way. So in your case, if you have
private Map<String, List<Place>> placeMap = new HashMap<>();
Then to store values you will need to do as follows:
NamedPlace p = new NamedPlace(x,y,answer,col,cat);
List<Place> list = placeMap.get (answer);
list.add(p);
However, this piece of code has some underlying problems.
It doesn't take into account that answer might not be present in placeMap.
It assumes that there's always a List<Place> instance for each key you query.
So the best way to fix those potential problems is to do as follows (Java 7 and later):
NamedPlace p = new NamedPlace(x,y,answer,col,cat);
if (placeMap.containsKey (answer) && placeMap.get (answer) != null) {
placeMap.get (answer).add(p);
} else {
List<Place> list = new ArrayList<Place> (); // ..or whatever List implementation you need
list.add (p);
placeMap.put (answer, list);
}
If you want to scna through the list of places, the code would look like this:
if (placeMap.containsKey (key) && placeMap.get (answer) != null) {
for (Place p: placeMap.get (key)) {
// Do stuff
}
}
This question already has answers here:
Sort a Map<Key, Value> by values
(64 answers)
Closed 8 years ago.
I saw this thread sorting a List of Map<String, String> and I know mine could sound a duplicate, but it is slight differen.
My example is:
List<Map<String, String>> myList = new ArrayList<Map<String, String>>();
...
for(MyClass1 c1 : c1)
{
...
for(MyClass2 c2 : c12)
{
SimpleBindings myBindindings= new SimpleBindings();
myBindindings.put(c1.getName(), c2.getName());
myList.add(myBindindings);
}
}
...
Concretely I can have
{
(John, Mike)
(John, Jack)
(Sam, Jack)
(Gloria, Anna)
(Jane, Carla)
...
}
and would like that my list is sorted by the maps key:
{
(Gloria, Anna)
(Jane, Carla)
(John, Mike)
(John, Jack)
(Sam, Jack)
...
}
Are you sure that
List<Map<String, String>>
is the approriate data type you want?
To me it looks like you are in fact looking simplify for
TreeMap<String, String>
i.e. a sorted map key -> value?
Or do you mean to use a List<StringPair> (for that, please choose a more appropriate name than StringPair, and implement that class to your needs)? I have the impression that in lack of an obvious Pair<String, String> class in Java you have been abusing SimpleBinding as a pair class. The proper way to have pairs in Java is to implement a new class, with a proper class name - "pair" is technical, not semantic.
You could also do
List<String[]>
and implement a Comparator<String[]> for sorting. But that doesn't save you any work over implementing a NamePair class and making it comparable yourself.
You need to implement Comparator to accomplish this...
Collections.sort(myList, new Comparator<ObjectBeingCompared>() {
#Override
public int compare(ObjectBeingCompared obj1, ObjectBeingCompared obj2) {
//Of course you will want to return 1, 0, -1 based on whatever you like
//this is just a simple example
//return 1 if obj1 should be ordered first
//return 0 if obj1 and obj2 are the same
//return -1 if obj1 should be ordered after obj2
return obj1.compareTo(obj2);
}
});
The HashMap data structure is used to allow access to its elements in O(1) time.
Because it is a container of data its pool or keys can vary in time. This mean that you can not assure in long therm an order for list of maps.
In your example you match two Strings and create Pair of data called SimpleBindings.
In case of your simple example you should not use Map<String,String> data structure to represent a Pair of data.
If you SimpleBindings really consist of two string, everything you must do is only implement a Comparable in SimpleBindings class like this:
class SimpleBinding implements Comparable<SimpleBinding> {
private final String key;
private final String value;
public SimpleBinding(String key, String value) {
Objects.nonNull(key);
Objects.nonNull(value);
this.key = key;
this.value = value;
}
#Override
public int compareTo(SimpleBinding that) {
return this.key.compareTo(that.key);
}
}
And the you just use the Collections.sort(bindings ) to have sorted result.
In case you do not have access to the class you should use the Comparator interface like this
enum SimpleBindingComparator implements Comparator<SimpleBinding> {
DEFUALT {
#Override
public int compare(SimpleBinding fist, SimpleBinding second) {
return fist.key.compareTo(second.key);
}
};
Then you sort your bindings like this Collections.sort(bindings ,SimpleBindingComparator.DEFAULT);
But if your case is more complex than this and your store a Map in the list you should define a logic that represent the order. In your case it can be sad that the order must maintained by c1.getName()
One choice is that you should not create a List but a map of list Map<String>,List<String>> this is so called multi map where a single key matches to multiple values. See MultiMap of guava and if you want it to be sorted then i propose to read about TreeMultiMap
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Maps with multiple types of values in java
I have an odd question. Maybe I'm going about this the wrong way, but let's see where this question goes :)
I would like a Map container that contains either Strings or lists of Strings. I want to enforce this rule during construction of this object so that you can't create a map with values that aren't either of those.
e.g.
class Record {
public Record(String key, Map<String,Object> attrs) {
// check that attrs only contains Objects which are Strings or List<Strings>
}
}
Other ways I have thought of to solve the problem might be...
1)
class Record {
public Record(String key, Map<String,String> attrs, Map<String,List<String>> multiAttrs) {
// ...
}
}
2)
class Record {
public Record(String key, Map<String,Value> attrs) {
// ...
}
}
class Value {
// Create some funky class that encapsulates lists.
// Perhaps returning the only element in the list if the size is 1,
// but returning the list otherwise
}
I am not immediately excited at the alternatives, but I'm just putting it there as stuff I've already considered. Really I want the distinction between Strings and List to be transparent to the user of the class.
Have you considered ListMultimap? For the single value case the list would only have one element. Multimap allows multiple elements (values) to be mapped to each key. So your method would be:
public Record(String key, ListMultimap<String, String> attrs)...
Also, since your Record seems to be another mapping, consider using Table which allows for two-key mapping.
Check out ArrayListMultimap from Google which will help with this need
You can continue calling put on this map, if you need to get the map in its simplified form you can use this method, or modify it :)
public static Map<Field, String> toSingularMap(ArrayListMultimap<Field, String> map) {
Map<Field, String> singular_map = new HashMap<Field, String>();
if (map != null && !map.isEmpty()) {
Map<Field, Collection<String>> real_map = map.asMap();
for (Iterator<Entry<Field, Collection<String>>> it = real_map
.entrySet().iterator(); it.hasNext();) {
Entry<Field, Collection<String>> entry = it.next();
Field field = entry.getKey();
Collection<String> values = entry.getValue();
String value = null;
if (values != null && !values.isEmpty()) {
ArrayList<String> list = new ArrayList<String>(values);
value = list.get(0);
}
singular_map.put(field, value);
}
}
return singular_map;
}
Or if you do not want to use an extra library, you can create a simple Wrapper class
class Wrap {
String value;
String[] values
}
and have your map use Map<String, Wrap> map, when looping you can then determine either through use of your class methods or just testing, which one of the Wrapper variables are populated
I would use only List<String>. You could maybe add some methods to allow adding a single String and wrap the passed argument using Arrays.asList(...). Using only a single type of objects will reduce the quantity of code to write and avoid many if/else.
Why not create a class
class MyFunkyValue{
private String onlyOneString;
private List<String> stringValues;
public MyFunkyValue(String s){
...
}
public MyFunkyValue(List<String>ls){
...
}
}
and use it like this:
Map<KeyClass,MyFunkyValue> m;