In a java ee application with JSF and JPA,I have to calculate totals of attributes in lists of objects. I have used LambdaJ library to achieve the calculation of sums.
As I use this sort of summations in several places of the application with possible implications to the overall performance, I designed the following test java application to test the efficacy of LambdaJ.
This application demonstrated a gross lack of efficacy of LambdaJ.
Is there any error in the testing programme or should I replace all lambdaJ occurrences with iterations ?
The result
Time taken to calculate a single total by iteration is 15 milliseconds.
Time taken to calculate a single total by LambdaJ is 199 milliseconds.
Time taken to calculate multiple totals by iteration is 23 milliseconds.
Time taken to calculate multiple totals by LambdaJ is 114 milliseconds.
The Main Method
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.lakmedi;
import ch.lambdaj.Lambda;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
/**
*
* #author buddhika
*/
public class main {
public static void main(String[] args) {
List<Bill> bills = new ArrayList<>();
Random r = new Random();
for (int i = 0; i < 100000; i++) {
Bill b = new Bill();
b.setGrossTotal(r.nextDouble());
b.setDiscount(r.nextDouble());
b.setNetTotal(b.getGrossTotal() - b.getDiscount());
bills.add(b);
}
calSingleByIteration(bills);
calSingleByLambdja(bills);
calMultipleByIteration(bills);
calMultipleByLambdja(bills);
}
public static void calSingleByIteration(List<Bill> bills) {
Date startTime = new Date();
double grossTotal = 0.0;
for (Bill b : bills) {
grossTotal += b.getGrossTotal();
}
Date endTime = new Date();
long timeTaken = endTime.getTime() - startTime.getTime();
System.out.println("Time taken to calculate a single total by iteration is " + timeTaken + " milliseconds.");
}
public static void calSingleByLambdja(List<Bill> bills) {
Date startTime = new Date();
double grossTotal = Lambda.sumFrom(bills).getGrossTotal();
Date endTime = new Date();
long timeTaken = endTime.getTime() - startTime.getTime();
System.out.println("Time taken to calculate a single total by LambdaJ is " + timeTaken + " milliseconds.");
}
public static void calMultipleByIteration(List<Bill> bills) {
Date startTime = new Date();
double grossTotal = 0.0;
double discount = 0.0;
double netTotal = 0.0;
for (Bill b : bills) {
grossTotal += b.getGrossTotal();
discount += b.getDiscount();
netTotal += b.getNetTotal();
}
Date endTime = new Date();
long timeTaken = endTime.getTime() - startTime.getTime();
System.out.println("Time taken to calculate multiple totals by iteration is " + timeTaken + " milliseconds.");
}
public static void calMultipleByLambdja(List<Bill> bills) {
Date startTime = new Date();
double grossTotal = Lambda.sumFrom(bills).getGrossTotal();
double discount = Lambda.sumFrom(bills).getDiscount();
double netTotal = Lambda.sumFrom(bills).getNetTotal();
Date endTime = new Date();
long timeTaken = endTime.getTime() - startTime.getTime();
System.out.println("Time taken to calculate multiple totals by LambdaJ is " + timeTaken + " milliseconds.");
}
}
The Bill Class
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.lakmedi;
/**
*
* #author buddhika
*/
public class Bill {
private double grossTotal;
private double netTotal;
private double discount;
public double getGrossTotal() {
return grossTotal;
}
public void setGrossTotal(double grossTotal) {
this.grossTotal = grossTotal;
}
public double getNetTotal() {
return netTotal;
}
public void setNetTotal(double netTotal) {
this.netTotal = netTotal;
}
public double getDiscount() {
return discount;
}
public void setDiscount(double discount) {
this.discount = discount;
}
}
Assuming you mean efficiency and not efficacy, then your measured performance is more or less as expected - see LambdaJ's own performance analysis.
You might get better results using Java 8 Lambda Expressions, but the main goal here is to improve readability and maintainability of source code and not speed. Are you sure that the time needed for performing the summations is critical to the overall performance of the application? I doubt it considering (I/O) waiting times for HTTP-traffic (JSF) and database queries (JPA) in which case this is premature optimization (the root of all evil).
Firstly, check you are using the latest version of LambdaJ, because the project site indicates that recent versions have some performance improvements.
However, I think you should expect lambdaj to be slower than writing the equivalent for-loop. Behind the more concise syntax, the JVM is doing a lot more work.
You could consider dropping lambdaj and using Java 8 streams and lambda expressions.
Here's what the author of lambdaj has to say on the matter:
Lemme clarify this once again: after the release of Java8 I don't see any point in developing/maintaining #lambdaj. Move to Java8 & be happy
-- Mario Fusco, May 2014
Related
This is a very long post, but only the first half is really relevant.
The second half describes only what I have tried to solve it, but what seemed to me too inefficient (it can perhaps help to get the idea of what I want). The relevant part ends with the line after the bolded question.
I am trying to simulate multiple productions in an imaginary factory to calculate what amount of goods of each type known will be available at the end. There are several different goods types, and they have all a specific maximum production capacity that can only be reached if enough ingredients are available. An example of how the production lines could look is here:
The goods at the bottom all have a known rate at which they are delivered to the factory, so for those, there is nothing to calculate, though this rate can change over time (also, the maximum production capacity can also change at any point in time, e.g., the capacity can be increased by adding workers or more machines).
As shown in the picture, for the other goods there are three things to look at:
Some lines produce a good out of a single other one.
Some lines produce a good out of two others.
Some lines have a good used for creation of more than one new good
(see, for example, "R8" in the middle of the illustration).
I have the following information:
Maximum production rate of each good (5 produced per hour, for example)
for the bottom goods we have the amount delivered to the factory (5 delivered per hour, for example)
how much of each is in stock now (so in case there is not enough delivered, if we still have some in stock, we don't need to reduce production)
At what times the delivery of a good will change (can happen to any good at the bottom)
At what times the maximum production rate of a good will change (can happen to any good not at the bottom)
With this information, I want to calculate the amount of each good at a given time in the future. I would like this to be as efficient as possible, since I need these calculations quite often.
I tried to make an implementation for this in Java, but I have some problems. Instead of fixing them I looked at my algorithm again and figured out it did not look as if it was very efficient anyway, so I wanted to know if someone has already seen or solved this kind of problem?
The way I tried to solve this is following:
I create maximum production (and delivery) intervals for each good using the known information when a production (or delivery) amount changes.
I put all resources at the bottom in a remaining Set and a checked Set (bottom goods are immediately checked ones).
I calculate the actual amount of goods produced for each good: therefore, I take each good in remaining and I check what goods can be produced, if all that can be produced only are made of checked goods I calculate the actual amount produced depending of the maximum rate and the available goods (depending on the production of the things it is made of and the amount in stock if this is less). Additionally, in this step I add production intervals if due to lesser production of a source good (but some in stock at the beginning) the production needs to be reduced. When finished the goods the new ones are made of get removed from the remaining Set and the new ones are added, as well as being added to the checked Set.
Now we have all the actual good productions for each good and we can calculate it. For this we loop over each good and take the actual production and add it up using the interval borders for time. We have now the amount of goods at the wanted time in the future.
Additional info: we cannot do the point 4. without 3. since the actual amount we calculate for a good, can be consumed again for the production of the next one, so we need need this Step in between.
If it helps to understand what I have done (or wanted to do) I add my code (not working). The class is already initialized with the maximum production rate intervals of each produced good currently in production. Since other goods can be in stock, for all goods that are not included we initialize them to with a production of zero and one interval.
public class FactoryGoods {
private long future;
private long now;
private Map<String, Integer> availableGoods;
private Map<String, ArrayList<ProductionInterval>> hourlyGoodIncrease;
/**
*
* #param future long current time
* #param now long last time the factory's resources got updates
* #param availableGoods Map<String,Integer> of the goods in the factory
* #param hourlyGoodIncrease Map<String,ArrayList<ProductionInterval>> of the intervals of production quantities for the goods
*/
public factoryGoods(long future, long now, Map<String,Integer> availableGoods, Map<String,ArrayList<ProductionInterval>> hourlyGoodIncrease) {
this.future = future;
this.now = now;
this.availableGoods = availableGoods;
this.hourlyGoodIncrease = hourlyGoodIncrease;
}
/**
* Calculates the resources present in a factory's storage
* #return a Map of quantities mapped on the String name of the good
*/
public Map<String,Integer> getResources() {
// Make sure all goods to have all goods inside the loop, to work on goods,
// that are produced, but also those which are only in store
HashMap<String, Boolean> goodChecked = new HashMap<String,Boolean>();
Set<String> remaining = new HashSet<String>();
for (Goods good: Goods.values()) {
String g = good.get();
if (hourlyGoodIncrease.get(g) == null) {
ArrayList<ProductionInterval> prods = new ArrayList<ProductionInterval>();
ProductionInterval start = new ProductionInterval(now, 0);
prods.add(start);
hourlyGoodIncrease.put(g, prods);
}
if (availableGoods.get(g) == null) {
availableGoods.put(g, 0);
}
if (good.isPrimary()) {
goodChecked.put(g, true);
} else {
goodChecked.put(g, false);
}
remaining.add(g);
}
// As long as goods are remaining to be checked loops over the goods, and
// recalculates hourly good increases for goods, that have all its sources
// already calculated
while (remaining.size() > 0) {
Set<String> removes = new HashSet<String>();
for (String good: remaining) {
if (goodChecked.get(good)) {
Good g = GoodFactory.get(good);
Set<String> to = new HashSet<String>();
Map<String,Float> from = new HashMap<String,Float>();
setUpFromAndToGoods(g, to, from, availableGoods);
if (areGoodsAlreadyCalculated(to, goodChecked)) {
//remaining.remove(good);
removes.add(good);
} else {
if (areGoodsReadyForCalculation(to, goodChecked)) {
// Get all resources we are working on now
Map<String,Float> fromDecrease = new HashMap<String,Float>();
for (String t: to) {
for (String f: GoodFactory.get(t).isMadeFrom().keySet()) {
from.put(f, (float) availableGoods.get(f));
}
}
// Get all interval borders
ArrayList<Long> intervalBorders = new ArrayList<Long>();
for (String wGood: from.keySet()) {
ArrayList<ProductionInterval> intervals = hourlyGoodIncrease.get(wGood);
for (ProductionInterval interval: intervals) {
long intervalStart = interval.getStartTime();
if (!intervalBorders.contains(intervalStart)) {
intervalBorders.add(intervalStart);
}
}
}
Collections.sort(intervalBorders);
intervalBorders.add(future);
for (String f: from.keySet()) {
hourlyGoodIncrease.put(f, createNewProductionIntervalls(intervalBorders, hourlyGoodIncrease.get(f)));
}
// For all intervals
int iLast = intervalBorders.size() - 1;
for (int i = 0; i < iLast; i++) {
long elapsedTime = intervalBorders.get(i + 1) - intervalBorders.get(i);
for (String t: to) {
Map<String, Float> source = GoodFactory.get(t).isMadeFrom();
for (String s: source.keySet()) {
Float decrease = fromDecrease.get(s);
fromDecrease.put(s, (decrease != null ? decrease : 0) + source.get(s));
}
}
// Calculate amount after normal maximum production
Set<String> negatives = new HashSet<String>();
Map<String,Float> nextFrom = new HashMap<String,Float>();
for (String f: from.keySet()) {
float delta = from.get(f) + (hourlyGoodIncrease.get(f).get(i).getHourlyIncrease() - fromDecrease.get(f)) * elapsedTime / (1000 * 60 * 60);
nextFrom.put(f, delta);
if (delta < 0) {
negatives.add(f);
}
}
// Check if got under zero
if (negatives.size() == 0) {
for (String f: from.keySet()) {
float newIncrease = hourlyGoodIncrease.get(f).get(i).getHourlyIncrease() - fromDecrease.get(f);
hourlyGoodIncrease.get(f).get(i).setHourlyIncrease(newIncrease);
from.put(f, nextFrom.get(f));
}
} else {
// TODO: handle case when more is used than exists
}
// Else calculate point where at least one from is zero and add an interval
// before its maximum, after needs to be adjusted
}
// Break to remove all calculated goods from the remaining set and rerun the loop
removes = to;
break;
}
}
}
}
for (String remove: removes) {
remaining.remove(remove);
}
}
// Final calculation of the goods amounts that are available in the factory
for (String good: goodChecked.keySet()) {
ArrayList<ProductionInterval> intervals = hourlyGoodIncrease.get(good);
intervals.add(new ProductionInterval(future, 0));
float after = availableGoods.get(good);
for (int i = 0; i < (intervals.size() - 1); i++) {
after += intervals.get(i).getHourlyIncrease() * (intervals.get(i + 1).getStartTime() - intervals.get(i).getStartTime()) / (1000 * 60 * 60);
}
availableGoods.put(good, (int) after);
}
return availableGoods;
}
private static ArrayList<ProductionInterval> createNewProductionIntervalls(ArrayList<Long> intervalBorders, ArrayList<ProductionInterval> hourlyIncreases) {
System.out.print("intervalBorders\n");
System.out.print(intervalBorders + "\n");
System.out.print("hourlyIncreases\n");
System.out.print(hourlyIncreases + "\n");
ArrayList<ProductionInterval> intervalls = new ArrayList<ProductionInterval>();
int i = 0;
long iTime = 0;
long nextTime = 0;
for (long l: intervalBorders) {
float increase = 0;
iTime = hourlyIncreases.get(i).getStartTime();
if (i + 1 < hourlyIncreases.size()) {
nextTime = hourlyIncreases.get(i + 1).getStartTime();
}
if (l == iTime) {
increase = hourlyIncreases.get(i).getHourlyIncrease();
} else if (iTime < l && l < nextTime) {
increase = hourlyIncreases.get(i).getHourlyIncrease();
} else if (l == nextTime) {
increase = hourlyIncreases.get(++i).getHourlyIncrease();
}
intervalls.add(new ProductionInterval(l, increase));
}
return intervalls;
}
private static void setUpFromAndToGoods(Good g, Set<String> to, Map<String,Float> from, Map<String,Integer> availableGoods) {
Set<String> unchecked = g.isUsedToCreate();
while (unchecked.size() > 0) {
String good = unchecked.iterator().next();
unchecked.remove(good);
to.add(good);
Set<String> madeFrom = GoodFactory.get(good).isMadeFrom().keySet();
for (String fromGood: madeFrom) {
if (!from.containsKey(fromGood)) {
from.put(fromGood, (float) availableGoods.get(fromGood));
Set<String> additions = GoodFactory.get(fromGood).isUsedToCreate();
for (String addition: additions) {
if (!to.contains(addition) && !unchecked.contains(addition)) {
unchecked.add(addition);
}
}
}
}
}
}
private static boolean areGoodsReadyForCalculation(Set<String> toGoods, Map<String,Boolean> goodChecked) {
for (String t: toGoods) {
Good toGood = GoodFactory.get(t);
for (String from: toGood.isMadeFrom().keySet()) {
if (!goodChecked.get(from)) {
return false;
}
}
}
return true;
}
private static boolean areGoodsAlreadyCalculated(Set<String> toGoods, Map<String,Boolean> goodChecked) {
for (String t: toGoods) {
if (!goodChecked.get(t)) {
return false;
}
}
return true;
}
}
public class ProductionInterval {
private long startTime;
private float hourlyIncrease;
public ProductionInterval(long startTime, float hourlyIncrease) {
this.setStartTime(startTime);
this.setHourlyIncrease(hourlyIncrease);
}
public float getHourlyIncrease() {
return hourlyIncrease;
}
public void setHourlyIncrease(float hourlyIncrease) {
this.hourlyIncrease = hourlyIncrease;
}
public long getStartTime() {
return startTime;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
public String toString() {
return "<starttime=" + this.startTime + ", hourlyIncrease=" + this.hourlyIncrease + ">";
}
}
Does someone know an algorithm that can solve my problem, or have some ideas how I can change my algorithm so that it gets more efficient? (I know it does not work at all, but with all these loops, I don't think it will be efficient and I would like to know if someone sees something I could make better before I put the work into finishing it).
You can apply a max flow algorithm like the Edmonds-Karp with few modifications, and you need to build the graph to feed to the algo:
Create a node for each good
You need one "source" node and one "sink" node
For each delivered good, create an arc from the source to respective node, with the capacity equal to delivery rate
For each final product, create an arc from its respective node to the sink, with capacity equal to production rate
For each dependency between goods, create an arc between respective nodes with capacity of one.
For each good, create an arc from source to the respective node with capacity equal to amount of the good in stock (for first iteration it's zero)
The results will be the flows from final goods nodes to the sink after the algorithm is finished. For your case, you need two modifications:
When calculating flow at a node, you take the minimum of the flows to it (since you require all dependencies to create a good), and then cap it at this good's maximum production rate for non-delivered goods
You need to account for change of goods in stock - will edit the answer later
Although, this algorithm is offline, which means it's not suited for flows changing over time, it's relatively simple, and if you're not too constrained by performance requirements, it may work - just run the algo again after adjusting the capacities. For online max flow, you can look at this,
Working out my idea of fractional simulation in C++ (sorry). Please see heavily commented code below.
(I know the prioritization in the face of constrained resources isn't what you want. It's not trivial to get a fair implementation of Derivative that produces as much as it can, so I wanted to validate this approach before going down the rabbit hole.)
#include <cassert>
#include <iostream>
#include <limits>
#include <utility>
#include <vector>
// Identifies a type of good in some Factory.
using Good = int;
// Simulates a factory. The simulation is crude, assuming continuous,
// fractional, zero-latency production. Nevertheless it may be accurate enough
// for high production volumes over long periods of time.
class Factory {
public:
// Declares a new raw material. `delivery_rate` is the amount of this good
// delivered per hour.
Good NewRawMaterial(double stock, double delivery_rate) {
assert(stock >= 0.0);
assert(delivery_rate >= 0.0);
return NewGood(stock, delivery_rate, {});
}
// Declares a new manufactured good. `max_production_rate` is the max amount
// of this good produced per hour. Producing one of this good consumes one
// `input`.
Good NewManufacturedGood(double stock, double max_production_rate,
Good input) {
assert(stock >= 0.0);
assert(max_production_rate >= 0.0);
return NewGood(stock, max_production_rate, {input});
}
// Declares a new manufactured good. `max_production_rate` is the max amount
// of this good produced per hour. Producing one of this good consumes one
// `input_a` and one `input_b`.
Good NewManufacturedGood(double stock, double max_production_rate,
Good input_a, Good input_b) {
assert(stock >= 0.0);
assert(max_production_rate >= 0.0);
return NewGood(stock, max_production_rate, {input_a, input_b});
}
// Returns the number of hours since the start of the simulation.
double Now() const { return current_time_; }
// Advances the current time to `time` hours since the start of the
// simulation.
void AdvanceTo(double time);
// Returns the amount of `good` in stock as of the current time.
double Stock(Good good) const { return stock_[good]; }
// Sets the delivery rate of `good` to `delivery_rate` as of the current time.
void SetDeliveryRate(Good good, double delivery_rate) {
assert(delivery_rate >= 0.0);
max_production_rate_[good] = delivery_rate;
}
// Sets the max production rate of `good` to `max_production_rate` as of the
// current time.
void SetMaxProductionRate(Good good, double max_production_rate) {
assert(max_production_rate >= 0.0);
max_production_rate_[good] = max_production_rate;
}
private:
// Floating-point tolerance.
static constexpr double kEpsilon = 1e-06;
// Declares a new good. We handle raw materials as goods with no inputs.
Good NewGood(double stock, double max_production_rate,
std::vector<Good> inputs) {
assert(stock >= 0.0);
assert(max_production_rate >= 0.0);
Good good = stock_.size();
stock_.push_back(stock);
max_production_rate_.push_back(max_production_rate);
inputs_.push_back(std::move(inputs));
return good;
}
// Returns the right-hand derivative of stock.
std::vector<double> Derivative() const;
// Returns the next time at which a good is newly out of stock, or positive
// infinity if there is no such time.
double NextStockOutTime(const std::vector<double> &derivative) const;
// The current time, in hours since the start of the simulation.
double current_time_ = 0.0;
// `stock_[good]` is the amount of `good` in stock at the current time.
std::vector<double> stock_;
// `max_production_rate_[good]` is the max production rate of `good` at the
// current time.
std::vector<double> max_production_rate_;
// `inputs_[good]` is the list of goods required to produce `good` (empty for
// raw materials).
std::vector<std::vector<Good>> inputs_;
// Derivative of `stock_`.
std::vector<double> stock_rate_;
};
void Factory::AdvanceTo(double time) {
assert(time >= current_time_);
bool caught_up = false;
while (!caught_up) {
auto derivative = Derivative();
double next_time = NextStockOutTime(derivative);
if (time <= next_time) {
next_time = time;
caught_up = true;
}
for (Good good = 0; good < stock_.size(); good++) {
stock_[good] += (next_time - current_time_) * derivative[good];
}
current_time_ = next_time;
}
}
std::vector<double> Factory::Derivative() const {
// TODO: this code prioritizes limited supply by the order in which production
// is declared. You probably want to use linear programming or something.
std::vector<double> derivative = max_production_rate_;
for (Good good = 0; good < stock_.size(); good++) {
for (Good input : inputs_[good]) {
if (stock_[input] <= kEpsilon) {
derivative[good] = std::min(derivative[good], derivative[input]);
}
}
for (Good input : inputs_[good]) {
derivative[input] -= derivative[good];
}
}
return derivative;
}
double Factory::NextStockOutTime(const std::vector<double> &derivative) const {
double duration = std::numeric_limits<double>::infinity();
for (Good good = 0; good < stock_.size(); good++) {
if (stock_[good] > kEpsilon && derivative[good] < -kEpsilon) {
duration = std::min(duration, stock_[good] / -derivative[good]);
}
}
return current_time_ + duration;
}
int main() {
Factory factory;
Good r1 = factory.NewRawMaterial(60.0, 3.0);
Good r2 = factory.NewRawMaterial(20.0, 1.0);
Good r3 = factory.NewManufacturedGood(0.0, 2.0, r1);
Good r4 = factory.NewManufacturedGood(0.0, 1.0, r1, r2);
auto print_stocks = [&]() {
std::cout << "t : " << factory.Now() << "\n";
std::cout << "r1: " << factory.Stock(r1) << "\n";
std::cout << "r2: " << factory.Stock(r2) << "\n";
std::cout << "r3: " << factory.Stock(r3) << "\n";
std::cout << "r4: " << factory.Stock(r4) << "\n";
std::cout << "\n";
};
print_stocks();
// Everything running smoothly
factory.AdvanceTo(24.0);
print_stocks();
// Uh oh, r1 supply cut off. Stock out at 44 hours.
factory.SetDeliveryRate(r1, 0.0);
factory.AdvanceTo(48.0);
print_stocks();
// r1 supply at 50%. r3 production prioritized.
factory.SetDeliveryRate(r1, 1.5);
factory.AdvanceTo(72.0);
print_stocks();
// r1 oversupplied.
factory.SetDeliveryRate(r1, 4.0);
factory.AdvanceTo(96.0);
print_stocks();
}
Output:
t : 0
r1: 60
r2: 20
r3: 0
r4: 0
t : 24
r1: 60
r2: 20
r3: 48
r4: 24
t : 48
r1: 0
r2: 24
r3: 88
r4: 44
t : 72
r1: 0
r2: 48
r3: 124
r4: 44
t : 96
r1: 24
r2: 48
r3: 172
r4: 68
I am writing a stats object. I want to track the average with minimal information
I currently have a method:
public synchronized void setSuccessful(long time) {
int succCount = _successful.get(); // atomic int
_successful.incrementAndGet();
long maxTime = _maxSuccessfulTimeNano.get(); // atomic long
if (time > maxTime) {
_maxSuccessfulTimeNano.set(time);
}
long avg = ((_avgSuggessfulTimeNano.get() /* atomic long */ * succCount) + time) / (succCount + 1);
_avgSuggessfulTimeNano.set(avg);
}
Is there a better way of implementing this? or is there a better stats to acquire
I'm trying to calculate the number of complete contiguous Periods in an Interval in Joda Time (where the Period is arbitrary but constant).
The simple solution I've come up with is a linear search using a while loop:
public static long periodsInAnInterval(Interval interval, Period period) {
int periods = -1;
DateTime marker = interval.getStart();
while (marker.isBefore(interval.getEnd()) || marker.isEqual(interval.getEnd())) {
marker = marker.plus(period);
periods++;
}
return periods;
}
An O(n) solution is obviously pretty horrible, so can anyone think of a better way? I'm wondering whether some kind of binary search could be used...
Here's a test case: https://gist.github.com/Mahoney/9899832
Edit - remember a Period does not have a known number of seconds; Period.toStandardDuration() is just an approximation assuming years have 365 days, months have 30 days and days have 24 hours. (Actually a quick test reveals Period.toStandardDuration bombs out with an exception if you have years or months in the period.)
Edit 2 - I'm happy to assume that the first period begins at the start of the interval - otherwise I suspect the answer might vary depending on whether the remainder time were at the beginning, the end or both.
Here's my preferred solution: use average length of a period to form a best guess and then refine it. This seems the most efficient and elegant way to do it.
import com.google.common.base.Function;
import com.google.common.collect.ImmutableMap;
import org.joda.time.*;
import static com.google.common.collect.FluentIterable.from;
import static java.util.Arrays.asList;
import static org.joda.time.DurationFieldType.*;
public class PeriodArithmetic {
public static long periodsInAnInterval(Interval interval, Period period) {
int bestGuess = (int) (interval.toDurationMillis() / toAverageMillis(period));
if (bestGuess < 0) return 0;
if (startPlusScaledPeriodIsAfterEnd(interval, period, bestGuess + 1)) {
return searchDownwards(interval, period, bestGuess);
} else {
return searchUpwards(interval, period, bestGuess);
}
}
private static long searchDownwards(Interval interval, Period period, int currentGuess) {
if (startPlusScaledPeriodIsAfterEnd(interval, period, currentGuess)) {
return searchDownwards(interval, period, currentGuess - 1);
} else {
return currentGuess;
}
}
private static long searchUpwards(Interval interval, Period period, int currentGuess) {
if (!startPlusScaledPeriodIsAfterEnd(interval, period, currentGuess + 1)) {
return searchUpwards(interval, period, currentGuess + 1);
} else {
return currentGuess;
}
}
private static boolean startPlusScaledPeriodIsAfterEnd(Interval interval, Period period, int scalar) {
return interval.getStart().plus(period.multipliedBy(scalar)).isAfter(interval.getEnd());
}
private static final long MILLIS_IN_DAY = Days.ONE.toStandardSeconds().getSeconds() * 1000L;
private static final long MILLIS_IN_YEAR = Days.ONE.toStandardSeconds().getSeconds() * 365250L;
private static final ImmutableMap<DurationFieldType, Long> averageLengthMillis
= ImmutableMap.<DurationFieldType, Long>builder()
.put(millis(), 1L)
.put(seconds(), 1000L)
.put(minutes(), Minutes.ONE.toStandardSeconds().getSeconds() * 1000L)
.put(hours(), Hours.ONE.toStandardSeconds().getSeconds() * 1000L)
.put(halfdays(), MILLIS_IN_DAY / 2)
.put(days(), MILLIS_IN_DAY)
.put(weeks(), Weeks.ONE.toStandardSeconds().getSeconds() * 1000L)
.put(months(), MILLIS_IN_YEAR / 12)
.put(years(), MILLIS_IN_YEAR)
.put(weekyears(), MILLIS_IN_YEAR)
.put(centuries(), MILLIS_IN_YEAR * 100)
.put(eras(), Long.MAX_VALUE)
.build();
private static long toAverageMillis(Period period) {
final Iterable<Long> milliValues = from(asList(period.getFieldTypes())).transform(toAverageMillisForFieldType(period));
return total(milliValues);
}
private static Function<DurationFieldType, Long> toAverageMillisForFieldType(final Period period) {
return new Function<DurationFieldType, Long>() {
#Override
public Long apply(DurationFieldType durationFieldType) {
final Long averageDuration = averageLengthMillis.get(durationFieldType);
return period.get(durationFieldType) * averageDuration;
}
};
}
private static long total(Iterable<Long> milliValues) {
long acc = 0;
for (Long milliValue : milliValues) {
acc += milliValue;
}
return acc;
}
}
I've put the beginnings of a binary search solution here: https://gist.github.com/Mahoney/9899936
It's much more complicated than the linear search; on the other hand it's roughly 100x faster at finding the number of months in 1,000 years, for instance.
It's also unfinished - more of an experiment than anything, so I'm sure there are untested edge cases (negative periods?).
Still keen to know if anyone has a more elegant solution (or just one I don't have to write, test & maintain!).
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
I have 3 classes. I'm learning about interfaces and the interface class TravelCost must have public abstract and the method type and name so that it is consistent through all three classes. The three classes (AirTravelCost, TrainTravelCost, CarTravelCost) will implement TravelCost. I have all that set and tested to work. However, the test page which I assume is where you input a search through the arrayList for the lowest cost and shortest duration. I don't know how to do this as I've never done this before in an ArrayList. Here the sample code in the test class:
import java.util.*;
public class TestTravelCost
{
public static void main(String [] args)
{
/*Scanner scn = new Scanner(System.in); //scanner object
System.out.println("Number of Miles: ");
double numOfMiles = scn.nextDouble();
System.out.println("Hotel cost per night: ");
double cost = scn.nextDouble();
System.out.println("Description: ");
String description = scn.nextLine();*/
TravelCost c = new CarTravelCost(400, 200, "Boston");//instantiate object for car travel
TravelCost t = new TrainTravelCost(6, 60.0, "Boston"); //instantiate object for train travel
TravelCost a = new AirTravelCost(224, "20110103", "0743" , "20110103", "1153", "Boston");//instantiate object for air travel
ArrayList<TravelCost> AL = new ArrayList<TravelCost>();//array list for car travel
AL.add(c);
AL.add(t);
AL.add(a);
for(TravelCost tc : AL)
{
System.out.println(tc.toString());
}
}
}
Output:
Car travel to Boston will take 7.2727272727272725 hours and cost 210.0
Train travel to Boston will take 6.0 hours and cost 70.0
Air travel to Boston will take 1.0166666666666666 and cost 243.48888888888888 //this is not the correct calculation, I don't know where I'm wrong but its suppose to be the same as the shortest duration.I guess I'm not good at math.
Here's the calculation method I used for Air Travel
public double getDuration()
{
//---DEPARTURE---//
int Dyear = Integer.parseInt(departureDate.substring(0,3)); //2011
int Dmonth = Integer.parseInt(departureDate.substring(4,5));//01
int Dday = Integer.parseInt(departureDate.substring(6,7));//03
int Dhour = Integer.parseInt(departureTime.substring(0,1));//0743
int Dminute = Integer.parseInt(departureTime.substring(2,3));//1153
//---ARRIVAL---//
int Ayear = Integer.parseInt(arrivalDate.substring(0,3)); //2011
int Amonth = Integer.parseInt(arrivalDate.substring(4,5));//01
int Aday = Integer.parseInt(arrivalDate.substring(6,7));//03
int Ahour = Integer.parseInt(arrivalTime.substring(0,1));//0743
int Aminute = Integer.parseInt(arrivalTime.substring(2,3));//1153
GregorianCalendar date = new GregorianCalendar(Dyear, Dmonth, Dday, Dhour, Dminute);//departure date & time
GregorianCalendar time = new GregorianCalendar(Ayear, Amonth, Aday, Ahour, Aminute);//arrival date & time
//date = arrivalDate - departureDate;//2011-01-03 - 2011-01-03 = 0
//time = arrivalTime - departureTime;//0734 - 1153 = 410
double duration = (Math.abs(date.getTimeInMillis() - time.getTimeInMillis()) / 60000.0) / 60.0;
return duration;
`enter code here` }
How do I get this result in my code?
LOWEST COST: Train Travel to Boston will take 11.0 hours and cost 70.0
SHORTEST DURATION: Air Travel to Boston will take 4.166666666666667 hours cost 234.0
You're not showing the TravelCost interface, but to achieve what you want it should at least have a getDuration and getCost method.
public interface TravelCost {
... // what you already have in the interface definition
public double getDuration();
public double getCost();
}
Armed with that, I'd create a small dummy class implementing the basics to be comparable on these properties:
public DummyTC implements TravelCost {
private double cost;
private double duration;
public DummyTC(double cost, double duration) {
this.cost = cost;
this.duration = duration;
}
public double getDuration() {
return duration;
}
public double getCost() {
return cost;
}
// and other methods/properties imposed by TravelCost
}
This will allow you to find what you're looking for:
// instantiate 2 DummyTC's with impossibly high cost &durations
TravelCost cheapest = new DummyTC(99999999999.99, 99999999999.99);
TravelCost shortest = new DummyTC(99999999999.99, 99999999999.99);
// iterate over the List
for(TravelCost tc : AL) {
// if present tc is cheaper than cheapest, swap
if ( tc.getCost() < cheapest.getCost() ) {
cheapest = tc;
}
// if present tc is shorter than shortest, swap
if ( tc.getDuration() < shortest.getDuration() ) {
shortest = tc;
}
}
// at this point cheapest and shortest will contain the cheapest and shortest
// ways of transportation, so we print them out.
System.out.println(cheapest.toString());
System.out.println(shortest.toString());
Another thing, your date handling code is horribly convoluted. Have a look at SimpleDateFormat
Date date = null;
Date time = null;
// create a SimpleDateFormat instance for your time/date format
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
try {
// parse it
date = format.parse(departureDate);
// done
} catch (ParseException e) {
// departureDate could not be parsed, you should handle that case here
}
try {
// parse it
time = format.parse(arrivalTime);
// done
} catch (ParseException e) {
// arrivalTime could not be parsed, you should handle that case here
}
As Date also has a routine to get the epoch-millis you can continue with the code you already have, although a long is probably a better return-type than a double in this case.
How to compute accurately the time it takes a Java program to write or read a number of bytes from/to a file ?
It is really important that the time is being measured accurately. (The time should be computed by the program itself).
The standard idiom is:
long startTime = System.nanoTime();
doSomething();
long elapsedTime = System.nanoTime() - startTime;
not tested, but something like:
long delta = System.nanoTime();
try {
// do your stuff
} finally {
delta = System.nanoTime() - delta;
}
There is a code sample here:
http://www.goldb.org/stopwatchjava.html
/*
Copyright (c) 2005, Corey Goldberg
StopWatch.java is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
*/
public class StopWatch {
private long startTime = 0;
private long stopTime = 0;
private boolean running = false;
public void start() {
this.startTime = System.currentTimeMillis();
this.running = true;
}
public void stop() {
this.stopTime = System.currentTimeMillis();
this.running = false;
}
//elaspsed time in milliseconds
public long getElapsedTime() {
long elapsed;
if (running) {
elapsed = (System.currentTimeMillis() - startTime);
}
else {
elapsed = (stopTime - startTime);
}
return elapsed;
}
//elaspsed time in seconds
public long getElapsedTimeSecs() {
long elapsed;
if (running) {
elapsed = ((System.currentTimeMillis() - startTime) / 1000);
}
else {
elapsed = ((stopTime - startTime) / 1000);
}
return elapsed;
}
//sample usage
public static void main(String[] args) {
StopWatch s = new StopWatch();
s.start();
//code you want to time goes here
s.stop();
System.out.println("elapsed time in milliseconds: " + s.getElapsedTime());
}
}
The way I would do that is just run it in a loop some number of times. Like if you run it 1000 times and clock it, that gives you milliseconds. Run it 1,000,000 times, and it gives you microseconds.
If you also want to find out why it's taking as long as it is, you can just pause it some number of times (like 10) while it's running, and that will tell you what it's doing and why.
The problem with the get System.xxx method is that the method itself needs a few milliseconds to compute. The usually "accepted" way of doing it is running the test a few tens of thousands of times and calculating an average of this.
Also, depending on your OS there is something called the time granularity (example for windows). This is the smallest amount of time your OS can compute. On some OS its a millisecond, on some others its a nanosecond. It might or might not be relevant in your case.