I'm running into problems trying to put a public iterator in a class to read a Map in the class and implementing this iterator in other classes. Or in other terms,
I have class A. Class A contains a private HashMap that I want to access through a public iterator that iterates this map.
I also have class B. I'm trying to run this iterator in class B to read the contents of class A.
This may seem a bit roundabout (or rather, a lot roundabout), but my assignment specified that the data system in class A be hidden to other classes and suggested using a public iterator to access the data. There is an example of what the method in class B might look like, which I've followed to the best of my ability.
Can't get it to work, though.
Here's a mockup of the code I have. I tried compiling it, and it works exactly as my real code.
// this is class A
public class Giant {
private Map<Item, Integer> myMap = new HashMap<Item, Integer>();
// add a bunch of items to the map, check if they worked fine
public Iterator<Map.Entry<Item, Integer>> giantIterator = myMap.entrySet().iterator();
}
// and this is in class B
public void receive(Giant mouse){
System.out.println("I've started!");
Iterator<Map.Entry<Item, Integer>> foo = mouse.giantIterator;
while (foo.hasNext()) {
Map.Entry<Item, Integer> entry = foo.next();
System.out.println("I'm working!");
}
}
I also have a testclass which creates objects of either class and then runs the receive method.
I receive the message "I've started!" but not "I'm working!"
At the same time, if I have the two iterators in either class print a toString, the toStrings are identical.
I can't simply move the actions I'm supposed to do in class B to class A either, because the iterator is in several different methods and is used for slightly different things in each.
I'm a bit stumped. Am I missing something from the syntax? Am I importing something wrong? Have I just completely messed up how this is supposed to work? Is this just flat out impossible?
Try exposing the iterator through a function like this:
public class Giant {
private Map<Item, Integer> myMap = new HashMap<Item, Integer>();
public Iterator<Map.Entry<Item, Integer>> getGiantIterator() {
return myMap.entrySet().iterator();
}
}
And in class B change:
Iterator<Map.Entry<Item, Integer>> foo = mouse.giantIterator;
to
Iterator<Map.Entry<Item, Integer>> foo = mouse.getGiantIterator();
That way the iterator will not be created until it is needed.
The way you have coded it, the iterator is created while the map is still empty. I suspect this may be the root of your problem.
Your iterator is constructed when A is constructed, and at that time the Map is empty.
Make a method getIterator() that returns an up to date version.
If you have one iterator, and you want various classes to access it, then set that variable to "static".
Related
I have Singleton class like this:
public class Singleton
private static Singleton instance;
private ArrayList<Release> releases;
private ArrayList<Place> places;
ArrayList<ArrayList<Object>> list = new ArrayList<ArrayList<Object>>(2);
private Singleton() {
releases = new ArrayList<Release>();
places = new ArrayList<Place>();
list.add(release); //error, required AL<Object>, provided AL<Release>
list.add(places); //same
}
public static Singleton getInstance(){
/* Singleton code */
}
I thought that it is possible because, every Class extends Object class. My intention is to read from files where ALs are saved as object a then these ALs have in collection of one AL, where al.get(PLACES_INDEX) would return places and so on. It is a good approach or am I missing something?
Later on I would like to have some unified method, which would be something like:
public ArrayList<T> getArrayList() {
/*return places or releases based on <T>*/
}
I don't know if it's even possible since this class is Singleton.
I will explain why you're getting the error, but from what you describe this looks like a bad design for your class: don't store "generic" lists in a list, to access them based on a certain index. And don't create a method like public ArrayList<T> getArrayList() { that returns one of the lists depending on the type T. This is overengineering, and makes your code much harder to maintain, and easy to break.
Just keep the distinct lists separately, and provide getters for each one of them. If you are reading from a file and want to deserialize the content into a data structure, simply create a class structure that models the content. Your code will be much simpler and easier to read.
Even though Release is a subclass of Object, ArrayList<Release> is not a subclass of ArrayList<Object>, therefore you cannot add an ArrayList<Release> to an ArrayList<ArrayList<Object>> (we say that generics are not covariant). If Java allowed you to do that, then you can end up with a scenario that breaks generic usage of the code:
ArrayList<ArrayList<Object>> list = new ArrayList<>();
ArrayList<Release> releases = new ArrayList<>();
list.add(releases); // imagine this is allowed
ArrayList<Object> releasesFromList = list.get(0);
releasesFromList.add(place); // oops, added a place to list of release
I am using a heterogeneous container similar to this one. I can put and receive objects from the container with ease:
Favorites f = new Favorites();
f.putFavorite(String.class, "Java");
String someString = f.getFavorite(String.class);
But there seem to be no easy way to iterate over such container. I can add a keySet() method to the Favorites class and simply return the key set of the internal Map object:
public Set<Class<?>> keySet() {
return favorites.keySet();
}
Now, I would like to iterate over the keys, use the keys to get the associated values, and call some methods on the received objects:
for (Class<?> klass : f.keySet()) {
// f.getFavorite(klass).<SOME_METHOD_SPECIFIC_TO_THE_CLASS-KEY>
}
I thought that I could access the methods of the objects held in my container by calling klass.cast(f.getFavorite(klass)).SOME_METHOD(), but it doesn't work either (meaning, I cannot access any methods except for the Object-related methods).
Let's say, that in my use case I would like to inspect the interfaces of all these objects I iterate over and act accordingly to the detected interface. Let's also assume that I may have dozens of objects of various classes and all of them implement one of three interfaces.
The only solution I can think of is to stuff my code with dozens of isinstance checks, but I would prefer a less cumbersome approach (i.e. checking if a given object implements one of three interfaces).
By trying to call a specific method on each entry, you are basically saying that you know better than the compiler, and that you know each entry has a specific super class.
If you know that's the case, you can use Class#asSubclass to type klass as Class<? extends KnownSuper> so that getFavorite will then return a subclass of KnownSuper (and therefore expose the method):
Class<KnownSuper> superClass = KnownSuper.class; //class with callMethod()
for (Class<?> klass : f.keySet()) {
f.getFavorite(klass.asSubClass(superClass)).callMethod()
}
However, this will obviously give a runtime exception if one of the key classes does not extend KnownSuper. So if the above would be safe, you should parameterize your heterogeneous container to only accept key classes that extend from KnownSuper in the first place.
If not all entries will be of this type, you could also check first if the key is suitable when iterating:
Class<KnownSuper> superClass = KnownSuper.class; //class with callMethod()
for (Class<?> klass : f.keySet()) {
if (superClass.isAssignableFrom(klass)) {
f.getFavorite(klass.asSubClass(superClass)).callMethod()
}
}
Just go through this simple example
Say you have below Favorites class definition
public class Favorites extends HashMap<String, String> {
}
Here is the test class
public class TestGeneric {
public static void main(String[] args) {
Favorites f = new Favorites();
f.put("test", "test");
for (String test1 : f.keySet()) {
f.get("").charAt(index)// you will see all string specific method as compiler knows in advance what object map going to contain
}
}
The moment you change Favorites extends HashMap<String, String> to Favorites extends HashMap you will just object specific methods as compiler does not know in advance which object Favorites is going to put in map
class Node {
char val;
boolean wordEnd;
Map<Character, Node> map = new HashMap<>();
public Node() {}
public Node(char v) {
val = v;
}
}
In Java, when is map = new HashMap<>() being called?
I see I have 2 constructors here. But when is the "map" line being called? Before or after the constructor?
Also, why can we define map with new like this? In C++, I think it is not right.
I thought we should declare the map first, then in constructor, we new it.
when is map = new HashMap<>() being called?
All initializers are called in the order that they appear in the text of the class. All initializers complete before the constructor's code gets started, so you shouldn't be afraid that your map is still null when you are inside the constructor. See section 12.5 of JLS for details.
Also, why can we define map with new like this? In C++, I think it is not right.
C++ lacks this syntax, except for static initializers in the later versions of the standard. Even though Java and C++ borrow from common sources, they remain very different languages, with rather different philosophies behind them.
The structure is like this:
public interface ItemList{ }
public enum ItemList1 implements ItemList {
Apple,
Orange;
}
public enum ItemList2 implements ItemList {
Banana,
Grapes;
}
and 5 more such enums
The requirement is to use these enums as Keys in a Map, and in those Maps I have put key as:
public Class SomeClass {
private Map<ItemList, OtherObject> objectList;
//other code
}
The ItemList which goes into the map is decided on runtime. And I need to use a sorted Map like TreeMap for other operations.
So, the TreeMap is unable to compare the key enums obviously because I have declared them as ItemList supertype.
So, I searched other questions and did something like this so that enums could use their compareTo method:
public interface ItemList<SelfType extends ItemList<SelfType>> extends Comparable<SelfType>{ }
public enum ItemList1 implements ItemList<SelfType> {
//enum values
}
But this doesn't solve the problem. I am still getting the same "ClassCastException" when I tried to retrieve a TreeMap which had my enums as Keys.
Please suggest if I am doing anything wrongly here, or what can be an other way to solve this purpose?
EDIT:
Link to the solution which I followed, but it's not working:
How to implement an interface with an enum, where the interface extends Comparable?
EDIT 2
Problem identified. Sorry guys.
My map was getting populated with different types of enums as keys, when all the keys should belong to same type for sorting to work.
Well, you already found out that it was the actual content of the Map, not the declaration of the classes that caused the exception, but it’s worth noting that using collections comparing these enums is easier than you think.
E.g. TreeMap doesn’t care whether its declared Generic type has a comparable key or not. If you have class declarations like
public interface ItemList{ }
public enum ItemList1 implements ItemList { Apple, Orange }
public enum ItemList2 implements ItemList { Banana, Grapes }
you can simply use it as
TreeMap<ItemList,Object> tm=new TreeMap<>();
tm.put(ItemList1.Apple, "A");
tm.put(ItemList1.Orange, "O");
System.out.println(tm.get(ItemList1.Apple)+" is for "+ItemList1.Apple);
tm.clear();
tm.put(ItemList2.Banana, "B");
tm.put(ItemList2.Grapes, "G");
System.out.println(tm.get(ItemList2.Banana)+" is for "+ItemList2.Banana);
without problems; it even works if you declare the map as TreeMap<Object,Object>.
Note that some methods require comparable types when requesting natural order by not specifying a Comparator, e.g. with the type declarations above,
List<Object> list=Arrays.<Object>asList(ItemList1.Orange,ItemList1.Apple,ItemList1.Orange);
Collections.sort(list);
does not compile, however, you can easily circumvent it by requesting the natural order via a null Comparator:
Collections.sort(list, null); // compiles and works
So it’s not necessary to mess around with complicated type declarations like ItemList<SelfType extends ItemList<SelfType>> in most cases.
It sounds like what you should be doing is having
private Map<? extends ItemList, OtherObject> objectList;
...to indicate objectList is a specific map of some subtype of ItemList, and then
objectList = new TreeMap<ItemList1, OtherObject>();
or
objectList = new TreeMap<ItemList2, OtherObject>();
to declare it as some specific type of ItemList. (You may have to store it temporarily as a Map<ItemListN, OtherObject> while you populate it, but then you can put it in objectList.
I am having trouble with the syntax on an iterator I created. I would use a for-each loop, except I need to edit the elements that I would be pulling out of that loop. I created a class called Players which creates a List of 4 Hands(Hand is another class that represents an individual player) in a card game. I am trying to create an iterator for this class, which is giving me trouble. My implementation is as follows:
public class Players
{ ....
public class PlayerIterator implements Iterator<Hand>
{
private final Iterator<Hand> iterator;
private PlayerIterator()
{
this.iterator = players.iterator();
}
....
}
}
As far as I know, my implementation seems correct, based on the fact that I havent received any errors in eclipse. I am, however, having trouble declaring an iterator in the actual program. How would I declare an iterator like that?
Right now this is giving me an error:
Iterator<Hand> it = Players.PlayerIterator();
Hand: The class of variables I am iterating through: a list of 4 Hands
Players: The class that constructs the list of Hands and contains the iterator implementation
PlayerIterator: The class within the Players class that implements the iterator
Does this make sense? I have a very loose grasp on iterators, so if I have not provided enough information I could easily create more.
It is a bit difficult to tell from the code you have posted, but I think you might just be missing a new keyword. I assume you have something like the following outside of the Players class:
Players players = new Players();
where an instance of the Players class has some collection of Hands as one of its fields. In this case what you want to do is this:
Iterator<Hand> iterator = players.new PlayerIterator();
However, it also looks like you might have intended PlayerIterator to be a static class, which would be needed if the Players class has a static collection of Hands:
private static List<Hand> players = new ArrayList<Hand>();
Then you want to do this:
Iterator<Hand> iterator = new Players.PlayerIterator();
If you could post more of your code, it would be easier to guide you to the right solution. Another option might be to make Players implement Iterable<Hand>, which means you can directly loop over a Players instance in a for-each loop.
I don't see the point of creating a Hand iterator when your already have a list of Hands in your class. It's hard to understand what your code is trying to do, but if I understand your scenario correctly, the following should suffice:
public class Players implements Iterable<Hand> {
List<Hand> hands = ...
//...
#Override
public Iterator<Hand> iterator() {
return hands.iterator();
}
//...
}