Updating Label with HashMap value - java

I have an issue, with binding a Label someLabel with a value.
So there is a class with a HashMap field used as an inventory:
public HashMap<Animals, Integer> inventory = new HashMap<>();
I need to display this Integer value live, like a timer of some sort.
The thing is, I tried to use bind property on the label, but it wil show only the initial value, and there are some issues with binding, as it wants a SimpleStringProperty class instance as a property. While my get(Object key) method is called, it returns a String after my modification, and won't let me do it...
Please point me in the right direction, should I monitor every time the method put() is called, to keep the label on the live by setText()?

HashMap does not implement Observable.
However JavaFX has ObservableMap which does implement Observable:
ObservableMap<Animals, Integer> inventory = FXCollections.observableHashMap();
Label label = ...
Animals key = ...
label.textProperty().bind(Bindings.valueAt(inventory, key).asString());
or for more control
label.textProperty().bind(Bindings.createStringBinding(() -> Objects.toString(inventory.get(key), "n/a"), inventory);

Related

Maintain java mapping from two different types of values in a single data structure

I have a collection of objects that look something like
class Widget {
String name;
int id;
// Intuitive constructor omitted
}
Sometimes I want to look up an item by name, and sometime I want to look it up by id. I can obviously do this by
Map<String, Widget> mapByName;
Map<Integer, Widget> mapById;
However, that requires maintaining two maps, and at some point, I will (or another user who is unfamiliar with the double map) will make a change to the code and only update one of the maps.
The obvious solution is to make a class to manage the two maps. Does such a class already exist, probably in a third party package?
I am looking for something that lets me do something along the lines of
DoubleMap<String, Integer, Widget> map = new DoubleMap<>();
Widget w = new Widget(3, "foo");
map.put(w.id, w.name, w);
map.get1(3); // returns w
map.get2("foo"); // returns w
A simple solution could be, to write your own key class that includes both keys.
class WidgetKey {
String id;
String name;
boolean equals() {...}
boolean hashCode() {...}
}
Map<WidgetKey, Widget> yourMap;
Beware that you have to implement equals and hashCode in the WidgetKey class. Otherwise put/get and other map methods wouldn't work properly.

Returning value of a HashMap

so I have been able to put objects into my hash map successfully, but I'm having trouble returning an object. When I used an arrayList for this same project, I simply displayed it with the following method:
public void displayDetails(int currentItem) {
accountIDTextField.setText(table.get(currentItem).getAccountID()+"");
accountNumberTextField.setText(table.get(currentItem).getAccountNumber());
surnameTextField.setText(table.get(currentItem).getSurname());
accountTypeTextField.setText(table.get(currentItem).getAccountType());
}
And pressing the 'first' button would go to the number 1 in the list.
first.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
currentItem = 1;
displayDetails(currentItem);
}
});
As for my hashing, I have used the accountNumber as the key, (hashed by using the % modulo function)
Working backwards, I can get the accountID when I pass in the accountNumber as a parameter in the get() method.
hashMap.get(12345678).getAccountID();
But how do I return the accountID if I just want to get the first object stored in the hash map(i.e get accountID without knowing accountNumber)?
(AccountID is an integer unique to a particular account and will be automatically generated when a new account record is created)
Sorry if this isn't worded very well, I'm still trying to get my head around Java and OOP in general. Any help would be greatly appreciated. Thanks
hope I understood you right. getting only the first item of a HashMap would be something like:
Map<String, String> myhashmap = new HashMap<String, String>();
myhashmap.entrySet().iterator().next();
You can get the contents of the Map by using Map.values().
I would't access the value based on it's order in the map because ordering is not guaranteed. You should give each one a defined number. Then you can access them like:
Object o = map.values().get(id);
to get the first:
Object o = map.values().get(0);

Bind HashTable/Map to Jtable

I have a C# background and I'm pretty new to Java. Trying to port my windows application to Mac using Java.
The issue I have is how to bind a HashTable that contains a class to a JTable so that the variables in the key show up in the JTable. In C# WPF it's very easy, just binding GridView.ItemSource to dictionary.keys. But in Java it seems much more complicated. Here is what I have so far:
Map<Files, String> files = new HashMap<Files,String>();
public class Files {
public Files(String files, String duration, String status) {}
}
public void AddFiles(String addfile, String addduration, String addstatus, String path){
files.put(new Files(
addfile, addduration, addstatus),
path);
}
In C# the class would look a little different, but I can just do GridView.Itemsource = files.Keys and voila, it all shows upp perfectly. How can I achieve something similar in Java?
I know that JTable can use a multidimensional array to load the values, so I am right now trying to load the values of HashTable into Object[][] tableData and then use:
String[] columnNames = {"File","Duration", "Status"};
final JTable table = new JTable(tableData, columnNames);
The problem is that I don't know how to access the variables inside the class "Files" inside the HashMap "files".
I know there is: .getKey().getClass() but I still haven't been able to load the multidimensional array with the values of the class "Files" inside the HashMap.
Create a tableModel extending from the base class AbstractTableModel. In here you'll have to override the appropriate methods (your IDE will point them out), the most important one being getValueAt().
Override this one in a fashion similar to:
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
switch (columnIndex) {
case 0:
return data.get(rowIndex).getValueX; // Change to however you'd like to call a single value from your map
case 1:
return data.get(rowIndex).getValueY;
case 2:
return data.get(rowIndex).getValueZ;
default:
throw new IndexOutOfBoundsException();
}
}
You can call your model with your data as a parameter. Afterwards, use this model as an argument to your JTable and you should be fine.
You can get the keys of a HashMap using HashMap.keySet() method.
This will return a Set of the key objects, which you have stored in your HashMap.
HashMap<Files, String> yourHashMap = new HashMap<Files, String>();
Set<Files> keys = yourHashMap.keySet();
Once you have got the set, assuming that you have the DefaultTableModel set to your JTable, you can do,
for(Files f : keys) {
yourDefaultTableModel.addRow(new String[]{f.files, f.duration, f.status});
}
NOTE:
Its always better to use a class which overrides equals() method & which is immutable to use as HashMap key.
Overriding equals() will avoid keys being overwritten incase of Hash collision, and making a class immutable doesn't make the value associated with the key orphan (since the key will not change once its created)
So I suggest using HashMap<String, Files> instead of HashMap<Files,String> if you feel making Files class immutable breaks its purpose.

Java: change a variable depending on dynamically generated text field name?

I have an ArrayList of TrainingClass objects with a variable "priority".
I am making a settings frame, where for each element currently in the ArrayList I make a TextField where the user sets priority.
This is how it is generated
for (TrainingClass tclass : mTrainingClasses) {
//Loop the ArrayList
JTextField txtPriority = new JTextField(3);
txtPriority.setBounds(10,10,100,20);
txtPriority.setText("" + tclass.getPriority());
getContentPane().add(txtPriority);
}
Now I would add a change listener, but...
Once I know which field has been changed, how can I access the proper element of the ArrayList mTrainingClasses?
In php, for example, I would simply make something like:
$mTrainingClasses->$changed_field->setPriority($new_value);
But, as far as I understand, I can’t do this in Java. So, how should I proceed?
Do I need to manually set the field name and listener for each element? I’m sure there is some other solution, but I have no idea at this point.
(I know I could use an ArrayList for the fields as well, such as
txtPriority.add(new JTextField(3));
But in this case, how do I know which index corresponds to the field that has been changed?
)
Have a list of Text Fields
List<JTextField> textFields = new ArrayList<JTextField>();
Change the loop like the following where you add all text fields to above list
for (TrainingClass tclass : mTrainingClasses) {
//Loop the ArrayList
JTextField txtPriority = new JTextField(3);
txtPriority.setBounds(10,10,100,20);
txtPriority.setText("" + tclass.getPriority());
getContentPane().add(txtPriority);
textFields.add(txtPriority);
}
In your listener you can do the following
mTrainingClasses.get(textFields.indexOf((JtextField) event.getSource()));
The above will return the TrainingClass which got changed.
There are several options:
Pass the TrainingClass element to the listener which you attach to the textfield. This will require to attach the listener in your for loop where you have access to both the TrainingClass and JTextField variable
Use a Map as suggested by #Ted Hopp
Use a List as you already suggested. Trick is to store an index in the JTextField so that afterwards you know which JTextField corresponds to which element in the List. You can use JComponent#putClientProperty and JComponent#getClientProperty for this.
You can use those JComponent#putClientProperty and JComponent#getClientProperty methods to store the TrainingClass variable directly
In your loop, you can populate a Map<JTextField, TrainingClass>. Then you can use that to look up the element from the changed field.
Map<JTextField, TrainingClass> fieldMap = new HashMap<>();
for (TrainingClass tclass : mTrainingClasses) {
//Loop the ArrayList
JTextField txtPriority = new JTextField(3);
txtPriority.setBounds(10,10,100,20);
txtPriority.setText("" + tclass.getPriority());
getContentPane().add(txtPriority);
map.put(txtPriority, tclass);
}
Alternatively, you can subclass JTextField and declare a data field that you can then reference directly in event handling.
You need some kind of mapping between the JTextField and the TrainingClass. Either make text field a property of your class or make a map that maps the two.
Map<TrainingClass, JTextField> myMap= new HashMap<TrainingClass, JTextField>();
for (TrainingClass tclass : mTrainingClasses) {
//Loop the ArrayList
JTextField txtPriority = new JTextField(3);
txtPriority.setBounds(10,10,100,20);
txtPriority.setText("" + tclass.getPriority());
getContentPane().add(txtPriority);
// map the textField to the training class
myMap.put(txtPriority, tclass);
}
When the field changes inside the listener method, you'd simply call:
public void eventListenerMethod(InputEvent e) {
JTextField fieldThatGeneratedEvent= e.getSource();
TrainingClass tClass= myMap.get(fieldThatGeneratedEvent);
}

Java cache design question

I need to develop a simple cache (no concurrency or refresh required) to hold different types of objects. The lookup of these objects may be in a different way. Like lets say we are caching book object which has ISBN number and author. Lookup of this object can be either by ISBN number like
Book lookupBookByISBN(String isbn);
OR it could be a lookupByAuthor like
List lookupBookByAuthor(String authorName);
In a very simple way, it means I can have a Cache object which has two maps one to store book object by ISBN and another to store the same object by authorname.
Like this, think of many such object type like book, so I do not want to store the same object in different maps just because the lookup of them are different.
One way I was thinking of having a single Map whose key is a custom Key object and value is Object (so that I can store any object or list of object)
The Key object is a immutable object which might look like this
public class Key {
private final Stirng keyName;
private final String keyValue;
public Key(String name,String value) {
this.keyName= name;
this.keyValue = value;
}
//getters for keyName and value
//hashcode and equals to be put as a key of a map
}
Implementation of lookup method will be
public Book lookupBookByISBN(String isbn) {
Key key = new Key("ISBN",isbn);
return ((Book)map.get(key));
}
public List<Book> lookupBookByAuthor(String isbn) {
Key key = new Key("Author",isbn);
return (List<Book>map.get(key));
}
The insert into map needs to be carefully done as the same object needs to be inserted twice into the map.
public void putBook(Book book) {
Key key = new Key("ISBN",book.getISBN());
map.put(key,book);
key = new Key("Author",book.getAuthor());
List<Book> list = map.get(key);
if (null == list) {
list = new ArrayList<Book>();
map.put(key,book);
}
list.add(book);
}
I somehow feel this might not be a good idea and I might need to put the same object in the map N number of times depending upon N dimensions by which I need to lookup the object.
Is there anyother way to design the same in a better way?
When you store an object in a collection (of any kind), you only store a reference to the object. So go ahead and use multiple maps, you will have only one copy of the actual object.
For example
Map<String,MyBigObject> map1 = new HashMap...
Map<String,MyBigObject> map2 = new HashMap...
MyBigObject mbo = new MyBigObject(...);
map1.put(mbo.getISBN(),mbo);
map2.put(mbo.getAuthor(),mbo);
The single object mbo is now accessible via either map.
EDIT: If you're worried about the complexity of multiple maps complicating the code, write a class MultiMap that contains all the maps and manages them in whatever way you want. You could have methods add(MyBigObject...) which inserts the object into all the maps using the various property accessors to set the correct key, and then lookup methods such as getByAuthor(...) and getByISBN(...), and whatever else you need. Hide all the complexity behind a simple unified interace.

Categories

Resources