I am working on website with games and I have map of players and their virtual tables.
private final ConcurrentMap<Player, List<Table>> tableOfPlayers = new ConcurrentHashMap<>();
and method to remove table
private void removeTable(Player player,Table table) {
if(tableOfPlayers.get(player).size() == 1) {
tableOfPlayers.remove(player);
} else {
tableOfPlayers.get(player).remove(table);
}
}
Is there any good way to solve this check-then-act idiom, because now it isn't thread-safe.
I know that I can synchronize both add and remove method, but I am wondering if it is possible to make it better. The reason why I check if size is equal to 1 is that if player have only one active table and I decide to remove I no longer need this player in my map.
Related
I'm trying to find if multiple HashMaps are empty.
To give some context. I have a hashmap declared here.
static Map<Integer, College> tblColleges = new HashMap<Integer, College>();
For each college object:
Map<Integer, Department> tblDepartments = new HashMap<Integer, Department>();
I'm trying to add a major. Majors can only exist as an attribute of Department.
Here's what I have right now.
int numberofColleges = Databases.tblColleges.size();
int emptyColleges = 0;
for(int key: Databases.tblColleges.keySet()) {
if(Databases.getTblColleges(key).tblDepartments.isEmpty()) {
emptyColleges++;
}
}
if(numberofColleges == emptyColleges) {
System.out.println("Invalid. Requires at least 1 department.");
}
I should only be able to create a Major if at least 1 college has a department.
Essentially for each college object that exists in the tblColleges, I'm checking to see if it's department hashmap is empty. If it is empty, then I increment the number of empty colleges.
Afterward, I compare the number of college objects with empty college objects found, if they are equal then I print an error.
I was wondering if there was a better more efficient way to do this, maybe with some function that exists that I'm not familiar with rather than using variables.
Q: Can you do the check "more efficiently"?
A: You could optimize it a bit:
boolean nonEmptyColleges = false;
for (int key: Databases.tblColleges.keySet()) {
if (!Databases.getTblColleges(key).tblDepartments.isEmpty()) {
nonEmptyColleges = true;
break;
}
}
The above short circuits as soon as it finds a College with a Department. That will be a substantial improvement in a lot of cases.
Then, assuming that Databases.tblColleges is a Map:
boolean nonEmptyColleges = false;
for (int college: Databases.tblColleges.values()) {
if (!college.tblDepartments.isEmpty()) {
nonEmptyColleges = true;
break;
}
}
Q: Can you do the check with less code?
A: Using Java 8 streams you could write the last as:
boolean nonEmptyColleges = Databases.tblColleges.values().stream()
.anyMatch(c -> !c.tblDepartments.isEmpty());
(I think ...)
Q: But is this the right approach?
A: IMO, no.
It seems that you intend to do this check each time you add a major. That's not necessary.
Majors can only exist as an attribute of Department.
The key thing that you need to check is that the Department you want to add the major for exists.
If the Department doesn't exist you can't add the major to it.
If the Department does exist you can the major to it, whether or not it is currently a department of a college1.
The bigger point here is that any data model is going to have a variety of data integrity rules / constraints on it. But that does mean that you need to explicitly check all of them each time the model is changed. You only need to check the preconditions for the change (e.g. that the Department exists) and any constraints that could be invalidated by the change.
1 - The "not" case assumes that there may be some other way of finding a Department. It could be a separate table of Department objects, or it could be that you are in the process of creating and building a new Department and haven't added it to its College yet.
I am working on a Spigot 1.8.9 plugin and am trying to add a feature when a staff right-clicks an item it teleports them to the next player that isn't in vanish and not themselves and if there aren't any it should return null.
On click I attempted to add all possible users to a list using
public static List<User> getPossibleUsers(User user){
List<User> result = new ArrayList<>();
for(User target : users)
if(!target.isVanished() && !user.getUUID().equals(target.getUUID()))
result.add(target);
return result;
}
The staff is also assigned an int called nextPlayer which is set to 0 when they login. Then when they click I add one to the int so next time they click it can get the next user.
private User getNextPlayer(User user) {
int next = user.nextPlayer;
List<User> users = getPossibleUsers(user);
if(users.size() == 0)
return null;
int current = 0;
for(User target : users) {
if(current == next){
return target;
}
current++;
}
user.nextPlayer = next;
}
The problem is I don't know how to make the getNextPlayer method correctly and make it efficient. I also would like to also to make it so once it hits the last player it loops back to the first player.
I'd suggest thinking about your problem entirely differently if you want it to be efficient, but efficiency really isn't a concern in this situation, so I'm opting to not pre-maturely optimize and instead work with the code you already have.
public static List<User> getPossibleUsers(User user){
List<User> result = new ArrayList<>();
for(User target : users)
if(!target.isVanished() && !user.getUUID().equals(target.getUUID()))
result.add(target);
return result;
}
This currently returns the Users in the same order, as they are defined on users.
This better have a natural sort order, otherwise you are going to have issues when people join / leave the server, as it will cause people to change their ordering in the list.
Now let's get back to first principals.
int next = user.nextPlayer;
Looks like you are storing the index of the player in the list you have already been in on the 'user'.
Once you have this, you can access that index directly from the list.
https://docs.oracle.com/javase/8/docs/api/java/util/List.html#get-int-
E get(int index)
So, doing users.get(next++); is all you need to do to 'fix' the code you have above. it increments next, and gets the user at that position (assuming the ordering is consistent, and hasn't changed) However, it may throw an exception if it's out of range of the list, so we wrap it in
if(next <= users.length) {
users.get(next++);
} else return null;
This will change it to returning null, if it would otherwise throw an exception.
BUT all of this still has a fatal flaw, that if the list is mutated between calls, that you could be potentially skipping or changing the order around.
A far better solution to this, is to instead cache the visited users, as well as the last visited user.
If the users are ordered, and you store the last visited user, instead of the index, you are storing data that is much more resilient to change, and more closely matches the behavior you want.
To more closely match your needs, you are asking that.
Generate a predictable, ordered list of users that don't include the admin, or anyone else that is vanished, to aid the admin in predicting where they are going.
Rotate through this list, by right clicking with a tool, (Note this is async, so all the state needs to be saved)
Ensure that all visited users are visited before repeating the sequence.
public class TeleportTooldata {
private ListIterator<UUID> cursor;
private List<UUID> cachedOrder;
public TeleportTooldata(List<UUID> applicableUsers) {
cachedOrder = applicableUsers;
}
#Nullable
public UUID next() {
if (!cursor.hasNext()) return null;
UUID next = cursor.next();
if (!cachedOrder.contains(next)) {
cachedOrder.add(next);
}
return next;
}
public void Update(List<UUID> applicableUsers) {
applicableUsers.removeAll(cachedOrder);
cachedOrder.addAll(applicableUsers);
}
}
public class TeleportToolUtil {
YourPluginUserRepo repo;
Map<User, TeleportTooldata> storage; //This could be a cache, make sure to remove if they log out, or maybe timed as well.
public List<UUID> getApplicableUsers() {
return repo.getOnlineUsers().stream()
.filter(User::isVanish)
.sorted(Comparator.comparing(User::getId)) // You can change the sort order
.map(User::getId)
.collect(Collectors.toList());
}
public void onToolUse(User user) {
TeleportTooldata data = storage.computeIfAbsent(user, x -> new TeleportTooldata(getApplicableUsers()));
UUID next = data.next();
if (next == null) {
data.Update(getApplicableUsers());
next = data.next();
if(next == null) {
storage.put(user, new TeleportTooldata(getApplicableUsers()));
next = data.next();
}
}
user.teleportTo(next);
}
}
A few changes.
We are now caching the ordering, so that you could conceptually also let the user go backwards through the list.
We are using ListIterator. ListIterator is an object that loops through lists, and stores the current position for you! Much like you were doing before, but without indexes.
We now have the possibility to update the data, in case a player joins late, or someone unvanishes they will be put at the back of the list if they are not already inside it.
when we run out of users, we attempt an update, if we are really out, we start again with a brand new list. (note this won't guarantee the same order every time (people will be 'properly' sorted when it updates if they were previously appended, but it's close enough for this usecase)
However! We still need to be mindful of memory leaks. using UUID's rather then players or users, means this class is very light weight, we should be pretty safe from memory leaks in the list of UUID AS LONG as the TeleportTooldata doesn't live too long.
You can replace the Map of TeleportTooldata with a cache (maybe from Guava?) to remove the data some time after the admin leaves the game.
If TeleportTooldata was expected to be long-lived, we would want to seriously consider removing UUID's from the history.
Also, not handled in my example, is the possibility of the users going offline after the order is cached.
To handle this, before teleporting the player, check if the uuid is online, otherwise go to the 'next' and follow all the same logic again.
I have a cache refresh logic and want to make sure that it's thread-safe and correct way to do it.
public class Test {
Set<Integer> cache = Sets.newConcurrentHashSet();
public boolean contain(int num) {
return cache.contains(num);
}
public void refresh() {
cache.clear();
cache.addAll(getNums());
}
}
So I have a background thread refreshing cache - periodically call refresh. And multiple threads are calling contain at the same time. I was trying to avoid having synchronized in the methods signature because refresh could take some time (imagine that getNum makes network calls and parsing huge data) then contain would be blocked.
I think this code is not good enough because if contain called in between clear and addAll then contain always returns false.
What is the best way to achieve cache refreshing without impacting significant latency to contain call?
Best way would be to use functional programming paradigm whereby you have immutable state (in this case a Set), instead of adding and removing elements to that set you create an entirely new Set every time you want to add or remove elements. This is in Java9.
It can be a bit awkward or infeasible however to achieve this method for legacy code. So instead what you could do is have 2 Sets 1 which has the get method on it which is volatile, and then this is assigned a new instance in the refresh method.
public class Test {
volatile Set<Integer> cache = new HashSet<>();
public boolean contain(int num) {
return cache.contains(num);
}
public void refresh() {
Set<Integer> privateCache = new HashSet<>();
privateCache.addAll(getNums());
cache = privateCache;
}
}
Edit We don't want or need a ConcurrentHashSet, that is if you want to add and remove elements to a collection at the same time, which in my opinion is a pretty useless thing to do. But you want to switch the old Set with a new one, which is why you just need a volatile variable to make sure you can't read and edit the cache at the same time.
But as I mentioned in my answer at the start is that if you never modify collections, but instead make new ones each time you want to update a collection (note that this is a very cheap operation as internally the old set is reused in the operation). This way you never need to worry about concurrency, as there is no shared state between threads.
How would you make sure your cache doesn't contain invalid entries when calling contains?? Furthermore, you'd need to call refresh every time getNums() changes, which is pretty inefficient. It would be best if you make sure you control your changes to getNums() and then update cache accordingly. The cache might look like:
public class MyCache {
final ConcurrentHashMap<Integer, Boolean> cache = new ConcurrentHashMap<>(); //it's a ConcurrentHashMap to be able to use putIfAbsent
public boolean contains(Integer num) {
return cache.contains(num);
}
public void add(Integer nums) {
cache.putIfAbsent(num, true);
}
public clear(){
cache.clear();
}
public remove(Integer num) {
cache.remove(num);
}
}
Update
As #schmosel made me realize, mine was a wasted effort: it is in fact enough to initialize a complete new HashSet<> with your values in the refresh method. Assuming of course that the cache is marked with volatile. In short #Snickers3192's answer, points out what you seek.
Old answer
You can also use a slightly different system.
Keep two Set<Integer>, one of which will always be empty. When you refresh the cache, you can asynchronously re-initialize the second one and then just switch the pointers. Other threads accessing the cache won't see any particular overhead in this.
From an external point of view, they will always be accessing the same cache.
private volatile int currentCache; // 0 or 1
private final Set<Integer> caches[] = new HashSet[2]; // use two caches; either one will always be empty, so not much memory consumed
private volatile Set<Integer> cachePointer = null; // just a pointer to the current cache, must be volatile
// initialize
{
this.caches[0] = new HashSet<>(0);
this.caches[1] = new HashSet<>(0);
this.currentCache = 0;
this.cachePointer = caches[this.currentCache]; // point to cache one from the beginning
}
Your refresh method may look like this:
public void refresh() {
// store current cache pointer
final int previousCache = this.currentCache;
final int nextCache = getNextPointer();
// you can easily compute it asynchronously
// in the meantime, external threads will still access the normal cache
CompletableFuture.runAsync( () -> {
// fill the unused cache
caches[nextCache].addAll(getNums());
// then switch the pointer to the just-filled cache
// from this point on, threads are accessing the new cache
switchCachePointer();
// empty the other cache still on the async thread
caches[previousCache].clear();
});
}
where the utility methods are:
public boolean contains(final int num) {
return this.cachePointer.contains(num);
}
private int getNextPointer() {
return ( this.currentCache + 1 ) % this.caches.length;
}
private void switchCachePointer() {
// make cachePointer point to a new cache
this.currentCache = this.getNextPointer();
this.cachePointer = caches[this.currentCache];
}
i'm getting a concurrentmodification exception, the problem is that i'm iterating through an arrayList and drawing and updating the sprites in the arraylist, while adding new sprites in another java class.
public abstract class Scene
{
ArrayList<UIElement> uiElements = new ArrayList<>();
ArrayList<GameObject> sprites = new ArrayList<>();
public ArrayList<GameObject> getSprites()
{
return sprites;
}
public ArrayList<UIElement> getUiElements()
{
return uiElements;
}
public abstract void load();
public abstract void unload();
public void update()
{
sprites.forEach(GameObject::update);
uiElements.forEach(UIElement::update);
}
public void draw(Graphics2D g2)
{
sprites.forEach(e -> e.draw(g2));
uiElements.forEach(e -> e.draw(g2));
}
}
so these are the iterating methods in the java class named Scene.java
public void onButtonsEvent(WiimoteButtonsEvent wiimoteButtonsEvent)
{
// shoot firebolt when button b is held
if( wiimoteButtonsEvent.isButtonBHeld())
{
if(updateIndexTwo % UPDATE_SPEED == 0)
{
Vector2D fireboltVector = new Vector2D(vector.getX()+SPRITE_WIDTH/2, vector.getY(), crossHair.getX() + CH_SIZE/2, crossHair.getY() + CH_SIZE/2);
Firebolt firebolt = new Firebolt(fireboltVector, damage, color, scene);
scene.getSprites().add(firebolt);
updateIndexTwo = 0;
}
updateIndexTwo++;
}
}
this is the code where the Firebolt(sprite) will be added to the sprites arraylist in Scene
i'm getting a concurrentmodification exception, the problem is that i'm iterating through an arrayList and drawing and updating the sprites in the arraylist, while adding new sprites in another java class.
The basic solution is fairly simple: don't do that. It is an intentional design feature that if an ArrayList is structurally modified, then any iterators over that list that were obtained prior to the modification are invalidated; further use of them causes ConcurrentModificationException to be thrown. This happens whether you're using Iterators directly, using an enhanced for loop, or using forEach(). The other basic collections classes (LinkedList, HashSet, etc.) all work the same way.
If indeed you must accommodate the collection being modified without breaking an ongoing iteration over it, then you need a collection class that accomodates such activity. For example, you could consider ConcurrentLinkedDeque, or one of the other collections from java.util.concurrent. It is possible to write your own, but it's tricky, and why do that when there are existing classes in the standard library that will serve?
Note also that although a solution can be found in java.util.concurrent, the problem does not depend on multiple threads being involved. In fact, if you do have multiple threads then using one of the concurrent collections serves a dual purpose: it also ensures consistent operations on the collection without need of external synchronization. If by any chance you were previously accessing the list from multiple threads without proper synchronization then getting the CME was a lucky break -- you might instead have just gotten silent malfunction.
Look at an existing thread safe class such as ArrayBlockingQueue. You can wrap your ArrayList in synchronization code but it's easier to use a data structure that already does what you want.
First off my problem is similiar to this already answered question Merging two arrayLists into a new arrayList, with no duplicates and in order, in Java.
However the difference here is that I tried to merge two lists with more than just a String together. The intention on my side was to merge two objects of the following kind (to simplify things I striped unnecessary infos from the example):
public class Data{
private int count;
private Type type;
private Key uniqueKey;
}
So that I get a new oject which has a summarized count out. This will eliminate unwanted duplicates because the uniqueKey on these objects was identical. The reason for this is that I mapped several business types to only one technical type.
The problem here is that you have to account for every possiblity in order to handle the merge correctly and don't miss an original object.
Here are some cases I tried to cover with unit test:
One normal, followed by two duplicates, and one normal = normal, merged, normal
Two duplicates, followed by two normal = merged, normal, normal
Two normal, followed by two duplicates = normal, normal, merged
and so on and so forth...
So how to solve that without going crazy?
Since I spent half a day with that problem, I thought the simple answer might be useful to someone else.
So what did I try:
I decided not to go recursive since I avoid it if I can for obvious reasons and used two nested loops
I wrote unit tests for every case I could think of
Then I tried step by step to make them all green
I banged my head against the table because everytime I made one green another one went red
I asked a colleague
He let me state the problem without showing him my "solution"
Here's the magic 15 min solution:
public static LinkedList<Data> merge(final LinkedList<Data> source) {
final HashMap<Data, Integer> temp = new HashMap<>();
for (final Data data : source) {
final int count = data.getCount();
data.setCount(0);
if (temp.get(data) == null) {
temp.put(data, count);
}
else {
temp.put(data, temp.get(data) + count);
}
}
final Set<Entry<Data, Integer>> set = temp.entrySet();
final LinkedList<Data> result = new LinkedList<>();
for (final Entry<Data, Integer> entry : set) {
final Data mergedData = entry.getKey();
mergedData.setCount(entry.getValue());
result.add(mergedData);
}
Collections.sort(result, new DataComparator());
return result;
}