Use the same object as both key and value in a HashMap - java

I've re-worded this search about as many times as I think I can and I've come up with nothing, so either this hasn't been asked before, or I don't know HOW to ask.
I am working on a personal project that boils down to trying to find the shortest path from a starting state to a finishing state.
There are too many (more than 2^64) states so I can't generate the entire graph. However, each state contains enough information to determine all the states adjacent to it. There are (infinitely) many paths from a state to another, and I am only interested in the shortest. This requires me to know if I've reached been to a state before, and also how I got there the first time.
My state object contains all the state information, as well as the depth of the path that lead me there, and the move I made to get there from the previous state in that path. If I get to the same state following a different path, the state information will be the same, but the depth and previous move fields will be different.
I want a data structure that will tell me if I have visited that state before, and if I have, retrieve the depth and previous state information from it.
The best solution I have come up with so far is to use a HashMap which maps a state to a state, and use the same state object as both the key and value, as in: myHashMap.put(myState, myState)
I've implemented hashCode() and equals() such that two states will be considered "equal" if their state information is the same (i.e. we've been in this room before) regardless of how I got there (i.e. which door I used to enter the room).
This seems rather silly but I can't think of another way (with fast storage/access) to store the information about whether I've been to a state and how I got there.
Does my plan make sense, or is there a better way?

You say that "I want a data structure that will tell me if I have visited that state before, and if I have, retrieve the depth and previous state information from it."
A Set (HashSet is likely better than a TreeSet for what you've described) will do the first part. Now, I see that you are trying to use a Map in order to do the second half which is retrieving the information. However, if you are already able to check if you've visited a state, that means you have a reference to the state. So, you don't need a map at all.
/* Marking a state as visited */
Set<State> visited = new HashSet<>();
visited.put(currentState);
/* Checking if visited/retrieving */
if (visited.contains(currentState)) {
// already visited
} else {
// do something with 'currentState'
}

If you see yourself storing key and value as the same value, then it is a Set what you really need.
Set<State> set = new HashSet<>();
set.put(stat1);
And by the way your solution is not very far from that as HashSet is backed by a HashMap behind the scenes which means a HashSet has the performance characteristic as those of HashMap and relies on equals and hashCode methods.

Related

How do I assign an identifier to a Java object

I have the following situation. In my Java back end I collect some data from a service, I create data objects out of it and pass that to my Angular front end. Then a user might choose do something with one of the objects and sends its changed state to the back end. The back end collects the same data again from the service and now I have to identify which object the user choose. I want to do this by creating a common identifier. And here comes my question: how can I best generate the identifier?
First I did the following:
Get data from service
Assign an increasing integer in for-loop
However, I ran into the following problem. The second time the service is called, the same data might return but in a different order. So my first thought was to order the collected data first and then assign the integer. But ordering this data is quite a pain so I thought of something else:
Take the two fields of the data object that makes it unique
Make a string out of those fields and concatenate
Do a .hashCode() on the result
This creates an int that can't be traced back to the original data. That's exactly what I want, but now for the funny part. The hashCodes can collide. My fear is that in very, very rare cases the hashCode will be the same, even though the original string was different!
Is my fear grounded? And how can I compose a better id?

Insert and Update in log(n) time with better performance

I am developing some financial algorithms using Java. I have a complex data structure with many properties that need to be updated during the life time of the algorithm. Sometimes this data structure is updated more than 1000 times ...
To improve the performance especially for get(search)/update/insert I decided to use TreeMap as a container which is quite efficient in that regard.
Now comes the challenging part. I need to update the data-structure properties for which I need to retrieve it from the container which requires:
check if container has the object
if yes, then get the object, else create new object and add to map
update the object if it is present in the container
This process takes THREE x log(n) i.e check, get and put. I want to do this in SINGLE log(n) time.
For that, my solution is:
I always add the object in the map (insert/update/get) using put. put returns the old object, I update the current object with the old values, which solves log(n) but different object lost reference to previous object because the new value is replaced in the map.
Is there any better solution or better container for updating the datastructure. I can use a List and use Binary Search of collections but for that I need to sort the datastructure again as list is not sorted.
Kindly Guide
I think you are doing pretty good.
O(k.log(n)) = O(log(n))
where k is a constant. So your time complexity is actually O(log(n))
You can achieve 1 and 2 in one hit if you switch to ConcurrentMap.computeIfAbsent(...). It returns the new/old object so you can update it.
If Java-7 then putIfAbsent but that requires an extra new - perhaps a bad thing if construction is expensive.
If you are not scared of having mutable objects around (which you seem to have given your proposed solution), you can do it with 1-2 operations. Instead of
1. contains()
2a. exists? get(), modify, put()
2b. doesn't exist? create, put()
you can just do
1. get()
2a. null? create put()
2b. not-null? modify object contents, as you already have reference
this way you have 1 search op for existing objects and 2 search ops for non-existing objects.
If you want to improve it further, you may want to use ConcurrentHashMap (after you get over your distrust of hashcodes ;) and putIfAbsent
1. old = putIfAbsent(createFresh())
2. old not null? update old
Said all that, I'm generally trying to avoid mutable objects for things longer than lifetime of single method. At some point you might want to multithread your processing and having mutable things is going to make it a lot more complicated. But there are various tradeoffs (like memory pressure), so it is up to you. But please look into hashmaps seriously, they are probably biggest optimalization you can do here, regardless of object (im)mutability.

Java issues with mantaining copied array list values

I'm new here, but I have searched for solutions to this issue for a couple hours and even though I kinda found some answers, but none of them actually worked.
I have an Array List called "parents" (of a node in this case) of the type "State" (8 queens arranged in a chess board). I am declaring it like this:
ArrayList<State> parents = new ArrayList<State>(3);
Likewise, I have a "children" array of states declared like this:
ArrayList<State> children = new ArrayList<State>(7);
I have to rearrange the state of the board so I don't have any queens in position to attack each other, using the Beam Search method. To do this, I have to copy the state of the parents to the children, randomly rearrange the queens in the children and check if the number of attacks in their states has lowered, rinse and repeat until I get 0 attacks (or the algorithm has gone on long enough without finding a better state than the last).
Finally, after this "contextualization" of the problem, let's go to the Issue per se.
Whenever I change the position of a queen in a child state, it is also changed in the parent state, even though I tried a lot of workarounds to passing an object from an ArrayList to another without copying the references. Right now, I'm doing it like this:
ArrayList <State> buffer = new ArrayList<State>(parents);
children.add(buffer.get(m)); //the m here is just so I can choose in a for witch parent will be copied to the children array
And then I am changing the queen values like this (not exactly this, but the actual code would require more contextualization and I would digress from the issue even more:
children.get(k).board[queen]=k;//basically changing the "Y" position of the queen to a "k" value that comes from a loop.
When I change the value of the queen in the children state like this, the parent state is also changed, and this is what I can't for the life of me fix, and I did try using some answers found here before I made this post, like using the clone method, like this:
buffer=(ArrayList<State>) parents.clone();
or the Collections.copy method:
ArrayList<State> buffer = new ArrayList<State>(3);
Collections.copy(buffer, parents);//actually this one gives me an out of bounds error, stating that the source does not fit in the destiny
and some others, but all to no avail, the parent values still get modified.
Does anyone have an Idea of what can be done to fix this? Or am I approaching the problem in a way that it can't be fixed? I'm also kinda new to Java so it is possible that I missed something in one of the solutions I've previously found, so if anyone out there is really sure that one of those methods have to work in my case, by all means, explain it and I will try to use it again.
(also, I tried to "build" the post in the better possible way, if anyone have any suggestion on how to formulate my questions in the future, they are also welcome)
Cheers :)

Find and delete specific objects from a Set with minimal iteration using Java

I have a set of objects and each object has its own id...
I also have a list of ids, each denoting the id of an object to be removed from the set..
Unfortunately I need the most efficient approach to do this... Obviously, I could iterate through the set, and for each entry iterate through the list to see if the ids match...
But is there a quicker way? I was wondering if it might be faster to use a Map to map ids to each object's placement within the set? It would be more to keep track of, but the efficiency here is the top priority - the set itself is dynamic and this operation will occur often...
The situation is essentially that I have a server thread that is closing sockets that another thread has determined are idle, and the server thread needs to get this done as quickly as possible, so it can resume its normal duties... Unfortunately the server thread is the only thread allowed to close sockets to avoid concurrency issues...
Are there better ways to do this?
I would go with #HovercraftFullOfEels' comment.
To expand a bit:
Replace your Set with a HashMap. Use the ID of the object as the key, and the object itself as the value. Then whenever you need to remove an object, it's a simple matter of
map.remove(id);
Generally speaking, any time you need random access to any form of Collection you're probably better off using some variation of Map instead (HashMap being the most common)

When an object has multiple internal collections, how can i iterate over them from the outside?

a while ago i wrote an editor for a navigation graph that represented the paths inside (and between) buildings. it was stored inside a Graph-class.
the edges, for example were stored in one collection per floor, plus one collection for the ones that were between floors.
to draw them (only the current floor) or save them to disk (all of them), i needed to get at them from the outside. for that i implemented methods like callWithAllEdges and callWithAllEdgesIn, the latter taking a parameter to specify a floor.
those methods took a functor (at least i called it that), that was then called with the edges.
this is what drawing the edges of one floor looked like:
graph.callWithAllEdgesIn(id, new Functor<Edge>() {
public void call(Edge e) {
drawEdge(g,e);
}
});
this is a bit long-winded, of course. might be a problem with java and not with my solution though, i dont know.
another way would have been to just make a method that puts references to all the needed edges into a new collection and then iterate over that, i guess. seemed kind of wrong to me though.
my question is: how could i have solved that better?
Your current design is perfectly reasonable. Another option is to provide an Iterable/Iterator. That way you don't need to copy everything into a new list, but can instead lazily step through your internal lists.

Categories

Resources