While implementing a database structure, my goal is to provide easy access to player data.
So, I have created the User class, which holds a Json instance and exposes the methods to take specific information from it.
public class User {
private Json data;
public User(OfflinePlayer player) {
File path = new File(player.getUniqueId() + ".json");
data = new Json(path);
}
public boolean isPremium() {
return data.getBoolean("premium");
}
}
The problem is that I have to create a new instance every time I need to know something about the same player from different parts of my code. That's very expensive!
So, is there a design pattern for this particular situation?
This is a simple cache. If you are using ORM such as hibernate, you could use second level cache for this.
You could also have unique user identifier (UUID id) as a key, with user data as a value in Map.
So, when you get request for user data, you first see if you have user with this uuid in cache(Map) and return data if you do.
If you don't have it, then go in database and fetch data.
Try creating a Map like this:
User user = null;
Map<UUID, User> usermap = new HashMap<>;
//before creating new user instance check if its present in Map
if(usermap.containskey(id){
//get user from Map
user = usermap.get(id);
else{
//not in map so create new User
user = new User(id);
usermap.put(id,user);
}
//use user object
But please be careful to destroy usermap instance or object containing it once it is not required. You can also so several modification with limiting size etc.
Related
I am wondering weather there is a better solution to my problem.
Better in the sense that not every object of the class Segment has to create a new database object.
I am trying to keep only one database in my program because the database is very big and I am sure there is a more efficient solution to this.
The Database holds objects of the class SegmentInformetion in a List. Each Object contains many informations each Segment object needs for its instantiation.
The Layer Class contains a List of Segments. The Layers Constructor contains an array with IDs. Every Segment will get its Information from the Database depending on the ID with which it is calling the Database.
Database {
List<SegmentInformation> segInfoList;
public SegmentInformation getSegInfos( int id ){
return segInfoList.get(id);
}
}
Layer{
List<Segments> segmentList;
public Layer( int[] segmentIDs ){
for (int i : segmentIDs){
segmentList.add( new Segment( segmentIDs[i] ) );
}
}
}
Segment{
double value1;
//....
double valuenN;
public Segment(int sID){
Database db = new Database();
SegmentInformation info = db.getSegInfos( sID );
value1 = info.getValue1();
//....
valueN = info.getValueN();
}
}
I am trying to avoid a global static variable which contains the Database.
Any suggestions for a more suitable way to instantiate all the Segment objects?
Use a Singleton to contain all the Segment objects:
In software engineering, the singleton pattern is a software design
pattern that restricts the instantiation of a class to one "single"
instance. This is useful when exactly one object is needed to
coordinate actions across the system. The term comes from the
mathematical concept of a singleton.
https://en.wikipedia.org/wiki/Singleton_pattern
I am creating a mock Twitter project which loads user data from a somewhat large text file containing ~3.6 million lines formatted like this:
0 12
0 32
1 9
1 54
2 33
etc...
The first string token is the userId and the second is the followId.
The first half of this helper method takes in the current user's ID, checks to see if it exists and creates a new user if necessary. After that, the followId is added to this new or existing user's following list of type ArrayList<Integer>.
With ~3.6 million lines to read, this doesn't take long (9868 ms).
Now the second half creates or finds the followed user (followId) and adds the userId to their followers list, but this additional code extends the amount of time to read the file exponentially (172744 ms).
I tried using the same TwitterUser object throughout the method. All of the adding methods (follow, addFollower) are simple ArrayList.add() methods. Is there anything I can do to make this method more efficient?
Please note: While this is school-related, I'm not asking for an answer to my solution. My professor permitted this slow object initialization, but I'd like to understand how I can make it faster.
private Map<Integer, TwitterUser> twitterUsers = new HashMap<Integer, TwitterUser>();
private void AddUser(int userId, int followId){
TwitterUser user = getUser(userId);
if (user == null){
user = new TwitterUser(userId);
user.follow(followId);
twitterUsers.putIfAbsent(userId, user);
} else{
user.follow(followId);
}
//adding the code below, slows the whole process enormously
user = getUser(followId);
if (user == null){
user = new TwitterUser(followId);
user.addFollower(userId);
twitterUsers.putIfAbsent(followId, user);
} else{
user.addFollower(userId);
}
}
private TwitterUser getUser(int id){
if (twitterUsers.isEmpty()) return null;
return twitterUsers.get(id);
}
If putIfAbsent(int, User) does what you would expect it to do, that is: checking if it's there before inserting, why do you use it within an if block whose condition already checks if the user is there?
In other words, if fetching a user returned a null value you can safely assume that the user was not there.
Now I'm not too sure about the internal workings of the *putIfAbsent* method (probably it would loop through the set of the keys in the map), but intuitively I would expect a normal put(int, User) to perform better, even more with a map that gets as large as yours as the input file gets scanned through.
Therefore I would suggest to try something like:
user = getUser(followId);
if (user == null){
user = new TwitterUser(followId);
user.addFollower(userId);
twitterUsers.put(followId, user);
} else{
user.addFollower(userId);
}
which would apply to the first half as well.
Description below the code...
// Singleton
public static final Map<String, Account> SHARED_ACCOUNT_HASHMAP =
Collections.synchronizedMap(new HashMap<>());
public init(String[] credentials) {
Account account = null;
String uniqueID = uniqueAccountIdentifier(credentials);
if (SHARED_ACCOUNT_HASHMAP.containsKey(uniqueID)) {
account = SHARED_ACCOUNT_HASHMAP.get(uniqueID);
log("...retrieved Shared Account object: %s", uniqueID);
}
// create the Account object (if necessary)
if (account == null) {
account = new Account(credentials);
// Store it in the SHARED_ACCOUNT_HASHMAP
SHARED_ACCOUNT_HASHMAP.put(uniqueID, account);
log("...created Account object: %s",uniqueID);
}
}
What I want to achieve
There are multiple Threads accessing this Singleton HashMap
The goal of this HashMap is to only allow the creation of ONE Account per uniqueID
The account later can be retrieved by various threads for Account operations
Each Thread has this init() method and runs it once.
So the first Thread that cannot find an existing uniqueID Account, creates a new one and places it in the HashMap. The next Thread finds that for the same uniqueID, there is an Account object already - so retrieves it for its own use later
My problem...
How can I get the other Threads (second, third, etc.) to wait while the first Thread is inserting a new Account object?
to phrase it another way, there should never be 2 threads ever that receive a value of null when reading the HashMap for the same uniqueID key. The first thread may receive a value of null, but the second should retrieve the Account object that the first placed there.
According to the docs for synchronizedMap()
Returns a synchronized (thread-safe) map backed by the specified map. In order to guarantee serial access, it is critical that all access to the backing map is accomplished through the returned map.
It is imperative that the user manually synchronize on the returned map when iterating over any of its collection views
In other words you still need to have synchronized access to SHARED_ACCOUNT_HASHMAP:
public init(String[] credentials) {
Account account = null;
String uniqueID = uniqueAccountIdentifier(credentials);
synchronized (SHARED_ACCOUNT_HASHMAP) {
if (SHARED_ACCOUNT_HASHMAP.containsKey(uniqueID)) {
account = SHARED_ACCOUNT_HASHMAP.get(uniqueID);
log("...retrieved Shared Account object: %s", uniqueID);
}
// create the Account object (if necessary)
if (account == null) {
account = new Account(credentials);
// Store it in the SHARED_ACCOUNT_HASHMAP
SHARED_ACCOUNT_HASHMAP.put(uniqueID, account);
log("...created Account object: %s",uniqueID);
}
}
}
Consider using ReadWriteLock if you have multiple readers/writers (see ReadWriteLock example).
Generally the ConcurrentHashMap performs better than the sinchronized hash map you are using.
In the following code I can feel smell of race condition check-then-act as you are trying to perform two operations on the synchronised map (containsKey and get):
if (SHARED_ACCOUNT_HASHMAP.containsKey(uniqueID)) {
account = SHARED_ACCOUNT_HASHMAP.get(uniqueID);
log("...retrieved Shared Account object: %s", uniqueID);
}
So to avoid race condition you need to synchronize over this map as:
synchronized (synchronizedMap) {
if (SHARED_ACCOUNT_HASHMAP.containsKey(uniqueID)) {
account = SHARED_ACCOUNT_HASHMAP.get(uniqueID);
log("...retrieved Shared Account object: %s", uniqueID);
}
// rest of the code.
}
Actually the synchronizedMap can protect itself against internal race conditions that could corrupt the map data but for external conditions (like above) you need to take care of that. If you feel you are using synchronized block at many places you can also think of using a regular map along with synchronized blocks. You will find this question also useful.
I have two classes. The OrderSlip class has a one-to-many relationship with orderedItemDescription.
class OrderSlip {
String employeeID
int serving
int tableNumber
static hasMany = [orderedItemDescription: OrderedItemDescription]
}
class OrderedItemDescription {
MenuItem menuItem
MenuItemProgressStatus progress//progress
String descriptionOfOrder
int quantity = 1
static belongsTo = OrderSlip
}
Now my problem is how do i iterate orderedItemDescription so that when i update my orderSlip i can add many orderedItemDescriptions along with its properties.
def updateOrderSlip(Long id) {
User currentUser = springSecurityService.currentUser
def orderSlipInstance = Table.get(id)
//other codes for orderedItemDescription here
orderSlipInstance.employeeID = currentUser.username
orderSlipInstance.serving= Integer.parseInt(params.serving)
orderSlipInstance.tableNumber= params.tableNumber
render(action:'server')
}
Im doing something like this in my gsp. im only adding data to the DOM with the add buttons. Then for the send order im hoping i can update it like the problem since im also adding many g:hiddenField for each orderedItemDescription in my summary
You should be persisting each new instance OrderedItemDescription somehow.
You can store it immediately in the DB upon click on add-button with the status flag set to incomplete. When you save the whole order, you must change the incomplete to complete.
Another option would be to keep the items in the http session. Upon send order you iterate through the in-session items and persist them all along with the order instance.
Both ways have advantages and drawbacks, but they both are useful.
I'm new to JAVA, but I know Objective-C. I have to write a server side Custom Code and I'm having trouble with the code below:
/**
* This example will show a user how to write a custom code method
* with two parameters that updates the specified object in their schema
* when given a unique ID and a `year` field on which to update.
*/
public class UpdateObject implements CustomCodeMethod {
#Override
public String getMethodName() {
return "CRUD_Update";
}
#Override
public List<String> getParams() {
return Arrays.asList("car_ID","year");
}
#Override
public ResponseToProcess execute(ProcessedAPIRequest request, SDKServiceProvider serviceProvider) {
String carID = "";
String year = "";
LoggerService logger = serviceProvider.getLoggerService(UpdateObject.class);
logger.debug(request.getBody());
Map<String, String> errMap = new HashMap<String, String>();
/* The following try/catch block shows how to properly fetch parameters for PUT/POST operations
* from the JSON request body
*/
JSONParser parser = new JSONParser();
try {
Object obj = parser.parse(request.getBody());
JSONObject jsonObject = (JSONObject) obj;
// Fetch the values passed in by the user from the body of JSON
carID = (String) jsonObject.get("car_ID");
year = (String) jsonObject.get("year");
//Q1: This is assigning the values to fields in the fetched Object?
} catch (ParseException pe) {
logger.error(pe.getMessage(), pe);
return Util.badRequestResponse(errMap, pe.getMessage());
}
if (Util.hasNulls(year, carID)){
return Util.badRequestResponse(errMap);
}
//Q2: Is this creating a new HashMap? If so, why is there a need?
Map<String, SMValue> feedback = new HashMap<String, SMValue>();
//Q3: This is taking the key "updated year" and assigning a value (year)? Why?
feedback.put("updated year", new SMInt(Long.parseLong(year)));
DataService ds = serviceProvider.getDataService();
List<SMUpdate> update = new ArrayList<SMUpdate>();
/* Create the changes in the form of an Update that you'd like to apply to the object
* In this case I want to make changes to year by overriding existing values with user input
*/
update.add(new SMSet("year", new SMInt(Long.parseLong(year))));
SMObject result;
try {
// Remember that the primary key in this car schema is `car_id`
//Q4: If the Object is updated earlier with update.add... What is the code below doing?
result = ds.updateObject("car", new SMString(carID), update);
//Q5: What's the need for the code below?
feedback.put("updated object", result);
} catch (InvalidSchemaException ise) {
return Util.internalErrorResponse("invalid_schema", ise, errMap); // http 500 - internal server error
} catch (DatastoreException dse) {
return Util.internalErrorResponse("datastore_exception", dse, errMap); // http 500 - internal server error
}
return new ResponseToProcess(HttpURLConnection.HTTP_OK, feedback);
}
}
Q1: Code below is assigning the values to fields in the fetched Object?
carID = (String) jsonObject.get("car_ID");
year = (String) jsonObject.get("year");
Q2: Is this creating a new HashMap? If so, why is there a need?
Map<String, SMValue> feedback = new HashMap<String, SMValue>();
Q3: This is taking the key "updated year" and assigning a value (year)? Why?
feedback.put("updated year", new SMInt(Long.parseLong(year)));
Q4: If the Object is updated earlier with update.add... What is the code below doing?
result = ds.updateObject("car", new SMString(carID), update);
Q5: What's the code below doing?
feedback.put("updated object", result);
Original Code
SMSet
SMInt
Q1: They read from the fetched JSON object and stores the values of the fields car_ID and year in two local variables with the same names.
Q2: Yes. The feedback seems to be a map that will be sent back to the client as JSON
Q3: It stores the value read into the local variable 'year' (as described earlier) in the newly created hashmap 'feedback'
Q4: Not sure, I assume the ds object is some sort of database. If so it looks like it takes the updated values stored in the hashmap 'update' and pushes it to the database.
Q5: It stores the "result" object under the key "updated object" in the feedback hashmap.
Hope this helps :)
Q1
No, it does not appear to be setting a class member variable, but rather a variable local to the execute() method. As soon as the method returns, those local vars are cleaned up by the GC. Well, not really, but they are now subject to GC, but that's getting really technical.
Q2
Yes, you are creating a HashMap and putting it's reference into a Map. Map is an interface, and it's good practice in Java to reference thing like this. This way you are not tying your code to a specific implementation. I believe in Objective-C they are know as Prototypes???
Q3
I am not sure why they are doing this. I assume somewhere in the code the feedback Map is used, and that value is plucked back out. Think of Maps as an NSDictionary. It looks like "year" is a String, so they use Long.parseLong() to convert it. Not sure what SMInt is...from the name it looks like a custom class that represents a "small int"???
Q4
I don't know what DataService is, but I have to guess its some sort of service the reads/write data??? From the method, I am guessing its calling the service to update the values you just changed.
Q5
Again, feedback is a Map...it's putting result in the "updated object" key of that map.