Is there a way to create a listener (JavaFX) for any changes made to any of an object's field?
I have a coordinate object:
public class Coord {
public int x;
public int y;
public Coord(int aX, int aY) {
x = aX;
y = aY;
}
}
I have a component that creates a coordinate object when the mouse enters and destroys it when it exits. I've attached an invalidation listener:
this.setOnMouseEntered(event -> {
_hoverCoord = new SimpleObjectProperty<Coord>(getCoord(event.getX(), event.getY()));
_hoverCoord.addListener(redraw);
});
this.setOnMouseExited(event -> {
_hoverCoord = null;
});
When the mouse moves, I've been creating a new coordinate. Great, the invalidation fires because I'm replacing the coordinate. But this creates a whole bunch of these short-lived objects. I've resolved this by just calling the code I want directly in the mouse move, but it raised the following questions:
My first question is: Is that kind of rapid-fire object creation/destruction worth worrying about, generally? (I know that's a hard question to answer but I'm thinking in terms of garbage collection when creating tens of thousands of objects in a short time.)
My second question is: Is there a "listener" that just watches a POJO like Coord for field level changes?
My third question is: If not, is there a way to preserve Coord as a simple object and yet listen for specific field changes (without adding methods or changing the x and y from int)? I mean, no accessors for the fields.
My fourth question is: If not, how would I put in the accessors in Coord?
First, transform your Coord in a JavaFX Bean:
public class Coord{
private final IntegerProperty x = new SimpleIntegerProperty(this, "x");
private final IntegerProperty y = new SimpleIntegerProperty(this, "y");
public final void setX(int x){ this.x.set(x); }
public final int getX(){ return x.get(); }
public final IntegerProperty xProperty(){ return this.x; }
//Repeat for y.
}
Then, you may add an invalidation or change listener to the x property:
myCoordinate.xProperty().addListener(redraw);
myCoordinate.yProperty().addListener(redraw);
Now, there's a question: why do you need to listen the coordinates? If you need to compute something, you may use the helper Bindings, for instance, if you want to compute x*y each time the cursor moves, then you may use:
productProperty.bind(Bindings.createIntegerBinding(
()->coordinate.getX() * coordinate.getY(), //Compute x*y
coordinate.xProperty(), //dependency on x property
coordinate.yProperty()//dependency on y property
));
Or you may create your readonly property:
private final ReadOnlyIntegerWrapper product = new ReadOnlyIntegerWrapper(this, "product");
public Coord(){
init();
}
private void init(){
product.bind(Bindings.createIntegerBinding(
()->coordinate.getX() * coordinate.getY(), //Compute x*y
coordinate.xProperty(), //dependency on x property
coordinate.yProperty()//dependency on y property
));
}
public final int getProduct(){
return product.get();
}
public final ReadOnlyIntegerProperty productProperty(){
return product.getReadOnlyProperty();
}
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I'm looking for some tip on how to do a percentage thing for my game I want all flowers in a range of 1-98 and white/black flowers 99-100 to make it more rarerity thanks for the help :)
public enum FlowerSuit {
WHITE_FLOWERS("white", ":white:", "470419377456414720", 1),
YELLOW_FLOWERS("yellow", ":yellow:", "470419561267855360", 1 ),
RED_FLOWERS("red", ":red:", "470419583250202644", 1),
RAINBOW_FLOWERS("rainbow", ":rainbow:", "470419602841665536", 1),
PASTEL_FLOWERS("pastel", ":pastel:", "470419629450199040", 1),
ORANGE_FLOWERS("orange", ":orange:", "470419647900942366", 1),
BLUE_FLOWERS("blue", ":blue:", "470419688753594368", 1),
BLACK_FLOWERS("black", ":black:", "470419706751352842", 1);
private final String displayName;
private final String emoticon;
private int value;
private final String id;
FlowerSuit(String displayName, String emoticon, String id, int value ) {
this.displayName = displayName;
this.emoticon = emoticon;
this.value = value;
this.id = id;
}
public String getDisplayName() {
return displayName;
}
public String getEmoticon() {
return emoticon;
}
public String getId() {
return id;
}
public int getValue() {
// TODO Auto-generated method stub
return value;
}
}
This is how I'd do it, but it can probably be improved, for starters by using Java 8 streams etc.
public enum FlowerSuit {
WHITE_FLOWERS("white", ":white:", "470419377456414720", 1,3),
YELLOW_FLOWERS("yellow", ":yellow:", "470419561267855360", 1,2),
RED_FLOWERS("red", ":red:", "470419583250202644", 1,2),
RAINBOW_FLOWERS("rainbow", ":rainbow:", "470419602841665536", 1,2),
PASTEL_FLOWERS("pastel", ":pastel:", "470419629450199040", 1,2),
ORANGE_FLOWERS("orange", ":orange:", "470419647900942366", 1,2),
BLUE_FLOWERS("blue", ":blue:", "470419688753594368", 1,2),
BLACK_FLOWERS("black", ":black:", "470419706751352842", 1,1);
private static Random random = new Random();
private final String displayName;
private final String emoticon;
private int value;
private final String id;
private final int freq;
private FlowerSuit(String displayName, String emoticon, String id, int value, int freq ) {
this.displayName = displayName;
this.emoticon = emoticon;
this.value = value;
this.id = id;
this.freq = freq;
}
public String getDisplayName() {return displayName;}
public String getEmoticon() {return emoticon;}
public String getId() {return id;}
public int getValue() {return value;}
/**
* Choose a flower
* white has a 3 in 16 (about a 5:1) chance of being picked
* Black has a 1 in 16 chance, everything else 2/16
* #return
*/
public static FlowerSuit pick() {
//first sum all the chances (currently it's 16)
int sum = 0;
for (FlowerSuit f:FlowerSuit.values()) sum+= f.freq;
//now choose a random number
int r = FlowerSuit.random.nextInt(sum) + 1;
//now find out which flower to pick
sum = 0;
for (FlowerSuit f:FlowerSuit.values()) {
sum += f.freq;
if (r<=sum) return f;
}
//code will never get here
return FlowerSuit.WHITE_FLOWERS;
}
public static void main(final String[] args) throws Exception {
//Test it
Map<FlowerSuit,Integer>count = new HashMap<FlowerSuit,Integer>();
for (int a=0;a<1000000;a++) {
FlowerSuit f = FlowerSuit.pick();
Integer i = (count.get(f)!=null)?count.get(f):new Integer(0);
i = new Integer(i+1);
count.put(f,i);
}
int sum = 0;
for (Map.Entry<FlowerSuit,Integer>e:count.entrySet()) sum+=e.getValue();
float f = Float.valueOf(sum);
for (Map.Entry<FlowerSuit,Integer>e:count.entrySet()) {
System.out.println(e.getKey() + " was chosen " + ((e.getValue() / f) * 100f) + "% of the time");
}
}
}
gives
BLUE_FLOWERS was chosen 12.4986% of the time
PASTEL_FLOWERS was chosen 12.4707% of the time
WHITE_FLOWERS was chosen 18.7365% of the time
BLACK_FLOWERS was chosen 6.2632003% of the time
ORANGE_FLOWERS was chosen 12.4986% of the time
RED_FLOWERS was chosen 12.5241995% of the time
YELLOW_FLOWERS was chosen 12.501401% of the time
RAINBOW_FLOWERS was chosen 12.5068% of the time
You can use a TreeMap to map all of the integers from 0 to 99 to a particular FlowerSuit. Take advantage of the floorEntry method to choose a FlowerSuit for each number. It might look something like this.
public class FlowerChooser {
private static final NavigableMap<Integer, FlowerSuit> FLOWER_SUITS;
private static final Random RANDOMS = new Random();
public FlowerChooser() {
FLOWER_SUITS = new TreeMap<>();
FLOWER_SUITS.put(0, FlowerSuit.RED_FLOWERS);
FLOWER_SUITS.put(14, FlowerSuit.ORANGE_FLOWERS);
FLOWER_SUITS.put(28, FlowerSuit.YELLOW_FLOWERS);
FLOWER_SUITS.put(42, FlowerSuit.GREEN_FLOWERS);
FLOWER_SUITS.put(56, FlowerSuit.BLUE_FLOWERS);
FLOWER_SUITS.put(70, FlowerSuit.INDIGO_FLOWERS);
FLOWER_SUITS.put(84, FlowerSuit.VIOLET_FLOWERS);
FLOWER_SUITS.put(98, FlowerSuit.WHITE_FLOWERS);
FLOWER_SUITS.put(99, FlowerSuit.BLACK_FLOWERS);
}
public FlowerSuit randomFlowerSuit() {
int index = RANDOMS.nextInt(100);
return FLOWER_SUITS.floorEntry(index).getValue();
}
}
Create just one object of this class, then whenever you want a FlowerSuit, call the randomFlowerSuit method.
The randomFlowerSuit method picks a random number from 0 to 99, then finds an appropriate entry in the map. The floorEntry method chooses an entry whose key is less than or equal to the chosen number. This means that numbers from 0 to 13 get mapped to red, 14 to 27 get mapped to orange, and so on. The only number that gets mapped to white is 98, and the only number that gets mapped to black is 99.
No matter what solution you implement, you want to include a frequency measure in your enum. As an example, you can do something like this:
public enum FlowerSuit {
WHITE_FLOWERS("white", ":white:", "470419377456414720", 1, 1),
YELLOW_FLOWERS("yellow", ":yellow:", "470419561267855360", 1, 20),
// More declarations here
// Add this variable
private final int frequency;
// Do just as you did before in the constructor, but with the frequency
FlowerSuit(String displayName, String emoticon, String id, int value, int frequency){
this.frequency = frequency;
// More assignments here
}
public int getFrequency(){
return frequency;
}
// More getters here
}
This addition is critical, and no matter what method you use to weight flower selection, you will want this addition to your FlowerSuit enum.
Now, we can explore a few different ways to perform this selection.
Note 1: I use ThreadLocalRandom for random numbers in a range, which is from java.util.concurrent.ThreadLocalRandom.
Note 2: For each of these, make a single instance of FlowerPicker, and use the pickFlower() method to pick the next flower. This avoid running costly setup code over and over.
Method 1: Bag of Flowers
This method is probably the easiest to implement. It entails creating a list of enums where each is represented frequency times, and then selecting a random entry from this list. It is similar to throwing a bunch of flowers in a bag, shaking it, and then reaching your hand in and grabbing the first flower you touch. Here's the implementation:
public class FlowerPicker(){
private ArrayList<FlowerSuit> bag;
public FlowerPicker(){
// Get all possible FlowerSuits
FlowerSuit[] options = FlowerSuit.values();
// You can use an array here or an array list with a defined length if you know the total of the frequencies
bag = new ArrayList<FlowerSuit>();
// Add each flower from options frequency times
for (FlowerSuit flower : options)
for (int i=0; i<flower.getFrequency(); i++)
bag.add(flower);
}
public FlowerBag pickFlower(){
// Now, select a random flower from this list
int randomIndex = ThreadLocalRandom.current().nextInt(0, bag.size());
return bag.get(randomIndex);
}
}
This method has the advantage of being simple enough to understand very easily. However, it can be inefficient if your frequencies are extremely specific (like if you want a rainbow flower to be returned 499,999,999 times out of 1,000,000,000). Let's move on to the next method.
Note 1: You could make this better by reducing the fractions representing the frequency of being chosen, but I'll leave this to you.
Note 2: You could also make this slightly better by storing identification numbers, not FlowerSuit objects in the bag list.
Method 2: Navigable Map
This method is a little bit more difficult. It uses a [NavigableMap][1], which is an implementation of [TreeMap][2]. This method is fairly similar to the Bag of Flowers method, but it is a little bit more efficient. Put simply, it uses the TreeMap to give each FlowerSuit a range of numbers that can be selected to return that FlowerSuit. Here's a full example:
public class FlowerPicker(){
private NavigableMap<Double, FlowerSuit> map;
public FlowerPicker(){
// Get all possible FlowerSuits
FlowerSuit[] options = FlowerSuit.values();
map = new TreeMap<Double, FlowerSuit>();
int runningTotal = 0;
// Add each flower with the proper range
for (FlowerSuit flower : options){
runningTotal += flower.getFrequency();
map.put(runningTotal, flower);
}
}
public FlowerBag pickFlower(){
// Now, select a random number and get the flower with this number in its range
int randomRange = ThreadLocalRandom.current().nextInt(0, bag.size());
return map.higherEntry(randomRange).getValue();
}
}
This is a solid method, and it scales well for very specific frequencies. If you have a bunch of different types of flowers, it will be slightly worse, but this method is still a good option at large scales. There's one more option though.
Method 3: Enumerated Distribution
This method is really nice because you barely have to do anything. However, it uses [EnumeratedDistribution][3] from Apache Commons. Enumerated Distribution requires a list of pairs of objects and weights. Anyway, lets jump into it:
public class FlowerPicker(){
private EnumeratedDistribution distribution;
public FlowerPicker(){
// Get all possible FlowerSuits
FlowerSuit[] options = FlowerSuit.values();
List<Pair<FlowerSuit, Double>> weights = new List<Pair<FlowerSuit, Double>>();
// Add each flower and weight to the list
for (FlowerSuit flower : options){
weights.add(new Pair(flower, flower.getFrequency()));
// Construct the actual distribution
distribution = new EnumeratedDistribution(weights);
}
public FlowerBag pickFlower(){
// Now, sample the distribution
return distribution.sample();
}
}
This is my favorite method, simply because so much of it is done for you. Many problems like this have been solved, so why not use solutions that always exist? However, there is some value to writing the solution yourself.
In conclusion, each of these methods are perfectly fine to use at your scale, but I would recommend the second or third method.
I'm looking for a way to dynamically add columns to a vaadin table.
I tried this:
private Button createAddColumnButton() {
Button addProductButton = new Button("Add column");
addProductButton.addClickListener(new ClickListener() {
public void buttonClick(ClickEvent event) {
count = count++;
table.addGeneratedColumn("Column "+count, new ColumnGenerator() {
#Override
public Object generateCell(final Table source, Object itemId, Object columnId) {
String x = "some stuff";
return x;
}
});
}
});
return addProductButton;
}
This button allowed me dynamically add a column, however only one column before I recieved an error saying I cannot have two columns with the same id. How can I change the ID so it is unique & add lots of columns?
TL;DR
Simple change your code to:
count = count + 1;
Explenation
That's beacause assigment
count = count++;
does not work in the way you expect. Take a look at the following code:
public class HelloStackOverflow {
public static void main(String[] args) {
int count = 0;
count = count++;
System.out.println(count);
}
}
This prints on standard output 0. You will even get warning (The assignment to variable count has no effect) if you change your code to:
count = ++count;
You can find even better explanation here.
The purpose of the program is to calculate the volumes of different geometrical figures (Like a cylinder or a pyramid). I've started out by adding a list where the user can choose between the different figures.
The problem is that I don't know how to make the program know which formula to use. I need to be able to separate the choices instead of just making an int out of the answer.
private void btnAktiveraActionPerformed(java.awt.event.ActionEvent evt) {
String form = listForm.getSelectedValue().toString();
int fo = Integer.valueOf( form );
String höjd = txfHöjd.getText().toString();
int hö = Integer.valueOf( höjd );
String bredd = txfBredd.getText().toString();
int br = Integer.valueOf( bredd );
String radie = txfRadie.getText();
int ra = Integer.valueOf(radie);
String djup = txfDjup.getText();
int dj = Integer.valueOf(djup);
double ACyl = 3.14*ra*ra*hö;
double APyr = (br*dj*hö)/2;
double AKub = br*dj*hö;
double ARät = br*dj*hö;
txfHöjd.setEnabled(false);
txfBredd.setEnabled(false);
txfDjup.setEnabled(false);
txfRadie.setEnabled(false);
listForm.setEnabled(false);
}
private void btnBeräknaActionPerformed(java.awt.event.ActionEvent evt) {
// I know this code won't work, its just a reminder.
if (answer == Cyinder){
System.out.print("volymen är: "+ACyl+" cm^3");
}
}
I don't understand your question very clearly. I would suggest to make a plan to solve your problems.
make a list of figures that program will calculate
make a list of methods to count volumes of those figures
create individual classes, variables etc...
create methods
create main method with user input
You mentioned you don't know which formula to use. I assume there won't be many formulas in your program. I would create an individual method for each individual figure i.e. piramidFormula(), cilinderFormula()...
There is no point to refer to polimorphism when I think your level of programming is very basic at this stage.
I hope that will help you a little bit.
You need a list to hold the things, you seem to understand this just fine.
You need a way to select things. Selection is typically not exactly the same thing as the list, you need a class to be responsible for the "selection" behaviour.
Each thing has a routine that can calculate the volume. That means it will need input parameters. This is where it starts to get tricky, because if you want all of your things to be in the same list, you need to decide how to manage the different input parameters for the different types in the list.
public List<VolumeCalculations> volumeCalculations ...
public interface VolumeCalculation {
public double getVolume();
}
public class CubleCalcuation implements VolumeCalculation {
private double side = 0;
public void setSide(double value) {
this.side = value;
}
#Override
public double getVolume() {
return side*side*side;
}
}
the other volume calculations are left as an exercise to you.
Then you need to put them all in the list
volumeCalculations.add(new CubeVolumeCalculation());
...
But when you select the calculation, you will need "something" to ask for the right input.
public interface CalculationInputGather {
public void setCalcualtion(VolumeCalcuation value);
public void askForInputs();
}
which the one for the CubleCalcuation might look like
public CubeInputGather implements CalculationInputGatherer {
#Override
public void setCalculation(VolumeCalcualtion value) {
if (value instanceof CubeCalcuation) {
this.volume = value;
}
throw new IllegalArgumentException("value must be a CubeCalculation");
}
public void askForInputs() {
System.out.println("enter the side value:");
// read the value
volume.setSide(value);
}
}
then when you know the selected item in the list, you can use a Map of Calcuations to their input gatherers to lookup the right input gatherer for the selected calcuation.
If you already have the list for the user to choose from, maybe consider a map instead. You can have all your shapes as the keys of the map and then the formulas for volume as the values of the map. The list of shapes can be provided to the user via the keySet and their response can be matched back against the map to find the formula.
EDIT: You have your formulas for each shape inside an action event. You'll need to move those into a separate class
public static class Formulas() {
// list all formulas here
private String cylinder = "3.14*r*r*h";
}
Then when you hit the action you can either create a new instance of the Formulas class and use any convenience methods you might write in there.