0

I have a defined a class to manage the game room. When a user creates a new room, I generate a new room with unique room number and add it to the hashset.

Now ,I hope to remove that Room object from the hashset and recycle the Room object for perfarmance issue, say 24 hours , or the abandoned Room object will spend most of my mememory

How can I achieve this? Also, any suggestion to improve the performance will be highly appreciated.

My class is as follows:

   public class RoomService {

private RoomService(){
    super();
}

private HashSet<Room> roomSet =new HashSet<Room>();

private static RoomService instance =new RoomService();

public static RoomService getServiceInstance(){
    return instance;
}

private static Integer generateRandom(int length) {
    Random random = new Random();
    char[] digits = new char[length];
    digits[0] = (char) (random.nextInt(9) + '1');
    for (int i = 1; i < length; i++) {
        digits[i] = (char) (random.nextInt(10) + '0');
    }
    return Integer.decode(new String(digits));
}

/**
 * Generate new Room with an unique Room number
 * @return
 */
public Room newRoom(){
    Room newRoom;
    do{
        newRoom =new Room(generateRandom(4));
    }
    while(!roomSet.add(newRoom));

    return newRoom;
}}







public class Room {
private Integer roomNum;
private Date createTime=new Date();
private String creatorId;


/*
 * constructor
 */
public Room(Integer roomNum) {
    super();
    this.roomNum = roomNum;
}




@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((roomNum == null) ? 0 : roomNum.hashCode());
    return result;
}


@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Room other = (Room) obj;
    if (roomNum == null) {
        if (other.roomNum != null)
            return false;
    } else if (!roomNum.equals(other.roomNum))
        return false;
    return true;
}




//getter and setter
//
//
public String getCreatorId() {
    return creatorId;
}

public void setcreatorId(String creatorId) {
    this.creatorId = creatorId;
}

public Integer getRoomNum() {
    return roomNum;
}

public Date getCreateTime() {
    return createTime;
}

}

JaskeyLam
  • 15,405
  • 21
  • 114
  • 149
  • 1
    this is exactly what you need: http://stackoverflow.com/a/3802420/544983 – Juvanis Jul 17 '14 at 14:08
  • Thank you! But I am using a Set rather than a map? Are their any example for set? – JaskeyLam Jul 17 '14 at 14:22
  • then you can take a look at guava "cachebuilder" examples. – Juvanis Jul 17 '14 at 14:30
  • Sorry, I don't quite understand the code. I copied that example to my code and use Integer as Key, Customer Class Room as Graph. And when I return return createExpensiveGraph(key); from load method of the CacheLoader, it says "The method createExpensiveGraph(Integer) is undefined for the type new CacheLoader(){}" Do I need to do anything else? – JaskeyLam Jul 17 '14 at 14:48
  • Put the objects to be recycled in a queue, ordered by recycle date. Keep a timer set to the earliest date in the queue. When the timer goes off, dequeue expired entries and execute whatever you do to "recycle" them. (And reset the timer, of course.) – Hot Licks Jul 18 '14 at 03:09

1 Answers1

1

You can do this yourself by using a Timer. Every time somebody creates a new room you create a new TimerTask instance that will delete the id again and schedule that task to be executed using public void schedule(TimerTask task, Date time). It could look something like this:

private final Timer timer; // Initialised somewhere

public Integer newRoomNum() {
    Integer newRoomNum = ... // Create id
    Date date = ... // Create date when the id should be deleted again
    timer.schedule(new DeleteKeyTask(newRoomNum), date);
    return newRoomNum;
}

Where DeleteKeyTask is a custom subclass of TimerTask that deletes the given id.

private class DeleteKeyTask extends TimerTask {
    private final Integer id;

    public DeleteKeyTask(Integer id) {
        this.id = id;
    }

    @Override
    public void run() {
        // remove id
    }

You could use different approaches to save space:

Instead of having a task per key, you can store the date along side the integer key. For example you can use a HashMap<Integer, Date> (or store milliseconds instead of date). The keys of the map form your previous set. The values indicate the time the key was inserted or expires.

You can then schedule the timer to remove the next expiring key, at that time you look for the next expiring key and schedule to remove that etc. This will cost you O(n) time to compute the next expiring key. Your run method for the task would look something like

public void run() {
    map.remove(id);
    Integer next = ... // Find next expiring key
    timer.schedule(new DeleteKeyTask(next), map.get(next));
}

And you would need to adapt the creation method:

public Integer create() { // Previously newRoomNum()
    Integer newRoomNum = ... // Create id
    Date date = ... // Create date when the id should be deleted again
    if(map.isEmpty()) // Only schedule when empty
        timer.schedule(new DeleteKeyTask(newRoomNum), date);
    map.put(newRoomNum, date);
    return newRoomNum;
}

This way you will just need to store a date per integer. If the O(n) overhead is too much for you when calculating the next, you can make it faster by using more space: use a Queue and insert new keys. The queue will allow you to retrieve the next expiring key, making looking up the next expiring key O(1).

Samuel
  • 18,286
  • 18
  • 52
  • 88
  • Thank you! but what i hope is that i can recycle the abandoned room object, while isn't each room has a task very memory -cost ? – JaskeyLam Jul 17 '14 at 15:43
  • @Jaskey I updated my answer to give more information on saving space – Samuel Jul 17 '14 at 17:03
  • Samuel , While would you please refactor the code to make it a efficent sample using the date you updated? Since in my point of view, if i do not want to have a timetask for every room, I should NOT add code "timer.schedule(new DeleteKeyTask..." when I created a new Room. ;PS. according to your update, I decided that I should use a Room object to wrape the room Num and the create date which will be more extensive in the future. – JaskeyLam Jul 18 '14 at 03:02
  • @Jaskey I added a new sample that explains how you would create a new room. You can make a composite object and store it in a set, but than you should make sure that its hashcode is the hashcode of the key, and equals only compares the keys. Otherwise you could have two rooms with the same id and different expiration dates in your set. – Samuel Jul 18 '14 at 11:21
  • Thank you! I have using eclipse generated "equals" and "hashcode" to do that. Also, your code is make sense to me. What I consider is that, what your mentioned of Queue is much ore efficent to use,since we just need to put the room enqueue in order and the "next expire date" will simply need to be dequeue one . Isn't it? Is there any concern that you use map? – JaskeyLam Jul 18 '14 at 11:27
  • @Jaskey If you use a map (key -> date) or a set (key, date) you store every key and every date one time. `Map.put` / `Set.add` and `Map.containsKey` / `Set.contains` are both constant time operations. If you also use a queue you have to store every key **again** in the queue, but you can get the next one faster. If you store it **only** in the queue then `put/add/contains` will be linear time operations. One additional comment on map vs set is that if you use a `Room` object in a set, you will need to create a new `Room` object every time you call `Set.contains`. – Samuel Jul 18 '14 at 11:33
  • Hmm, but now I have chosen to Store the room in the Set(Room have a field of date, it itself konws when it will be abandoned). I still have to use another structure to let the task to run on ,Queue(in the order Room created) or in your case Map(key-->date), don't I? So, I still not know what is the benifits of using map in the case. What I think is that if I use Integer to be stored in the set, maybe map is a better choice, since we have to have a key-date mapping somewhere? – JaskeyLam Jul 18 '14 at 11:43
  • @Jaskey The `Map` is a replacement for the `Set`. So if you chose to use a set, you won't need a map. You will need a `Queue`, that, as you said, stores the elements in order or creation, if you want to efficiently get the next expiring room. Otherwise just loop through the set and find the next expiring room ;) – Samuel Jul 18 '14 at 12:24
  • @Jaskey If you are happy with the answer, you can accept it (click the checkbox under the answer score)! – Samuel Jul 22 '14 at 14:54