This question already has answers here:
Java time-based map/cache with expiring keys [closed]
(8 answers)
Closed 8 years ago.
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;
}
}
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).
Related
My class has a scheduled method that runs every half a second and it checks the data to see if any of the devices have timed out. If they are timed out for at least 5 seconds I am throwing an event to my database (this is done by checkSpecs method which I haven't reproduced here. The actual class is quite large so I have attempted to reduce the size while keeping relevant parts).
Now I am adding a new method to the class, checkReconnections which I want to use to throw another event to the database whenever a connection that previously timed out is regained.
Because my class is so large and I am monitoring several devices with this method, I attempted to create a helper method monitorConnectionStatus that accepts two booleans as arguments. My question is concerning this method.
I was under the impression that in passing the instance variables into monitorConnectionStatus when the method is invoked, that method gains access to them and can reassign those variables. This is my intent in order for the method to function as intended. But my IDE is telling me that the value of disconnectionStatus will always be false, which caused me to think, am I wrong in believing the instance variable will be reassigned? Is it possible that my IDE is just wrong in this case?
When I reassign the value of disconnectionStatus is it reassigning the value of the instance variable hasADisconnected or is it just doing it with a local argument variable?
Public Class OffSpecAgent {
private final DataModel dataModel;
private int deviceATimeoutCounter = 0;
private boolean isDeviceATimeout = false;
private boolean hasADisconnected = false;
private int deviceBTimeoutCounter = 0;
private boolean isDeviceBTimeout = false;
private boolean hasBDisconnected = false;
#Scheduled(fixedDelay = 500)
public void offSpecMonitor() {
checkTimeouts();
checkReconnections();
checkSpecs();
}
private void checkTimeouts() {
deviceATimeoutCounter = dataModel.getBoolean(DEVICE_A_TIMEOUT) ? deviceATimeoutCounter + 1 : 0;
isDeviceATimeout = deviceATimeoutCounter >= 10;
deviceBTimeoutCounter = dataModel.getBoolean(DEVICE_B_TIMEOUT) ? deviceBTimeoutCounter + 1 : 0;
isDeviceBTimeout = deviceATimeoutCounter >= 10;
}
private void checkReconnections() {
monitorConnectionStatus(isDeviceATimeout, hasADisconnected);
monitorConnectionStatus(isDeviceBTimeout, hasBDisconnected);
}
private void monitorConnectionStatus(boolean timeoutCondition, boolean disconnectionStatus) {
if (timeoutCondition) {
disconnectionStatus = true;
}
if (disconnectionStatus && !timeoutCondition) {
disconnectionStatus = false;
//throw reconnection event
}
}
}
In java, variables are passed by value into methods, meaning your method monitorConnectionStatus is only aware that it's getting false, false values. You would have to update your method to access the instance variable directly.
private void monitorConnectionStatus() {
if (this.timeoutCondition) {
this.disconnectionStatus = true;
}
if (this.disconnectionStatus && !this.timeoutCondition) {
this.disconnectionStatus = false;
//throw reconnection event
}
}
Note the keyword this is not required.
Also, I just want to add that you are using the term class variable incorrectly. The variables you are referring to are instance variables.
You can read more about that here:
https://www.tutorialspoint.com/What-are-class-variables-instance-variables-and-local-variables-in-Java
I refactored the class and now it looks like this:
Public Class OffSpecAgent {
private final DataModel dataModel;
private static class ConnectionTracker {
boolean timeout, timeoutExceeded, hasDisconnected;
int timeoutCounter = 0;
}
private final ConnectionTracker deviceATracker = new ConnectionTracker();
private final ConnectionTracker deviceBTracker = new ConnectionTracker();
#Scheduled(fixedDelay = 500)
public void offSpecMonitor() {
checkTimeouts();
checkReconnections();
checkSpecs();
}
private void checkTimeouts() {
trackTimeout(plcTracker, dataModel.getBoolean(DEVICE_A_TIMEOUT), 10);
trackTimeout(plcTracker, dataModel.getBoolean(DEVICE_B_TIMEOUT), 20);
}
private void trackTimeout(ConnectionTracker tracker, boolean isTimeout, int maxTimeout){
tracker.timeout = isTimeout;
tracker.timeoutCounter = isTimeout ? tracker.timeoutCounter + 1 : 0;
tracker.timeoutExceeded = tracker.timeoutCounter >= maxTimeout;
}
private void checkReconnections() {
monitorConnectionStatus(deviceATracker);
monitorConnectionStatus(deviceBTracker);
}
private void monitorConnectionStatus(ConnectionTracker tracker) {
if (tracker.timeoutExceeded) {
tracker.hasDisconnected = true;
}
if (tracker.hasDisconnected && !tracker.timeout) {
tracker.hasDisconnected = false;
//throw reconnection event
}
}
}
This seems to be much of an improvement, the tracker object actually makes the code more readable in my opinion, and now I am able to have the desired behavior. Thank you to everyone who responded to my question.
We have a Spring boot service where we receive files everyday, because of some issue (on producer) we are receiving multiple files with same name & date appended.
The new file overwriting the old, to handle it we want to append a sequence (starting from 1) at every file name.
But sequence should be auto-reset to '1' at midnight everyday.
Can anybody suggest a API or a way to reset the sequence.
To generate auto sequence, we are using AtomicSequenceGenerator , but we are unable to implement a simple auto-reset logic.
public class AtomicSequenceGenerator implements SequenceGenerator {
private AtomicLong value = new AtomicLong(1);
#Override
public long getNext() {
return value.getAndIncrement();
}
}
To not receive twice a 1:
public class AtomicSequenceGenerator implements SequenceGenerator {
private AtomicLong value = new AtomicLong(1);
private volatile LocalDate lastDate = LocalDate.now();
#Override
public long getNext() {
LocalDate today = LocalDate.now();
if (!today.equals(lastDate)) {
synchronized(this) {
if (!today.equals(lastDate)) {
lastDate = today;
value.set(1);
}
}
}
return value.getAndIncrement();
}
}
That is a bit ugly, so try a single counter:
public class AtomicSequenceGenerator implements SequenceGenerator {
private static long countWithDate(long count, LocalDate date) {
return (((long)date.getDayOfYear()) << (63L-9)) | count;
}
private static long countPart(long datedCount) {
return datedCount & ((1L << (63L-9)) - 1);
}
private static boolean dateChanged(long datedCount, LocalDate date) {
return (int)(datedCount >>> (63L-9)) != date.getDayOfYear();
}
private AtomicLong value = new AtomicLong(countWithDate(1, LocalDate.now()));
#Override
public long getNext() {
long datedCount = value.getAndIncrement();
LocalDate today = LocalDate.now();
if (dateChanged(dateCount, today)) {
long next = countWithDate(1L, today);
if (value.compareAndSet(datedCount+1, next)) {
datedCount = next;
} else {
datedCount = getNext();
}
}
return datedCount;
}
}
This uses an AtomicLong with the day-of-year packed into the counter.
One pulls a next counter.
If the date changed then:
when one could set the next day's 1, then give it.
when not, someone earlier with probably an earlier counter took the 1,
and then we need to take the next again.
You could create a singleton instance of your generator which resets itself as soon as a new date is passed.
Something like this:
public class AtomicSequenceGenerator implements SequenceGenerator {
// Private constructor in order to avoid the creation of an instance from outside the class
private AtomicSequenceGenerator(){}
private AtomicLong value = new AtomicLong(1);
#Override
public long getNext() {
return value.getAndIncrement();
}
// This is where the fun starts
// The T indicates some type that represents the file date
private static T prev_file_date = null;
private static AtomicSequenceGenerator instance = new AtomicSequenceGenerator();
public static synchronized long getNext(T file_date)
{
if ((prev_file_date == null) || (!prev_file_date.equals(file_date)))
{
instance.value.set(1);
prev_file_date = file_date;
}
return (instance.getNext());
}
}
As requested by #JoopEggen my version of his first solution :
public class AtomicSequenceGenerator implements SequenceGenerator {
private final Clock clock;
private final Object lock = new Object();
#GuardedBy("lock")
private long value;
#GuardedBy("lock")
private LocalDate today;
public AtomicSequenceGenerator(Clock clock) {
this.clock = clock;
synchronized (lock) {
value = 1;
today = LocalDate.now(clock);
}
}
#Override
public long getNext() {
synchronized (lock) {
LocalDate date = LocalDate.now(clock);
if (!date.equals(today)) {
today = date;
value = 1;
}
return value++;
}
}
}
The main differences are :
This uses just a private monitor to protect both the LocalDate and the value.
value is now a plain long, since it's guarded by a lock, it doesn't need to be AtomicLong anymore.
I inject a Clock object (for easier testing)
No double checked locking. Arguably double checked locking can be faster, but I don't know if it's really needed, until you do some performance testing.
First off, I need to override the method:
public boolean recordBid(int bidPrice, String sellerID)
so it manages the recording of a bid.
To begin with, if the bidPrice is greater than the buyNowprice then this bidPrice should reset to the buyNowPrice. After the bid price has been reset (if required) the method should invoke the superclass version of the recordBid() method, passing along the bid price and seller ID as parameters, and trap the result that it returns (ie. store it in a variable), so that it can be checked to determine if the bid price has been recorded successfully.
Im a little confused on how I need to trap the result and also not sure if Im doing it the right way here?.
My original recordBid() method:
public boolean recordBid(int bidPrice, String bidderID)
{
if (saleEnded == true)
{
return false;
}
else if (bidPrice <= this.highestBid)
{
return false;
}
else
{
this.highestBid = bidPrice;
this.bidderID = bidderID;
return true;
}
}
My subclass, where I need to overwrite recordBid()
public class BuyItNowSale extends ItemSale {
//instance variables
private double buyNowPrice;
private boolean acceptingNearestOffer;
public BuyItNowSale(String itemNumber, String itemDescription, String itemCondition,
String sellerID, boolean acceptingNearestOffer) {
super(itemNumber, itemDescription, itemCondition, sellerID);
this.acceptingNearestOffer = false;
//overidden recordBid() method
public boolean recordBid(int bidPrice, String bidderID) {
if(bidPrice > buyNowPrice) {
bidPrice = 0;
super.recordBid(bidPrice, sellerID);
}
}
With your requirement
if the bidPrice is greater than the buyNowprice then this bidPrice should reset to the buyNowPrice
Your method should be:
#Override
public boolean recordBid(int bidPrice, String bidderID) {
if(bidPrice > buyNowPrice) {
bidPrice = (int) buyNowPrice;
}
return super.recordBid(bidPrice, sellerID);
}
I have a very large graph with hundreds of millions of nodes and relationships, where I need to make a traversal to find if a specific node is connected with another one containing a particular property.
The data is highly interconnected, and for a pair of nodes there can be multiple relationships linking them.
Given that this operation needs to run on a real time system I have very strict time constraints, requiring no more than 200ms to find possible results.
So I have created the following TraversalDescriptor:
TraversalDescription td = graph.traversalDescription()
.depthFirst()
.uniqueness(Uniqueness.NODE_GLOBAL)
.expand(new SpecificRelsPathExpander(requiredEdgeProperty)
.evaluator(new IncludePathWithTargetPropertyEvaluator(targetNodeProperty));
The Evaluator checks for each path if the end node is my target, including and pruning the path if that's the case or excluding it and continuing if it's not.
Also, I set a limit on the time spent traversing and the maximum number of results to find.
Everything can be seen in the code below:
private class IncludePathWithTargetPropertyEvaluator implements Evaluator {
private String targetProperty;
private int results;
private long startTime, curTime, elapsed;
public IncludePathWithTargetPropertyEvaluator(String targetProperty) {
this.targetProperty = targetProperty;
this.startTime = System.currentTimeMillis();
this.results = 0;
}
public Evaluation evaluate(Path path) {
curTime = System.currentTimeMillis();
elapsed = curTime - startTime;
if (elapsed >= 200) {
return Evaluation.EXCLUDE_AND_PRUNE;
}
if (results >= 3) {
return Evaluation.EXCLUDE_AND_PRUNE;
}
String property = (String) path.endNode().getProperty("propertyName");
if (property.equals(targetProperty)) {
results = results + 1;
return Evaluation.INCLUDE_AND_PRUNE;
}
return Evaluation.EXCLUDE_AND_CONTINUE;
}
Finally I written a custom PathExpander because each time we need to traverse only edges with a specific property value:
private class SpecificRelsPathExpander implements PathExpander {
private String requiredProperty;
public SpecificRelsPathExpander(String requiredProperty) {
this.requiredProperty = requiredProperty;
}
public Iterable<Relationship> expand(Path path, BranchState<Object> state) {
Iterable<Relationship> rels = path.endNode().getRelationships(RelTypes.FOO, Direction.BOTH);
if (!rels.iterator().hasNext())
return null;
List<Relationship> validRels = new LinkedList<Relationship>();
for (Relationship rel : rels) {
String property = (String) rel.getProperty("propertyName");
if (property.equals(requiredProperty)) {
validRels.add(rel);
}
}
return validRels;
}
// not used
public PathExpander<Object> reverse() {
return null;
}
The issue is that the traverser keep going even long after the 200ms have passed.
From what I understood the evaluator behavior is to enqueue all following branches for each path evaluated with EXCLUDE_AND_CONTINUE, and the traverser itself won’t stop until it has visited all subsequent paths in the queue.
So what can happen is: if I have even few nodes with a very high degree it will result in thousands of paths to be traversed.
In that case, is there a way to make the traverser abruptly stop when the timeout has been reached and return possible valid paths that have been found in the while?
I would go with the following line of thought:
Once the timeout was elapsed stop expanding the graph.
private class SpecificRelsPathExpander implements PathExpander {
private String requiredProperty;
private long startTime, curTime, elapsed;
public SpecificRelsPathExpander(String requiredProperty) {
this.requiredProperty = requiredProperty;
this.startTime = System.currentTimeMillis();
}
public Iterable<Relationship> expand(Path path, BranchState<Object> state) {
curTime = System.currentTimeMillis();
elapsed = curTime - startTime;
if (elapsed >= 200) {
return null;
}
Iterable<Relationship> rels = path.endNode().getRelationships(RelTypes.FOO, Direction.BOTH);
if (!rels.iterator().hasNext())
return null;
List<Relationship> validRels = new LinkedList<Relationship>();
for (Relationship rel : rels) {
String property = (String) rel.getProperty("propertyName");
if (property.equals(requiredProperty)) {
validRels.add(rel);
}
}
return validRels;
}
// not used
public PathExpander<Object> reverse() {
return null;
}
I think taking a look at Neo4J TraversalDescription Definition might be beneficial for you too.
I would implement the expander to keep the lazy nature of the traversal framework, also for it's simpler code. This would prevent the traversal eagerly collecting all relationships for a node, Like this:
public class SpecificRelsPathExpander implements PathExpander, Predicate<Relationship>
{
private final String requiredProperty;
public SpecificRelsPathExpander( String requiredProperty )
{
this.requiredProperty = requiredProperty;
}
#Override
public Iterable<Relationship> expand( Path path, BranchState state )
{
Iterable<Relationship> rels = path.endNode().getRelationships( RelTypes.FOO, Direction.BOTH );
return Iterables.filter( this, rels );
}
#Override
public boolean accept( Relationship relationship )
{
return requiredProperty.equals( relationship.getProperty( "propertyName", null ) );
}
// not used
#Override
public PathExpander<Object> reverse()
{
return null;
}
}
Also the traversal will continue as long as the client, i.e. the one holding the Iterator received from starting the traversal calls hasNext/next. There will be no traversal on its own, it all happens in hasNext/next.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
OK i have multiple Java files but only looking at 2 at the moment and i am doing a GUI program.
I have an arraylist of Landlords in one file with a getLandlords method that returns the array and it won't work eclipse just gives an error no matter what i try
File 1
package assignment2;
import java.awt.event.*;
import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
public class RealEstateManage extends JFrame implements ActionListener, ListSelectionListener{
public static void main(String[] args)
{
RealEstateManage theGUI = new RealEstateManage();
}
//Initial setup for base of GUI
private JFrame jfFrame;
private JButton jbAddLandlord, jbAddProperty, jbAddLease, jbRecord, jbMaintenance, jbDisplayproperty;
private JTextArea jtaResults;
private JTextField jtfinput1, jtfinput2, jtfinput3, jtfinput4;
private JList <Landlord> jlLandlord;
private Vector<Landlord> vlist = new Vector<Landlord>();
private JPanel jpButtons = setupButtons(), jpResults = setupResults();
public RealEstateAgency R = new RealEstateAgency();
public RealEstateManage()
{
jfFrame = new JFrame ();
jfFrame.add(jpButtons, "North");
jfFrame.add(jpResults,"West");
jfFrame.setSize(900, 400);
jfFrame.setLocation(400, 300);
jfFrame.setTitle("Real Estate Management");
jfFrame.setVisible(true);
}
//Setup the buttons at the top of the GUI
public JPanel setupButtons()
{
JPanel jp = new JPanel ();
jbAddLandlord = new JButton("Add New Landlord");
jbAddProperty = new JButton("Add New Property");
jbAddLease = new JButton("Add New Lease");
jbRecord = new JButton("Record Payment");
jbMaintenance = new JButton("Maintenance");
jbDisplayproperty = new JButton("Display Properties");
jp.add(jbAddLandlord);
jp.add(jbAddProperty);
jp.add(jbAddLease);
jp.add(jbRecord);
jp.add(jbMaintenance);
jp.add(jbDisplayproperty);
jbAddLandlord.addActionListener(this);
jbAddProperty.addActionListener(this);
jbAddLease.addActionListener(this);
jbRecord.addActionListener(this);
jbMaintenance.addActionListener(this);
jbDisplayproperty.addActionListener(this);
return jp;
}
public JPanel setupResults()
{
JPanel jp = new JPanel ();
vlist.add(new Landlord("Fred Jones", "23 Hamilton Road", "0458 789 456", 456123369));
jtfinput1 = new JTextField (10);
jtfinput2 = new JTextField (10);
jtfinput3 = new JTextField (10);
jtfinput4 = new JTextField (10);
ArrayList<Landlord> Alist = R.getLandlords();
jlLandlord = new JList<Landlord>(vlist);
jlLandlord.addListSelectionListener(this);
jlLandlord.setPreferredSize(new Dimension(250, 300));
JLabel jlResults = new JLabel ("Output!");
jtaResults = new JTextArea (18, 30);
jtaResults.setEnabled(false);
jp.add(jlLandlord);
jp.add(jlResults);
jp.add(jtaResults);
return jp;
}
//List Events
#Override
public void valueChanged(ListSelectionEvent arg0) {
// TODO Auto-generated method stub
}
//Button Action Events
public void actionPerformed(ActionEvent ae) {
// Button Events
if(ae.getSource() == jbAddLandlord)
{
addLandlord();
}
if(ae.getSource() == jbAddLandlord)
{
}
}
//Add landlord function
public void addLandlord()
{
boolean inputIsOk = true;
String stName, stAddress, stNum, stBank;
long bank = 0;
JPanel myPanel = new JPanel();
myPanel.add(new JLabel("Name:"));
myPanel.add(jtfinput1);
myPanel.add(new JLabel("Address:"));
myPanel.add(jtfinput2);
myPanel.add(new JLabel("Phone:"));
myPanel.add(jtfinput3);
myPanel.add(new JLabel("Bank Account: (8-10 Digits)"));
myPanel.add(jtfinput4);
int result = JOptionPane.showConfirmDialog(null, myPanel,
"Please Enter X and Y Values", JOptionPane.OK_CANCEL_OPTION);
if (result == JOptionPane.OK_OPTION) {
stName = jtfinput1.getText();
stAddress = jtfinput2.getText();
stNum = jtfinput3.getText();
stBank = jtfinput4.getText();
try{bank = Long.valueOf(stBank).longValue();
}
catch(NumberFormatException nfe){
inputIsOk = false;
}
if (inputIsOk = true){
R.addLandlord(new Landlord(stName, stAddress, stNum, bank));
}
}
// jlLandlord.updateUI();
}
}
FILE 2
package assignment2;
import java.util.*;
// The RealEstateAgency class is intended to be the central place to go to for finding
// a landlord that the system "knows" about.
public class RealEstateAgency {
private ArrayList<Landlord> allLandlords; // A way of collecting all the Landlords
public RealEstateAgency()
{
// prepare the agency for future landlords...
allLandlords = new ArrayList<Landlord>();
addLandlord(new Landlord("stName", "stAddress", "stNum", 12456897));
System.out.println(getLandlords());
}
// Method to note the provided Landlord, by adding it to the RealEstateAgency's internal ArrayList.
// Returns true if apparently successful, false if something goes wrong such as the landlord already being present.
public boolean addLandlord(Landlord whoToStore)
{
boolean success = false;
if (whoToStore == null) // ensure there is actually a parameter
success = false;
else {
if (allLandlords.contains(whoToStore)) // landlord already in the list
success = false;
else
{ // Landlord not yet in list
allLandlords.add(whoToStore); // So add this landlord
success = true;
}
}
return success;
}
// Method to obtain a landlord from the RealEstateAgency's collection of known landlords,
// by providing the full name. If no such landlord exists, null will be returned.
public Landlord getLandlord(String fullName)
{
Landlord current = null;
boolean found = false;
int index = 0;
while ((!found) && (index < allLandlords.size()))
{
current = allLandlords.get(index);
if (current.getFullName().equals(fullName))
found = true;
else
index++;
}
// If we get to here, and "found" is true, then "current" will be the matching Landlord
if (!found) // did not find a match
current = null; // so ensure we return no Landlord
return current;
}
// Method to obtain an ArrayList containing the same landlords as those which the
// RealEstateAgency knows about (but without exposing the inner ArrayList object
// so that any alterations made to the returned ArrayList won't impact the RealEstateAgency)
public ArrayList<Landlord> getLandlords()
{
ArrayList<Landlord> duplicate;
duplicate = (ArrayList<Landlord>) allLandlords.clone();
return duplicate;
}
public ArrayList<Landlord> getLandlords1()
{
return allLandlords;
}
}
Any help or suggestions would be appreciated.
The Issues i am having is in the setupResults() function and the getLandlords() function
Thanks in advance
LANDLORD CODE
package assignment2;
import java.util.*;
// The Landlord class allows you to create objects that encapsulate the key data
// about each Landlord, as well as the information about all the properties that
// they make available for rent (some of which may have current leases active).
public class Landlord {
private String fullName; // The name of the landlord
private String address; // Where the landlord lives!
private String phone; // allow for spaces in number
private long bankAccount; // To permit 10 digits, requires the 'long' data type
private ArrayList<Property> myProperties; // All properties owned by this landlord
public Landlord(String fullName, String address, String phone, long bankAccount)
{
// Simply store the parameters into the fields...
this.fullName = fullName;
this.address = address;
this.phone = phone;
this.bankAccount = bankAccount;
// prepare for future leases...
myProperties = new ArrayList<Property>();
}
// ACCESSORS for each field of basic information
public String getFullName()
{
return fullName;
}
public String getAddress()
{
return address;
}
public String getPhone()
{
return phone;
}
public long getBankAccount()
{
return bankAccount;
}
// Method to note/store another Property, where this Landlord is considered the owner of the property.
// It returns true if successfully added to the list of properties belonging to this landlord.
public boolean addProperty(Property theProperty)
{
boolean result;
if (theProperty == null) // if it is null, we ignore this method call.
result = false;
else if (!myProperties.contains(theProperty)){ // Make sure it is not already in the array list
myProperties.add(theProperty);
result = true;
}
else // This means w have already added the property, so cannot add it again.
result = false;
return result;
}
// Method to return an iterator that may be used to cycle over all leases involving this landlord.
public Iterator<Property> getPropertiesIterator()
{
return myProperties.iterator();
}
public String toString()
{
return "Landlord: " + fullName;
}
}
PROPERTY CODE
package assignment2;
// The Property class encapsulates the basic information about one property the system knows
// about: essentially the address of the property, and a reference to the Lease details of the property.
public class Property {
private String propertyAddress;
private Lease currentLease; // When null, there is no active lease (it is available).
public Property(String address)
{
this.propertyAddress = address;
this.currentLease = null; // Initially, nobody is leasing it.
}
public String getPropertyAddress()
{
return propertyAddress;
}
// no mutator for address, because it won't ever change.
// Method to return the current lease's details.
public Lease getCurrentLease()
{
return currentLease;
}
// Method to set the current Lease of this Property.
// If null is provided, the property will not have any Lease assigned to it;
// otherwise, the provided Lease will be recorded as describing the relationship for this property.
public void setCurrentLease(Lease newLease)
{
currentLease = newLease;
}
// Method to report whether or not this property is currently associated with a lease (regardless
// of whether fully paid or not)
public boolean isLeased()
{
if (currentLease == null)
return false;
else
return true;
}
public String toString()
{
return "Property: " + propertyAddress;
}
}
LEASE CODE
package assignment2;
import java.util.*;
// The Lease class encapsulates information about an individual lease of a property by a tenant.
// The data stored here includes all the tenant’s details (name, phone number) as well as
// characteristics of the lease itself such as the duration, the weekly rent amount, how long
// remains in terms of payments, what maintenance has occurred.
public class Lease {
private String tenantFullName; // The name of the person who is living in the leased property.
private String tenantPhone; // Contact number for the person. Allow a leading zero at start, spaces between sections.
private int rentRate; // The amount of rent to be paid each week.
private int leasePeriod; // Either 6 or 12, being a 6 month or 12 month agreement
private int weeksRemaining; // How many weeks remain to be paid for. Initially 26 or 52.
private ArrayList<Maintenance> maintenanceRecord; // record of all maintenance during life of lease
// Create a new lease, recording the following details:
// - tenantName: records the name of the person living in the property
// - tenantPhone: records a contact phone number for the tenant
// - rentRate: the amount of rent which is to be paid each week.
// - leasePeriod: Either 6 or 12, indicating the number of months the lease is to last for.
public Lease(String tenantName, String tenantPhone, int rentRate, int leasePeriod)
{
this.tenantFullName = tenantName;
this.tenantPhone = tenantPhone;
this.rentRate = rentRate;
if (leasePeriod == 12 || leasePeriod == 6) // Check validity of parameter
this.leasePeriod = leasePeriod;
else // if not valid, set to default of 6 month.
this.leasePeriod = 6;
// Determine (automatically) how many weeks the lease is for, and set as initial value
// for the 'weeksRemaining' field:
if (this.leasePeriod == 12)
this.weeksRemaining = 52; // a Year remains to be paid for
else
this.weeksRemaining = 26; // Only half a year remains to be paid for.
// Prepare the lease for future Maintenance records...
maintenanceRecord = new ArrayList<Maintenance>();
}
// ACCESSORS
public String getTenantFullName()
{
return tenantFullName;
}
public String getTenantPhone()
{
return tenantPhone;
}
public int getRentRate()
{
return rentRate;
}
public int getLeasePeriod()
{
return leasePeriod;
}
public int getWeeksRemaining()
{
return weeksRemaining;
}
// Method to reduce the number of weeks remaining to be paid over the period of the lease.
// If the parameter is in excess of the current number of remaining weeks, the method will
// return false, otherwise it returns true and adjusts the number of remaining weeks.
public boolean reduceWeeksRemaining(int howMuch)
{
boolean result;
if (howMuch > weeksRemaining)
result = false;
else
{
weeksRemaining -= howMuch; // Reduces how many weeks remain unpaid.
result = true;
}
return result;
}
// Method to add another maintenance information record to this lease's list of maintenance records.
public void noteMaintenance(String reason, double cost)
{
Maintenance maintInfo = new Maintenance(reason,cost);
maintenanceRecord.add(maintInfo);
}
// Method to return an Iterator that may be used to cycle over all maintenance records for this lease
public Iterator<Maintenance> getMaintenanceRecords()
{
return maintenanceRecord.iterator();
}
}
MAINTENANCE CODE
package assignment2;
// The Maintenance class is used to represent an individual occurrence of Maintenance
// and records the cost and description of the maintenance.
public class Maintenance {
private String reason;
private double cost;
public Maintenance(String reason, double cost)
{
this.reason = reason;
this.cost = cost;
}
// ACCESSORS:
public String getReason()
{
return reason;
}
public double getCost()
{
return cost;
}
// Gives a explanation of the cost and reason represented by this Maintenance object.
public String toString()
{
return "$" + cost + " for " + reason;
}
}
The reason is that you initialize setupResults() before R.
change:
private JPanel jpButtons = setupButtons(), jpResults = setupResults();
public RealEstateAgency R = new RealEstateAgency();
public RealEstateManage()
{
to
private JPanel jpButtons;
private JPanel jpResults;
public RealEstateAgency R = new RealEstateAgency();
public RealEstateManage()
{
jpButtons = setupButtons();
jpResults = setupResults();
As a sidenote:
You take the result as:
ArrayList<Landlord> Alist = R.getLandlords();
but never use that value.
Maybe you meant to write:
ArrayList<Landlord> Alist = R.getLandlords();
jlLandlord = new JList<Landlord>(Alist.toArray(new Landlord[0]));
^ ^^^^^^^^^^^^^^^^^^^^^^^^^
but you did not.