Best practise for counting data in java - java

Lets say there is a pool of data taken a CSV file where we have a key value pairs, but keys are not unique. The requirement is to sift through each and every line and convert the CSV data into something useful. I'll make an example using a game log with format:
player, pointChange, timestamp
What I would like to do (which seems like a common operation) is to create a summary - how many points has over time. My idea was to create an inner class that represents a single entry:
private class GameFrame{
private String player;
private int points;
private ArrayList<String> timeline = new ArrayList<String>();
private ArrayList<int> pointHistory = new ArrayList<int>();
GameFrame(String player, int points, String time){
this.player = player;
this.points = points;
this.time.add(time);
}
public String getName(){return this.player;}
public void increment(int change){
this.pointHistory.add(this.points);
this.points += change;} //will work with negatives to decrement points as well
public void timeProgress(String time){this.time.add(time);}
}
The actual challenge:
The original data is of unknown size and is read line by line. Is there a good practice/recommended method to process such data. I was thinking about making a list of all GameFrame objects and nesting a second loop, something like this:
pseudocode:
for(everything in the input list){
load up line data;
for(everything in gameFrame list){
compare names;
if names match - update with data
return;}
got out of inner loop so it's a new player.
create entry for new player and add it to gameFrame list
}
Is it a good approach or there is a better way of doing it (perhaps to sort the data first or by using a library I don't know about)?
UPDATE:
I will try to do this using a hashmap instead of ListArray as suggested by Luke

Heavy solution: Database
More appropriate if you're going to have lots of records, you want to do the parsing/inserting in one session once, and then do processing later/multiple times, and if you're going to be appending data constantly. Databases make it really easy to work with sets of data.
Create a table named frames, with fields player (varchar), point_change (int) and timestamp (datetime), or similar. In the parsing step, simply insert the rows. Then you can select distinct player from frames; to get all players. Or select player, sum(pointChange) from frames group by player; to get points for a particular player. Or include the timestamp in a where clause to get points over a particular window of time.
Light solution: HashMap
More appropriate if you're going to do this one time. Or if there are so few records that it can be run many times trivially. It avoids the whole 'setting up a database' step.
HashMap<String, Integer> map = new HashMap<String, Integer>();
public void insert(String player, int scoreChange) {
Integer value = map.get(player);
if (value == null)
value = 0;
map.put(player, value + scoreChange)
}
public void getScore(String player) {
Integer value = map.get(player);
if (value == null)
value = 0;
return value;
}

Related

How to find the largest Count of Objects assosiated with the same Object

I have a Mentor class in which I have an ID for each mentor and an ArrayList of mentee IDs, like this:
public class Mentor {
int mentorId;
ArrayList<Integer> mentees = new ArrayList<>();
public Mentor(int mentorId, ArrayList<Integer> mentees) {
this.mentorId = mentorId;
this.mentees = mentees ;
}
}
The problem is that some mentees can be mentors too.
I would like to somehow get a count of all of the mentees associated to the top mentor as well as how many mentors are under the top mentor.
So, basically, if a mentor has a mentee, who is also a mentor, then this mentor's mentees are also associated to the top mentor.
So, my thinking was to loop through the mentee-list and see if any of the id's match an ID of Mentor. If true, Add this mentor's list of mentees to a list and loop again, but this will not work dynamically.
My main class looks something like this:
ArrayList<Mentor> mentors = new ArrayList<>();
ArrayList<Integer> mentees = new ArrayList<>();
ArrayList<Integer> mentees2 = new ArrayList<>();
mentees.add(2);
mentees.add(3);
mentees2.add(4);
mentees2.add(5);
//[1,{2,3}]
mentors.add(new Mentor(1, mentees));
//[2,{4,5}]
mentors.add(new Mentor(2, mentees2));
int mentorCount = 0;
int menteeCount = 0;
for (Mentor mentor : mentors) {
for (Integer mentee : mentees) {
mentorCount++;
if (mentee == mentor.mentorId){
mentorCount++;
//add each mentee to arraylist and start the process again, but is there an easier way to do this.
}
}
}
I was wondering if there is a way of solving this, maybe using streams?
I would recommend using good object oriented design, you shouldn't just use integer id's like that, because in this situation you could simply make an ArrayList of Person objects Where Mentees, and Mentors Inherit from Person. Then you can check if a Person is an instance of Mentee versus Mentor:
for (Person p : people) {
if (p instanceof Mentor)
{
// Mentor logic
}
if (p instanceof Mentee)
{
// Mentee Logic
}
}
Firstly, let's briefly recap the task.
You have a set of mentors, each mentor has a collection of mentees. Some of them might happen also to be mentors and so on.
Class design
From the perspective of class design, the solution is pretty simple: you need only one class to describe this relationship - Mentor. And each Mentor should hold a reference to a collection of Mentors (not integer ids).
In your domain model, as you described it, there's no substantial difference between a mentor and a mentee. Mentor points to other mentors - is a simplest way to model that. Mentee is just an edge case, a mentor which has an empty collection of mentors.
You don't need to include classes and features in your application that doesn't bring benefit.
Data structure
From the perspective of data structures, this problem can be described very well with an acyclic disjointed Graph.
Acyclic because if we consider the relationship between mentors when a mentor could indirectly point at themself (i.e. a mentor N has a mentee, with in tern points to another mentee that happens to be also a mentor of mentor N) the task becomes ambiguous. Therefore, I'm making an assumption that no one can be a mentor to himself.
I also depicted this data structure as disjointed because mentors in this model (as well as in real life) can form isolated groups, which in graph theory called connected components. That means that there could be several mentors with the same count of mentee, which happens to be the largest.
Depth first search
In order to find all the vertices (mentors) connected with a particular mentor, we have a classic traversal algorithm, which is called Depth first search (DFS). The core idea of DFS is to peek a single neighbor of the given vertex, then in turn peek one of its neighbors and so on until the hit the vertex that doesn't point to the other vertex (i.e. maximum depth is reached). And then it should be done with every other neighbors of the previously visited vertices.
There are two ways to implement DFS.
Iterative approach
For that, we need a stack. It will store all unvisited neighbors of the previously encountered vertexes. The last vertex from each list of neighbors in every branch will be explored first because it will be placed on the top of the stack. Then it will get removed from the stack and it's neighbors will be placed on the stack. This process repeats in a loop until the stack contains at least one element.
The most performant choice of collection for the stack is ArrayDeque.
Because this approach require continuous modification of the stack by adding and removing vertices, it isn't suitable to be implemented with Stream IPA.
Recursive approach
The overall principle is the same as described above, but we don't need to provide a stack explosively. The call stack of the JVM will be utilized for that purpose.
With this approach, there's also some room to apply streams. For that reason, I've chosen the recursive implementation. Also, its code is probably a bit easier to understand. But keep in mind that recursion has certain limitations, especially in Java, and not suitable for processing a large set of data.
Recursion
A quick recap on recursion.
Every recursive implementation consists of two parts:
Base case - that represents a simple edge-case for which the outcome is known in advance. For this task, the base case is the given vertex has no neighbors. That means menteesCount of this vertex needs to be set to 0 because it has no mentee. And the return value for the base case is 1 because this vertex, in turn, is a valid mentee of another vertex that holds a reference to it.
Recursive case - a part of a solution where recursive calls a made and where the main logic resides.
The recursive case could be implemented using streams and entails recursive invocation of the for every neighbor of the given vertex. Each of these values will contribute to the menteesCount of the given vertex.
The value returned by the method will be menteesCount + 1 because for the vertex which triggered this method call, the given vertex will be a mentee as well as its mentees.
Implementation
Class mentor basically serves as a vertex of the graph. Each vertex has a unique id and collection of adjacent vertexes.
Also, in order to reuse values obtained by performing DFS I've added a field menteesCount which is initially initialized to -1 in order to distinguish between vertices that has no adjacent vertices (i.e. menteesCount has to be 0) and vertices which value wasn't calculated. Every value will be established only ones and then reused (another approach will be to utilize a map for that purpose).
Method getTopMentors() iterates over the collection of vertices and invokes DFS for every vertex which value wasn't calculated yet. This method returns a list of mentors with the highest number of associated mentees
Method addMentor() that takes a vertex id, and id of its neighbors (if any) was added in order to interact with the graph in a convenient way.
Map mentorById contains every vertex that was added in the graph and, as its name suggests, allows retrieving it based on the vertex id.
public class MentorGraph {
private Map<Integer, Mentor> mentorById = new HashMap<>();
public void addMentor(int mentorId, int... menteeIds) {
Mentor mentor = mentorById.computeIfAbsent(mentorId, Mentor::new);
for (int menteeId: menteeIds) {
mentor.addMentee(mentorById.computeIfAbsent(menteeId, Mentor::new));
}
}
public List<Mentor> getTopMentors() {
List<Mentor> topMentors = new ArrayList<>();
for (Mentor mentor: mentorById.values()) {
if (mentor.getCount() != -1) continue;
performDFS(mentor);
if (topMentors.isEmpty() || mentor.getCount() == topMentors.get(0).getCount()) {
topMentors.add(mentor);
} else if (mentor.getCount() > topMentors.get(0).getCount()) {
topMentors.clear();
topMentors.add(mentor);
}
}
return topMentors;
}
private int performDFS(Mentor mentor) {
if (mentor.getCount() == -1 && mentor.getMentees().isEmpty()) { // base case
mentor.setCount(0);
return 1;
}
int menteeCount = // recursive case
mentor.getMentees().stream()
.mapToInt(m -> m.getCount() == -1 ? performDFS(m) : m.getCount() + 1)
.sum();
mentor.setCount(menteeCount);
return menteeCount + 1;
}
public static class Mentor {
private int id;
private Set<Mentor> mentees = new HashSet<>();
private int menteesCount = -1;
public Mentor(int id) {
this.id = id;
}
public boolean addMentee(Mentor mentee) {
return mentees.add(mentee);
}
// getters, setter for menteesCount, equeals/hashCode
}
}
An example of the graph used as a demo.
main() - the code models the graph shown above
public static void main(String[] args) {
MentorGraph graph = new MentorGraph();
graph.addMentor(1, 3, 4);
graph.addMentor(2, 5, 6, 7);
graph.addMentor(3, 8, 9);
graph.addMentor(4, 10);
graph.addMentor(5, 11, 12);
graph.addMentor(6);
graph.addMentor(7, 13, 14);
graph.addMentor(8);
graph.addMentor(9, 16, 17, 18);
graph.addMentor(10);
graph.addMentor(11, 18);
graph.addMentor(12);
graph.addMentor(13);
graph.addMentor(14, 19, 20);
graph.addMentor(15);
graph.addMentor(16, 21, 22);
graph.addMentor(17);
graph.addMentor(18);
graph.addMentor(19);
graph.addMentor(20);
graph.addMentor(21);
graph.addMentor(22);
graph.getTopMentors()
.forEach(m -> System.out.printf("mentorId: %d\tmentees: %d\n", m.getId(), m.getCount()));
}
Output
mentorId: 1 mentees: 10
mentorId: 2 mentees: 10
Use Person and Mentor and Mentee subclasses as suggested by acornTime, define mentees as a list of Person and the information you want becomes simple to get:
import java.util.*;
import java.util.stream.Stream;
public class Main{
public static void main(String[] args) {
ArrayList<Person> mentees = new ArrayList<>();
mentees.add(new Mentee(11));
mentees.add(new Mentee(12));
mentees.add(new Mentor(2, new ArrayList<>()));
mentees.add(new Mentee(13));
mentees.add(new Mentee(14));
mentees.add(new Mentor(3, new ArrayList<>()));
mentees.add(new Mentor(4, new ArrayList<>()));
mentees.add(new Mentor(5, new ArrayList<>()));
Mentor mentor = new Mentor(1, mentees);
System.out.println(mentor.menteesCount());
System.out.println(mentor.mentorsInMentees().count());
}
}
interface Person {
int getId();
}
class Mentor implements Person{
private final int mentorId;
private List<Person> mentees = new ArrayList<>();
public Mentor(int id, ArrayList<Person> mentees) {
mentorId = id;
this.mentees = mentees ;
}
#Override
public int getId() {
return mentorId;
}
public List<Person> getMentees() {
return mentees;
}
public int menteesCount() {
return mentees.size();
}
public Stream<Person> mentorsInMentees(){
return mentees.stream().filter(m -> (m instanceof Mentor));
}
}
class Mentee implements Person{
private final int menteeId;
public Mentee(int id) {
menteeId = id;
}
#Override
public int getId() {
return menteeId;
}
}
Test it online here
You should do something like a depth-first or breadth-first search (*):
Maintain a Set<Integer> containing all the people you have already seen.
Maintain a queue of some kind (e.g. an ArrayDeque), of people you are going to check.
Put the first person (or any number of people, actually) into this queue.
Then, while the queue is not empty:
Take the next person in the queue
If you've already seen them, go to the next item in the queue
If you've not already seen them, put the person into the seen set; add all of their mentees into the queue
That's it. The number of people at the end is the size of the seen set.
(*) Whether you do depth-first or breadth-first search depends on which end of the queue you add mentees to: adding them to the same end that you remove them from results in depth-first search; adding them to the other end results in breadth-first search. If you don't care which, choose either.

Multiple string replacement in a single string generating all possible combinations

I'm trying to replace multiple words in a string with multiple other words. The string is
I have sample {url} with time to {live}
Here the possible values for {url} are
point1
point2
Possible values for {live} are
10
20
The four possible answers are
I have sample point1 with time to 10
I have sample point1 with time to 20
I have sample point2 with time to 10
I have sample point2 with time to 20
This can also increase to three.
I have {sample} {url} with time to {live}
What would be best data structures and good approach to solve this problem ?
You can do it something like:
public static void main(String[] args) {
String inputStr = "I have {sample} {url} with time to {live}";
Map<String, List<String>> replacers = new HashMap<String, List<String>>(){{
put("{sample}", Arrays.asList("point1", "point2"));
put("{live}", Arrays.asList("10", "20"));
put("{url}", Arrays.asList("url1", "url2", "url3"));
}};
for (String variant : stringGenerator(inputStr, replacers)) {
System.out.println(variant);
}
}
public static List<String> stringGenerator(String template, Map<String, List<String>> replacers) {
List<String> out = Arrays.asList(template);
for (Map.Entry<String, List<String>> replacerEntry : replacers.entrySet()) {
List<String> tempOut = new ArrayList<>(out.size()*replacerEntry.getValue().size());
for (String replacerValue : replacerEntry.getValue()) {
for (String variant : out) {
tempOut.add(variant.replace(replacerEntry.getKey(), replacerValue));
}
}
out = tempOut;
}
return out;
}
also you can try make similar solution with recursion
You can use a template string and print the combinations using System.out.format method like below:
public class Combinations {
public static void main(String[] args) {
String template = "I have sample %s with time to %d%n"; //<-- 2 arguments case
String[] points = {"point1", "point2"};
int[] lives = {10, 20};
for (String point : points) {
for (int live : lives) {
System.out.format(template, point, live);
}
}
}
}
The code solves the 2 argument case but it can be easily extended to the 3 cases substituting the sample word with another %s in the template and a triple loop.
I'm using the simplest array structures, it is up to you decide which structure is the more adapt for your code.
Unless you want the hardcoded solution with simple nested loops shown in Dariosicily's answer, you will need to store "replacee-replacements" pairings, for example the string {url} paired with a list of strings point1 and point2. A simple class can do that, like
class StringListPair{
public final String s;
public final List<String> l;
public StringListPair(String s,List<String> l){
this.s=s;
this.l=l;
}
}
and then a list of replacements can be initialized as
List<StringListPair> mappings=Arrays.asList(
new StringListPair("{url}",Arrays.asList("point1","point2")),
new StringListPair("{live}",Arrays.asList("10","20","30")));
(If someone wants to totally avoid having a helper class, these are all strings, so a List<List<String>> can do the job too, having "{url}","point1","point2" lists inside, just then we would have to fight with indexing the inner lists everywhere)
Then two common approaches pop into my mind: a recursive one, generating all possible combinations in a single run, and a direct-indexing one, numbering all combinations and generating any of them directly upon request. Recursion is simpler to come up with, and it has no significant drawbacks if all the combinations are needed anyway. The direct approach generates a single combination at a time, so if many combinations are not going to be used, it can spare a lot of memory and runtime (for example if someone would need a single randomly selected combination only, out of millions perhaps).
Recursion will be, well, recursive, having a completed combination generated in its deepest level, thus it needs the following:
the list of combinations (because it will be extended deep inside the call-chain)
the mappings
the candidate it is working on at the moment
something to track what label it is supposed to replace a the moment.
Then two things remain: recursion has to stop (when no further labels remain for replacement in the current candidate, it is added to the list), or it has to replace the current label with something, and proceed to the next level.
In code it can look like this:
static void recursive(List<String> result,List<StringListPair> mappings,String sofar,int partindex) {
if(partindex>=mappings.size()) {
result.add(sofar);
return;
}
StringListPair p=mappings.get(partindex);
for(String item:p.l)
recursive(result,mappings,sofar.replace(p.s,item),partindex+1);
}
level is tracked by a simple number, partindex, current candidate is called sofar (from "so far"). When the index is not referring to an existing element in mappings, the candidate is complete. Otherwise it loops through the "current" mapping, and calling itself with every replacement, well, recursively.
Wrapper function to creata and return an actual list:
static List<String> userecursive(List<StringListPair> mappings,String base){
List<String> result=new ArrayList<>();
recursive(result, mappings, base, 0);
return result;
}
The direct-indexing variant uses some maths. We have 2*3 combinations in the example, numbered from 0...5. If we say that these numbers are built from i=0..1 and j=0..2, the expression for that could be index=i+j*2. This can be reversed using modulo and division operations, like for the last index index=5: i=5%2=1, j=5//2=2. Where % is the modulo operator, and // is integer division. The method works higher "dimensions" too, just then it would apply modulo at every step, and update index itself with the division as the actual code does:
static String direct(List<StringListPair> mappings,String base,int index) {
for(StringListPair p:mappings) {
base=base.replace(p.s,p.l.get(index % p.l.size())); // modulo "trick" for current label
index /= p.l.size(); // integer division throws away processed label
}
return base;
}
Wrapper function (it has a loop to calculate "2*3" at the beginning, and collects combinations in a list):
static List<String> usedirect(List<StringListPair> mappings,String base){
int total=1;
for(StringListPair p:mappings)
total*=p.l.size();
List<String> result=new ArrayList<>();
for(int i=0;i<total;i++)
result.add(direct(mappings,base,i));
return result;
}
Complete code and demo is on Ideone

HashMap "Unique" Keys

Firstly, I'd like to say I am still learning java, so excuse any unconventional code and/or questions.
I'm trying to allow the HashMap to set 'unique' keys while only using one put method.
This is what I currently have:
static int killcount = 0;
static Map<Integer, Integer> enumMap = new HashMap<Integer, Integer>();
public static void incrementKillcount() {
enumMap.put(getId(), killcount++);
}
(Again, excuse any unconventional java code, I'm still learning).
In this instance, if I'm not mistaken, the key is interchangeable (or at least from my experimenting). So the key doesn't really matter all that much. But while only having one put method, every key shares the same value. I'd like to make the value have some sort of 'unique' value.
For example, if I wanted this to count by increasing the killcount by 1 (killcount++;) until it reached 10, and then moving to a different key to count to 10 again, it would start counting from 10 instead of 1.
Thanks in advance, and again, excuse me for my terrible java skills! :)
The problem is static key word for killcount in your:-
static int killcount = 0;
The killcount will be initialized only once since it is static.
So increment operator will increment the previous value in killcount.
Solution to your problem
:-
set the killcount to zero while changing the key after saving it to map i.e. once killcount reaches 10.
public static void incrementKillcount() {
enumMap.put(getId(), killcount++);
if(killcount==10){
//resetting the static killcount value once it reaches 10
killcount=0;
}
For Scenario in your comment:-
public static void main(String[] args){
int sizeOfEachJar=10;
int numberOfJars=2;
Map<Integer, Integer> jarMap=new HashMap<Integer, Integer>(sizeOfEachJar);
//I am putting 10 cookies in each jar which is max size of each jar
for(int i=1;i<=numberOfJars;i++){
jarMap.put(i, sizeOfEachJar);
}
//Now eating three cookies from first jar
for(int i=0;i<3;i++){
jarMap.put(1, sizeOfEachJar--);
}
//Now eating 2 cookies out of 2nd jar
for(int i=0;i<2;i++){
jarMap.put(2, sizeOfEachJar--);
}
//Now finding out how many cookies remaining in all jars
int remainingCookies=0;
for(int i=1;i<=numberOfJars;i++){
remainingCookies+=jarMap.get(i);
}
System.out.println(remainingCookies);
}
You have to check your getID() code whether it returns same value or not.
as per your words your getId is returning same value each time
assuming same =1
int getId()
{
return same;
}
update your code with getId() by placing some use full logic to return a key
you may update like:
int getId()
{
return same++;
}
you may take same as static or according to your convince.
prefer effective java for the same.

Bukkit plugin - check if specific data from a file exists, and declare it

I'm having an issue with a bukkit plugin I'm updating. In what I'm doing, when a player uses a bed, it saves the following data to a file named beds.yml:
ThePlayersUsername:
X: (double)
Y: (double)
Z: (double)
world: worldName
When the plugin is initialized, I need to declare a few variables, because they are used later on in the code.
Player p = (Player)sender;
String dn = p.getDisplayName();
dn = ChatColor.stripColor(dn);
double x = (Double) yml.get(dn + ".x");
double y = (Double) yml.get(dn + ".y");
double z = (Double) yml.get(dn + ".z");
World bw = Bukkit.getWorld((yml.get(dn + ".world").toString()));
Location l = new Location(bw, x, y, z);
I can't do this if they don't exist, but, I can't reset to any defaults because new items get added to the config each time someone enters a bed, and all data is user-specific.
How would I go about checking if the player has some data in the config, if not, telling them to sleep in a bed, and when they do have data in the config, how to declare it so they can use a command to teleport using data from the config. Thanks.
Create 2 list or array:
-The online players
-The players in the config
A simple string list is do the magic just store the names. Then compare them. Something like that:
List<String> online_players = new List<String>();
//fill list code here
List<String players_in_config = new List<String>();
//fill list code here
List<String> output = new List<String>();
for(String s : online_players){
for(String a : players_in_config){
if(s.equals(a)){
output.add(s);
break;
}
}
}
//and now output contains the names of the players that online and have a bed in the config
You are going to want to use a Map here. Maps are a data structure that creates key-value pairs for your data. You can ask a Map for the data associated with a given key and you will get the value you put into it, or null if they key was not found.
Your map would look something like this:
private Map<Player, Location> bedLocations = new HashMap<Player, Location>();
We are going to store this data inside your JavaPlugin class since it needs access inside your listener and command handler. You will also need a way to get this Map from within your listener. An accessor method will do the job here. See the below method for how to do this. You will need to do it later as well.
public Map<Player, Location> getBedLocations() {
return bedLocations;
}
In case you were wondering, a HashMap is a type of map that used the int hashCode() method to quicken key value access, but that's a more high level topic that's irrelevant to the issue at hand.
That map should store a key-value pair of all Players currently online that have a bed location associated with them. Since you don't need someone's info until they come online, you shouldn't load out of the configuration yet, but you should still open the configuration like so to prevent reading the file every time. This should be done in the onEnable method.
//Near your map and other class variables
private FileConfiguration bedConfiguration;
//Inside your onEnable
bedConfiguration = YamlConfiguration.loadConfiguration(new File(PLUGIN_VARIABLE.getDataFolder(), "beds.yml"));
Don't forget to store that bedConfiguration in a class variable. Also create an accessor method for it. I would name it getBedConfiguration().
Now you have a loaded configuration and a place to store your data, what next? You need to populate your Map with player data when they join.
Inside your listener, you should write a method to listen for the PlayerJoinEvent. Inside here you will load your player data.
Firstly, we need to get the YML file from our JavaPlugin class.
FileConfiguration bedConfig = plugin.getBedConfiguration();
That will get you the FileConfiguration that you opened up earlier. Now we check to see if the player's data is in the config.
if (bedConfig.isSet(event.getPlayer().getName()) {
//Next section here
}
If the isSet method returns true, then something exists there. Now we load the data.
String playerName = event.getPlayer.getName(); //You can do this above the if statement if you wish.
double x = (Double) bedConfig.get(playerName + ".x");
double y = (Double) bedConfig.get(playerName + ".y");
double z = (Double) bedConfig.get(playerName + ".z");
World bw = Bukkit.getWorld((bedConfig.get(dn + ".world").toString()));
Location loc = new Location(bw, x, y, z);
Your data is loaded! Excellent. Now we store it in the map for later access. First, get the Map from the JavaPlugin class. Then we put our data into the Map.
Map<Player, Location> map = plugin.getBedLocations();
map.put(event.getPlayer(), loc);
The data is in the map! To access it, make sure you get the map first and then call map.get(PLAYER);
Other things that need to be done are... saving the data when a player leaves / the server shuts down and removing the player from the map when they leave (Keeping players in a map is a very bad practice, you can use their name or UUID as a key if you wish to prevent that issue).
To save your Map to the config, you can either change the config whenever a bed location changes, or you can wait and loop over all the Map entries and save them all at the end. To loop over these you can use an advanced for loop.
for(Player player : map.keySet()) {
Location loc = map.get(player);
//save here
}
This will loop over all players in the map and let you access their location with the map.get(player) method.
To remove a player from the map, listen for the PlayerQuitEvent and use map.remove(player); to unload their data. Don't forget to save it first if you don't save it every update.
If you have any questions, please leave a comment. I tried to be very thorough.

Android game rpg inventory system

I am using an ArrayList as my "inventory".
I am having trouble figuring out a way to add multiples of the same item without taking up a spot in the "inventory". For example: I add a potion to my inventory. Now I add ANOTHER potion but this time instead of adding ANOTHER potion to the inventory, it should instead show that I have: Potions x 2, while only taking up one spot in the ArrayList. I have come up with a few solutions but I feel as if they are bad practices. One solution I tried was to add an AMOUNT variable to the item itself and increment that. Help me find a much better solution?
EDIT: Ok please ignore the above. I have gotten pretty good answers for that but what surprised me was that there were almost no tutorials on role playing game inventory systems. I've done a lot of google searching and cannot find any good examples/tutorials/source code. If anyone could point me to some good examples/tutorials/source code (does not matter what language, but preferable java, or even c/c++) I would appreciate it, thanks. Oh, and any books on the subject matter.
The usual way to solve this (using the standard API) is to use a Map<Item, Integer> that maps an item to the number of of such items in the inventory.
To get the "amount" for a certain item, you then just call get:
inventory.get(item)
To add something to the inventory you do
if (!inventory.containsKey(item))
inventory.put(item, 0);
inventory.put(item, inventory.get(item) + 1);
To remove something from the inventory you could for instance do
if (!inventory.containsKey(item))
throw new InventoryException("Can't remove something you don't have");
inventory.put(item, inventory.get(item) - 1);
if (inventory.get(item) == 0)
inventory.remove(item);
This can get messy if you do it in many places, so I would recommend you to encapsulate these methods in an Inventory class.
Good luck!
Similar to aioobe's solution, you can use TObjectIntHashMap.
TObjectIntHashMap<Item> bag = new TObjectIntHashMap<Item>();
// to add `toAdd`
bag.adjustOrPutValue(item, toAdd, toAdd);
// to get the count.
int count = bag.get(item);
// to remove some
int count = bag.get(item);
if (count < toRemove) throw new IllegalStateException();
bag.adjustValue(item, -toRemove);
// to removeAll
int count = bag.remove(item);
You can create a multiples class.
class MultipleOf<T> {
int count;
final T t;
}
List bag = new ArrayList();
bag.add(new Sword());
bag.add(new MultipleOf(5, new Potion());
Or you can use a collection which records multiples by count.
e.g. a Bag
Bag bag = new HashBag() or TreeBag();
bag.add(new Sword());
bag.add(new Potion(), 5);
int count = bag.getCount(new Potion());
You're probably better off creating a class called InventorySlot, with a quantity and contents field. This also give you the flexibility of adding other properties, such as what the inventory slot can contain, should you decide to create a 'potions' only sack or something like that.
Alternatively, a StackCount and a boolean IsStackable, or perhaps MaxStack property is used in quite a few MMO's, it's a perfectly valid technique too.
or an class InventoryField with an item and an integer for the amount.
public class InventoryField{
int count;
Item item;
}
public class Inventory extends ArrayList<InventoryField>{
...
}
How about the following
public class Item{
int count;
String name;
}
Then have a list representing the inventory
public class Player {
List<Item> inventory = new ArrayLis<Item>();
}

Categories

Resources