Say that I have this class:
public class Bucket<T> {
T filling;
}
and the filling attribute can be an instance of either one of these:
public class Oil{
float volume;
}
or
public class Water{
float volume;
}
In one part of my code, I have a list of Buckets:
LinkedList<Bucket> list;
Now, I want to order the elements of "list"(Bucket) by the attribute "volume".
But I can't compare T.volume. So, how do I do that?
Sorry in advance if my question is stupid, I'm still learning java.
I answer you assuming this is a theoritical question, I do not advise you to implement that but to find a better pattern to do what you want.
You want to sort a List of Bucket. To call Collections.sort() method, Bucket needs to implements Comparable (and so to define compareTo method).
You should define an interface implemented by your "filling" elements
interface Element {
float getVolume();
void setVolume(float volume);
}
class Oil implements Element {
float volume;
#Override
public float getVolume() {
return volume;
}
#Override
public void setVolume(float volume) {
this.volume = volume;
}
}
class Water implements Element {
float volume;
#Override
public float getVolume() {
return volume;
}
#Override
public void setVolume(float volume) {
this.volume = volume;
}
}
Now you can define Bucket class:
class Bucket implements Comparable<Bucket> {
Element filling;
#Override
public int compareTo(Bucket o) {
return Float.compare(filling.getVolume(), o.filling.getVolume());
}
}
And this code:
public static void main(String[] args) {
List<Bucket> elems = new LinkedList<>();
Bucket o = new Bucket();
o.filling = new Oil();
o.filling.setVolume(5);
Bucket w = new Bucket();
w.filling = new Water();
w.filling.setVolume(12);
elems.add(w);
elems.add(o);
Collections.sort(elems);
for(Bucket b: elems) {
System.out.println(b.filling.getVolume());
}
}
Will print:
5.0
12.0
Shame on me I wrote the whole code, but it's easier to understand by reading this one than reading a bad explanation I could write.
Related
My Java program is not sorting the outputs. I am not experienced with Java and after looking through similar questions I wasn't able to figure out the issue with my code.
The output shows the 3 spheres and their colors but does not display the radius or sort them by their area like it should.
Below are the 3 .java files involved in my program, I have no errors or warnings in Eclipse so I am assuming I have put some parameter or value in the wrong place... I appreciate any help thanks so much!
ComparableSphere.java
public class ComparableSphere extends GeometricObject implements Comparable<ComparableSphere>{
private double radius;
public ComparableSphere(){
this("white",0);
this.radius = 0;
}
public ComparableSphere(String color, double radius){
super(color);
this.radius = radius;
}
public double area() {
return Math.PI * this.radius * this.radius * 4;
}
public double perimeter() {
return 2 * Math.PI * this.radius;
}
#Override
public int compareTo(ComparableSphere o) {
// TODO Auto-generated method stub
return 0;
}
}
GeometricObject.java
public abstract class GeometricObject {
private String color;
protected GeometricObject(){
this("white");
}
protected GeometricObject(String color) {
this.color = color;
}
public String getColor(){
return this.color;
}
public void setColor(String color){
this.color = color;
}
public abstract double area();
public abstract double perimeter();
public String toString() {
return this.getClass().getSimpleName() + ": color= " + this.color;
}
public boolean equals(Object obj) {
if(!(obj instanceof GeometricObject)){
return false;
}
GeometricObject other = (GeometricObject)obj;
return this.color.equalsIgnoreCase(other.color);
}
}
driver.java
import java.util.ArrayList;
import java.util.Collections;
public class driver {
public static void main(String[] args){
ComparableSphere sphere1 = new ComparableSphere("Purple", 10.1);
ComparableSphere sphere2 = new ComparableSphere("Orange", 3.8);
ComparableSphere sphere3 = new ComparableSphere("Tan", 5.2);
ArrayList<ComparableSphere> sphereList = new ArrayList<ComparableSphere>();
sphereList.add(sphere1);
sphereList.add(sphere2);
sphereList.add(sphere3);
System.out.println("Unsorted list: \n"+sphereList+"\n");
Collections.sort(sphereList);
System.out.println("Sorted list: \n"+sphereList);
}
}
enter image description here
Your compareTo always returns 0, so that means that you are considering all ComparableSphere objects equal to each other, according to the compareTo contract:
Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
This means that Collections.sort thinks it has nothing to do, because all the objects are equal to each other.
You need to write the logic in the compareTo method to make the comparison between this object and the passed-in object and return an appropriate value. This will give Collections.sort the information it needs to sort the list properly.
After researching a little bit, I couldn't figure out how to create a obj1 distance to be able to compare with obj2. All these methods were given in assessment I had so, no chance to change logic of it. I suppose to return 3 Strings answer depending of the data. Thanks a lot in advance guys. I've attached a pease of pic.
enter image description here
public class Main {
public static void main(String[] args) {
Distance dist1 = new DistanceImplementation();
Distance obj2 = new DistanceImplementation();
dist1.setFeetAndInches(1, 8);
obj2.setFeetAndInches(3, 5);
System.out.println(dist1.getDistanceComparison(obj2));
}
}
public abstract class Distance {
protected int feet;
protected float inches;
abstract public void setFeetAndInches(int feet, float inches);
abstract public int getFeet();
abstract public float getInches();
abstract String getDistanceComparison(Distance dist2);
}
class DistanceImplementation extends Distance {
#Override
public void setFeetAndInches(int feet, float inches) {
this.feet = feet;
this.inches = inches;
}
#Override
public int getFeet() {
return this.feet;
}
#Override
public float getInches() {
return this.inches;
}
#Override
String getDistanceComparison(Distance dist2) {
// if (dist2) { ????????????
return null;
}
}
Well, after reading the assessment, I think that you can safely assume that 1 foot = 12 inches. So, in order to correctly implement the getDistanceComparison method, you could calculate the total distance in inches for both the current object and the parameter, compare them and then return the corresponding string value.
Suppose you have the following method:
private float getTotalInches() {
return (float) feet * 12.0 + inches;
}
This method returns the total inches of this DistanceImplementation instance, taking into account the feet and the inches attributes.
Please note that for the total result to be of type float, we need to first cast the feet attribute to float, so that it actually becomes of type float. Then, we multiply by 12.0 (note the .0, it's important because it indicates that the 12.0 literal value is also a float). Then, we are summing two float values, which yields a result of type float. While all this casting and convertions are not always necessary (sometimes the compiler is smart enough as to guess the correct types and preserve decimal precision), it's considred good practice to make your intentions crystal-clear, so that future developers that will maintain your code know what you have tried to accomplish.
Then, once you have this method, it would be easy to compare the total inches of both DistanceImplementation instances and return the corresponding string:
#Override
String getDistanceComparison(Distance dist2) {
float myTotalInches = getTotalInches();
float otherTotalInches = dist2.getTotalInches();
if (myTotalInches > otherTotalInches) {
// return ...
} else if (myTotalInches < otherTotalInches) {
// return ...
} else {
// return ...
}
}
Here is the solution on which I was working and it might be useful as well
package com.prog;
import java.util.Scanner;
abstract class Distance {
protected int feet;
protected float inches;
abstract public void setFeetAndInches(int feet, float inches);
abstract public int getFeet();
abstract public float getInches();
abstract String getDistanceComparison(Distance dist2);
}
public class DistanceCalculator {
private static final Scanner scan = new Scanner(System.in);
public static void main(String[] args) {
Distance dist1 = new DistanceImplementation();
Distance dist2 = new DistanceImplementation();
int feet1 = 1;
float inches1 = (float) 2.0;
int feet2 = 3;
float inches2 = (float) 4.1;
dist1.setFeetAndInches(feet1, inches1);
dist2.setFeetAndInches(feet2, inches2);
System.out.println(dist1.getDistanceComparison(dist2));
}
}
package com.prog;
public class DistanceImplementation extends Distance {
#Override
public void setFeetAndInches(int feet, float inches) {
this.feet=(int) (feet+ (inches/12));
this.inches=inches+ (feet*12);
}
#Override
public int getFeet() {
return feet;
}
#Override
public float getInches() {
return inches;
}
#Override
String getDistanceComparison(Distance dist2) {
String ret;
int dist1a=this.getFeet();
System.out.println(dist1a);
int dist2a=dist2.getFeet();
if(dist1a > dist2a)
return "First is greater";
else if(dist1a < dist2a)
return "Second is greater";
else
return "Both are equal";
}
}
Is there a way to combine the following 2 Statesments?
Map<Integer,Double> collX = listeAllerPunkte.stream().collect(groupingBy(DataPoint::getId,
averagingDouble(DataPoint::getX)));
Map<Integer,Double> collY = listeAllerPunkte.stream().collect(groupingBy(DataPoint::getId,
averagingDouble(DataPoint::getY)));
I have a Class DataPoints like this:
public class DataPoint {
public final double x;
public final double y;
private int Id;
public DataPoint(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public int getId() {
return Id;
}
}
The Id contains a Random value between 0-5.
listeAllerPunkte is a List with a lot of DataPoints
Now I want to create a DataPoint for each DataPoints in the List with the same Id. The DataPoint should have the average of the x and y values of the Datapoints with the same Id.
With the two Statemantes from the Beginning i must create the DataPoints manually out of the two Maps.
Is there a way to create them directly in the stream?
A general solution would be using a collector which can combine two collectors to process both at once. Unfortunately, such collector does not exist in the standard API, but this answer provide an implementation of such a collector.
Alternatively, you can create a solution for this specific case by creating your own class for holding the summary of points, e.g.
static class DataPointSummary {
long count;
double sumX, sumY;
public double getAverageX() {
return count==0? 0: sumX/count;
}
public double getAverageY() {
return count==0? 0: sumY/count;
}
public void add(DataPoint p) {
count++;
sumX+=p.getX();
sumY+=p.getY();
}
public DataPointSummary merge(DataPointSummary s) {
count+=s.count;
sumX+=s.sumX;
sumY+=s.sumY;
return this;
}
#Override
public String toString() {
return "DataPointSummary["+count+" points"
+", avg x="+getAverageX()+", avg y="+getAverageY()+']';
}
}
Then you may collect your points like
Map<Integer,DataPointSummary> coll = listeAllerPunkte.stream().collect(
groupingBy(DataPoint::getId, Collector.of(
DataPointSummary::new, DataPointSummary::add, DataPointSummary::merge)));
Note that I assumed that you method signature public double getId() is a typo and actually public int getId() as otherwise, the examples in your question won’t work.
The summary implementation above works well if the coordinates of the points have the same magnitude. If you encounter both, very large values and very small values within the same group, you may need a summing with error compensation algorithm. Instead of implementing it yourself, I recommend using the summary implementation of the JRE instead:
static class DataPointSummary {
final DoubleSummaryStatistics x=new DoubleSummaryStatistics();
final DoubleSummaryStatistics y=new DoubleSummaryStatistics();
public double getAverageX() {
return x.getAverage();
}
public double getAverageY() {
return y.getAverage();
}
public void add(DataPoint p) {
x.accept(p.getX());
y.accept(p.getY());
}
public DataPointSummary merge(DataPointSummary s) {
x.combine(s.x);
y.combine(s.y);
return this;
}
#Override
public String toString() {
return "DataPointSummary["+x.getCount()+" points"
+", avg x="+getAverageX()+", avg y="+getAverageY()+']';
}
}
This variant is used the same way as the first one.
So I have an ArrayList of objects. Inside those objects are various attributes and their values.
The code is pretty simple. GBox and GCircle are childs of GHP. The ArrayList is in World.
What I want to do is print the HP and volume of the box and the HP and diameter of the circle. I understand I could override toString() but I actually want to get the values. What's the correct syntax to do so?
//Main.java
public class Main {
public static void main(String[] args) {
Ini i = new Ini();
}
}
//Ini.java
public class Ini {
private static World w;
public Ini() {
w = new World;
w.makeGBox();
w.makeGCircle();
System.out.println("Box: HP: " +
w.getList().get(0).getHP() +
"Volume: " +
w.getList().get(0).GBox.getVolume());
//compile error no variable GBox in GHP
System.out.println("Circle: HP: " +
w.getList().get(1).getHP() +
"Radius: " +
w.getList().get(1).GCircle.getRadius());
//compile error no variable GCircle in GHP
}
}
//World.java
import java.util.ArrayList;
public class World {
private ArrayList<GHP> list = new ArrayList<>();
public void makeGBox() {
list.add(new GBox());
}
public void makeGCircle() {
list.add(new GCircle());
}
public ArrayList<GHP> getList() {
return list;
}
}
//GHP.java
public class GHP {
private int HP;
public GHP() {
setHP(5);
}
public int getHP() {
return HP;
}
public void setHP(int HP) {
this.HP = HP;
}
}
//GBox.java
public class GBox extends GHP{
private int volume;
public GBox() {
setVolume(10);
}
public int getVolume() {
return volume;
}
public void setVolume(int volume) {
this.volume = volume;
}
}
//GCircle.java
public class GCircle extends GHP{
private int radius;
public GCircle {
setRadius(7);
}
public int getRadius() {
return radius;
}
public void setRadius(int radius) {
this.radius = radius;
}
}
Apart from the many compilation problems, you need these changes to achieve what you want.
for (GHP ghp : w.getList()) { // Avoid using get(index) without a forloop, as such
if (ghp instanceof GBox) { // Using the instanceof operator, you can differentiate the 2 class types
System.out.println("Box: HP: " + ghp.getHP() + "Volume: "
+ ((GBox) ghp).getVolume()); // Cast it to GBox to be able to call getVolume
}
if (ghp instanceof GCircle) {
System.out.println("Circle: HP: " + ghp.getHP() + "Radius: "
+ ((GCircle) ghp).getRadius());// Cast it to GCircle to be able to call getRadius
}
}
You would need to cast the generic GHP reference to the specific type like:
((GCircle) ghp).getRadius()
You might also want to have a look on instanceof operator.
The idea being:
for output you override the toString() method because you don't need any class specific information, just print out object details
for class-specific operations you downcast to he specific type
When you read the list values, the only thing the compiler knows, is that the list contains GHP instances.
First check the type and then cast it to the subclass.
GHP ghp = w.getList().get(0);
if(ghp instanceof GBox) {
GBox gbox = (GBox) ghp;
// Here you can access the method getVolume()
/* ... */ gbox.getVolume();
}
A cleaner, more OOP alternative to adding methods to the base class and/or instanceof checks is to use the Visitor pattern that allows you to separate your object structure from any algorithms that operate on them. The algorithm in this case is simply a "display" algorithm.
That said, for most simple cases (like this one) adding methods to the base class and overriding or using instanceof is fine.
List<Shape> shapes = new ArrayList<Shape>();
....
...
for (Shape shape : shapes) {
System.out.println(shape.getHp());
if(shape instanceof Circle){
System.out.println(((Circle) shape).getValuem());
}else if(shape instanceof Box){
System.out.println(((Box) shape).getHieght());
}
try this way..
This is how I add a line to my chart at the moment. This is the abstract class for an arbitrry funciton I want to display:
public abstract class ArbitraryFunction implements
ValueProvider<ArbitraryFunctionData, Double> {
private String field;
public abstract Double f(Double x);
/**
* Constructor
*/
public ArbitraryFunction(String field) {
this.field = field;
}
#Override
public Double getValue(ArbitraryFunctionData object) {
return object.get(field);
}
#Override
public void setValue(ArbitraryFunctionData object, Double value) {
object.put(field, value);
}
#Override
public String getPath() {
return field;
}
}
This is how the chart is created:
ArbitraryFunction f1 = new ArbitraryFunction("f1") {
#Override
public Double f(Double x) {
return Math.sin(x);
}
};
functionMap.put(f1.getPath(), f1);
// collects the data of the functions and adds them to the store
for (Double x = 0.0; x <= 2 * Math.PI; x = x + 0.1) {
ArbitraryFunctionData d = new ArbitraryFunctionData();
d.setName("" + x);
for (Map.Entry<String, ArbitraryFunction> entry : functionMap.entrySet()) {
ArbitraryFunction tmp = entry.getValue();
d.put(tmp.getPath(), tmp.f(x));
}
store.add(d);
}
chart.setStore(store);
verticalAxis.setPosition(Position.LEFT);
verticalAxis.addField(f1);
verticalAxis.setSteps(2);
verticalAxis.setMinorTickSteps(5);
chart.addAxis(verticalAxis);
This works so far as intended. The graph shows my lines as it should do it and the vertical axis is correct too. But I have problems drawing the horizontal axis since I don't know what I need to give horizontalAxis.addField( ??? ). I've tried a few things, but nothing worked.
Does anyone know how I need to set up the horizontal axis?
What do you want the horizontal axis value to be? Is it another NumericAxis - does each data point have a x value that it should be drawn on? Each d in your loop has a String name and some value - perhaps you want a CategoryAxis<ArbitraryFunctionData, String> that just draws those name values?
Looks like I misunderstood earlier - your Function objects are just used in setup, not in changing how you plot data
I'm still not sure what you are after, but it sounds like you mostly want to plot some lines. Each data point (ArbitraryFunctionData?) seems to have Y values for each function being used, and a title, but no X values, so there is no way to plot each point as (X,Y) with two numeric axes, just as (name, Y) using a CategoryAxis and a NumericAxis. This would end up more or less like this sample: http://www.sencha.com/examples/#ExamplePlace:linechart - strings along the bottom, and numbers along the side.
Here's take one, build mostly off of the idea/structure you already have:
public class FunctionPlotter implements EntryPoint {
public static class ArbitraryFunctionData {
private double xValue;
private Map<String, Double> yValues = new HashMap<String, Double>();
public double get(String key) {
return yValues.get(key);
}
public void put(String key, double yValue) {
yValues.put(key, yValue);
}
public double getXValue() {
return xValue;
}
public void setxValue(double xValue) {
this.xValue = xValue;
}
}
public interface AFDProperties extends PropertyAccess<ArbitraryFunctionData> {
//xvalue is unique, key off of that
#Path("xValue")
ModelKeyProvider<ArbitraryFunctionData> key();
//automatic ValueProvider generation for the get/setXValue methods
ValueProvider<ArbitraryFunctionData, Double> xValue();
}
/**
* This is really doing two different jobs at once - wasn't quite was I was trying to suggest in
* that other question. See the second version of this for clarification...
*/
public static abstract class ArbitraryFunction implements ValueProvider<ArbitraryFunctionData, Double> {
private final String field;
public ArbitraryFunction(String field) {
this.field = field;
}
public abstract Double f(Double x);
#Override
public Double getValue(ArbitraryFunctionData object) {
return object.get(field);
}
#Override
public void setValue(ArbitraryFunctionData object, Double value) {
object.put(field, value);
}
#Override
public String getPath() {
return field;
}
}
#Override
public void onModuleLoad() {
Viewport vp = new Viewport();
Set<ArbitraryFunction> functions = new HashSet<ArbitraryFunction>();
ArbitraryFunction f1 = new ArbitraryFunction("f1") {
#Override
public Double f(Double x) {
return Math.sin(x);
}
};
functions.add(f1);
AFDProperties props = GWT.create(AFDProperties.class);
ListStore<ArbitraryFunctionData> store = new ListStore<ArbitraryFunctionData>(props.key());
// collects the data of the functions and adds them to the store
for (Double x = 0.0; x <= 2 * Math.PI; x = x + 0.1) {
// Create one data object, and set the X value, since that is the same for all Y values
ArbitraryFunctionData d = new ArbitraryFunctionData();
d.setxValue(x);
// For each function, set the corresponding Y value
for (ArbitraryFunction func : functions) {
d.put(func.getPath(), func.f(x));
}
store.add(d);
}
Chart<ArbitraryFunctionData> chart = new Chart<ArbitraryFunctionData>();
chart.setStore(store);
//Y-axis
NumericAxis<ArbitraryFunctionData> verticalAxis = new NumericAxis<ArbitraryFunctionData>();
verticalAxis.setPosition(Position.LEFT);
verticalAxis.addField(f1);//needs to know this field to properly set the range of values
//f2, f3, etc
verticalAxis.setSteps(2);
verticalAxis.setMinorTickSteps(5);
chart.addAxis(verticalAxis);
// X-Axis, this time reading from the xValue, not the series of ValueProviders
NumericAxis<ArbitraryFunctionData> horizAxis = new NumericAxis<ArbitraryFunctionData>();
horizAxis.setPosition(Position.BOTTOM);
horizAxis.addField(props.xValue());//same value for all
horizAxis.setSteps(2);
horizAxis.setMinorTickSteps(5);
chart.addAxis(horizAxis);
for (ArbitraryFunction func : functions) {
LineSeries<ArbitraryFunctionData> line = new LineSeries<ArbitraryFunctionData>();
// configure x axis
line.setXAxisPosition(Position.BOTTOM);//where is it
line.setXField(props.xValue());//what value do i use
// configure y axis
line.setYAxisPosition(Position.LEFT);//where is it
line.setYField(func);//what value do i use
//probably want to customized per func
line.setStroke(RGB.GRAY);
line.setStrokeWidth(2);
chart.addSeries(line);
}
vp.setWidget(chart);
RootPanel.get().add(vp);
}
}
And here's take two, this time with much simpler data and actually making the Function its own ValueProvider, and keeping the data dirt simple - just a double! Note that the ValueProvider is the function, and we never call getValue ourselves, we let the axis/series do it for us! Added a second function here to demonstrate that it does actually work.
public class FunctionPlotter implements EntryPoint {
/**
* Where did everything go? We're just making a ValueProvider now that can handle
* each number as a value, and working out the details from there
*
* For fun, added per-function coloring too
*/
public abstract static class Function implements ValueProvider<Double, Double> {
private final String name;
private final Color color;
public Function(String name, Color color) {
this.name = name;
this.color = color;
}
#Override
public abstract Double getValue(Double object);
#Override
public String getPath() {
return name;
}
#Override
public void setValue(Double object, Double value) {
//no-op
}
public Color getColor() {
return color;
}
}
#Override
public void onModuleLoad() {
Viewport vp = new Viewport();
Set<Function> functions = new HashSet<Function>();
Function f1 = new Function("f1", RGB.RED) {
#Override
public Double getValue(Double x) {
return Math.sin(x);
}
};
functions.add(f1);
Function f2 = new Function("f2", RGB.BLACK) {
#Override
public Double getValue(Double x) {
return Math.cos(x);
}
};
functions.add(f2);
//Turns out Stores can hold any objects - should probably factor out this key provider for reuse...
ListStore<Double> store = new ListStore<Double>(new ModelKeyProvider<Double>() {
#Override
public String getKey(Double item) {
return item.toString();
}
});
// collects the data of the functions and adds them to the store
for (Double x = 0.0; x <= 2 * Math.PI; x = x + 0.1) {
store.add(x);
}
Chart<Double> chart = new Chart<Double>();
chart.setStore(store);
//Y-axis
NumericAxis<Double> verticalAxis = new NumericAxis<Double>();
verticalAxis.setPosition(Position.LEFT);
for (Function func : functions) {
verticalAxis.addField(func);//needs to know this field to properly set the range of values
}
verticalAxis.setSteps(2);
verticalAxis.setMinorTickSteps(5);
chart.addAxis(verticalAxis);
// X-Axis, this time reading from the xValue, not the series of ValueProviders
NumericAxis<Double> horizAxis = new NumericAxis<Double>();
horizAxis.setPosition(Position.BOTTOM);
horizAxis.addField(new IdentityValueProvider<Double>());//magic value provider that returns the same string
horizAxis.setSteps(2);
horizAxis.setMinorTickSteps(5);
chart.addAxis(horizAxis);
for (Function func : functions) {
LineSeries<Double> line = new LineSeries<Double>();
// configure x axis
line.setXAxisPosition(Position.BOTTOM);//where is it
line.setXField(new IdentityValueProvider<Double>());//what value do i use
// configure y axis
line.setYAxisPosition(Position.LEFT);//where is it
line.setYField(func);//what value do i use
//probably want to customized per func
line.setStroke(func.getColor());
line.setStrokeWidth(2);
chart.addSeries(line);
}
vp.setWidget(chart);
RootPanel.get().add(vp);
}
}