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.
Related
I am reading from a CSV file which contains data about Hills, all the reading etc works fine but now I want to create a map which associates the "County Name" (A column header in the file) with all the hills that have the county name in it.
I am along the right path, because my code works for the first countyName, but the problem is when my for loop If statement goes to the else statement, I want it to technically create another set (clearing the previous values) and assigning the new hill data with the county name
My output is:
{Perth and Kinross=[16,Knock of Crieff,Perth and Kinross,279.0,56.389329,-3.826973, 3,Creag Uchdag,Perth and Kinross,879.0,56.465278,-4.098107]}
So it has all the hills with "Perth and Kinross" in it. So now my next county name is "Stirling" for example so the map should end up like
{Perth and Kinross=[16,Knock of Crieff,Perth and Kinross,279.0,56.389329,-3.826973, 3,Creag Uchdag,Perth and Kinross,879.0,56.465278,-4.098107], Stirling=[7,Meall Buidhe,Stirling,719.0,56.419004,-4.308645]}
What I'm unsure about is how to create another set without clearing the values stored for Perth and Kinross
My code is:
Map<String, Set<Hill>> hillsByCounty = new HashMap<>();
if (h.getCounty().equals(countyName)) {
hillsByCounty.get(countyName);
currentSet.add(h);
hillsByCounty.put(countyName, currentSet);
} else {
countyName = h.getCounty();
currentSet.clear();
currentSet.add(h);
}
}
return hillsByCounty;
}
Where exactly is it going wrong. I have a feeling it is the clear function but i'm not sure how else i would do this.
My current code prints out:
{Perth and Kinross=[7,Meall Buidhe,Stirling,719.0,56.419004,-4.308645], Stirling=[7,Meall Buidhe,Stirling,719.0,56.419004,-4.308645]}
As its overwriting the set. What would be my workaround?
You reuse the same set. So if your county changes, you clear the set and fill it with the next county. Instead you should create a new set. As long as you reuse the same set, well, you reuse the same set, empty it and fill something else in it. hillsByCounty.put(...) does not clone the set, but just stores a reference.
The value for each entry of the map is the same set so every modification on one set will also appear at the other entries.
You can check for an existing set in the loop body:
for (Hill h : hills) {
Set<Hill> currentSet = hillsByCountry.get(h.getCountry());
if (currentSet == null) {
currentSet = new HashSet<>();
hillsByCountry.put(h.getCountry(), currentSet);
}
currentSet.add(h);
}
I'm having some trouble with one of my methods. A brief overview of what my method is supposed to do. Here's what I have so far.
public boolean addGame(String team1, String team2) {
boolean result;
if (team1.equals(team2))
result = false;
}
if (a game between two parameter teams has previously been added by
earlier call to addGame){
result = false;
}
else {
result = true;
}
I want my method to return false if a game between two parameter teams has previously been added to the conference object by an earlier call to addGame and
if the name of team1 and team2 are the same name.
My issue is the syntax for what's inside of my second if statement. Not sure how to go about it.
Thanks in advance for all the help.
Depends on how long you plan on saving the information. If it is just in the runtime, you can do something such as saving the two teams to an array.
If you want to save it for a longer time, you need some way of persisting the information, possibly a table in a database?
Then, you can query to data and check if the combination existed already.
You can create a history of the added teams:
ArrayList<String> history = new ArrayList<String>();
//As you add the teams
history.add(teamName); //add to history as well
//To check if teams already exist or added before
if(history.contains(team1) || history.contains(team2)){
return false;
}
If you have an ArrayList called games, and the games are stored in the format "team1;team2", then you can do games.indexOf(team1 + ";" + team2) == -1 for the if statement.
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;
}
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.
I'm implementing an Statistics + Achievements system. Basically the structure is:
An Achievement have many related Statistics, this relations must associate each Achievement with the required Statistics (and it value). For example the Achievement1 needs the Statistic1 with value 50 (or greater) and the Statistic2 with value 100 (or greater).
Given an Statistic I need also know what are the related Achievements (in order to check them when the Statistic changes.
Both Stats and Achievements have an unique id.
My problem is I donĀ“t know whats the best data(s) structure(s) for representing that. By the way I'm using:
SparseArray<HashMap<Statistic, Integer>> statisticsForAnAchievement;
For the first point where the index of the array is the Achievement ID and the HashMap contains the Statistic/TargetValue pairs. And a:
SparseArray<Collection<Achievement>> achievementsRelatedToAStatistic;
For the second point where the index is the StatisticID and the item is the collection of Achievements related.
Then I need to handle both objects keeping it coherence.
Is there an easier way of representing that? Thanks
As a Statistic(or a group of Statistics) describes an Achievement shouldn't that/those Statistic/s be stored in the Achievement class? For example, an improved Achievement class:
public class Achievement {
SparseArray<Statistic> mStatistics = new SparseArray<Statistic>();
// to get a reference to the statisctics that make this achievement
public SparseArray<Statistic> getStatics() {
return mStatistics;
}
// add a new Statistic to these Achievement
public void addStatistic(int statisticId, Statistic newStat) {
// if we don't already have this particular statistic, add it
// or maybe update the underlining Statistic?!?
if (mStatistics.get(statisticId) == null) {
mStatistic.add(newStat);
}
}
// remove the Statistic
public void removeStatistic(int statisticId) {
mStatistic.delete(statisticId);
}
// check to see if this achievment has a statistic with this id
public boolean hasStatistics(int statisticId) {
return mStatistic.get(statisticId) == null ? false : true;
}
// rest of your code
}
Also, the Statistic class should store its target(the 50 value for Statistic1) value in it as a field.
An Achievement have many related Statistics, this relations must
associate each Achievement with the required Statistics (and it
value). For example the Achievement1 needs the Statistic1 with value
50 (or greater) and the Statistic2 with value 100 (or greater).
The statistics are already stored in the achievments so all you have to do is store an array/list of ids of the achievments(or the achievements them self) and with this you'll have access to the statistics that made those achievements.
Given an Statistic I need also know what are the related Achievements
(in order to check them when the Statistic changes.
You would use the above array/list of achievements, iterate them and check to see if the achievement holds that particular Statistic:
ArrayList<Achievement> relatedAchievements = new ArrayList<Achievement>();
for (Achievement a : theListOfAchievments) {
if (a.hasStatistics(targetStatistic)) {
relatedAchievements.add(a); // at the end this will store the achievements related(that contain) the targetStatistic
}
}
Another option is to have somewhere a static mapping that stores which achievements have a Statistic, mapping that would get updated each time one of the addStatictic or removeStatistic methods gets called.
Regarding your code, if you don't need the Statistic object and are happy with just holding a reference to its id then you could improve the statisticsForAnAchievement with:
SparseArray<SparseIntArray> statisticsForAnAchievement;
// the index of the SparseArray is the Achievement's id
// the index of the SparseIntArray is the Statistic's id
// the value of the SparseIntArray is the Statistic's value