In a case like this:
public class Order {
List<Double> prices = List.of(1.00, 10.00, 100.00);
List<Double> pricesWithTax = List.of(1.22, 12.20, 120.00);
Double sumBy(/* method reference */) {
Double sum = 0.0;
for (Double price : /* method reference */) {
sum += price;
}
return sum;
}
public List<Double> getPrices() { return prices; }
public List<Double> getPricesWithTax() { return pricesWithTax; }
}
how can I declare the sumBy method in a way that can be called like this:
Order order = new Order();
var sum = order.sumBy(order::getPrices);
var sumWithTaxes = order.sumBy(order::getPricesWithTax);
I'm not using the Java 8 API for the sum because my goal is only to understand how pass a method reference.
Your 2 methods take no argument and return an object, so that fits the Supplier.get() method.
Don't use Double for the sum variable, since that will auto-box and auto-unbox way too much.
Method can be static since it doesn't use any fields or other methods of the class.
static double sumBy(Supplier<List<Double>> listGetter) {
double sum = 0.0;
for (double price : listGetter.get()) {
sum += price;
}
return sum;
}
Better yet:
static double sumBy(Supplier<List<Double>> listGetter) {
return listGetter.get().stream().mapToDouble(Double::doubleValue).sum();
}
You seem to want a Supplier like
Double sumBy(Supplier<List<Double>> f) {
Double sum = 0.0;
for (Double price : f.get()) {
sum += price;
}
return sum;
}
Your List.of syntax was giving me errors. So I did
List<Double> prices = Arrays.asList(1.00, 10.00, 100.00);
List<Double> pricesWithTax = Arrays.asList(1.22, 12.20, 120.00);
Then I tested like
public static void main(String[] args) throws IOException {
Order order = new Order();
double sum = order.sumBy(order::getPrices);
double sumWithTaxes = order.sumBy(order::getPricesWithTax);
System.out.printf("%.2f %.2f%n", sum, sumWithTaxes);
}
Outputs
111.00 133.42
I think the Supplier<T> functional interface is what you’re looking for:
Double sumBy(Supplier<Collection<Double>> supplier) {
Collection<Double> prices = supplier.get();
}
Use Double sumBy(Supplier<List<Double>> doubles)
Related
How can I accumulate inside a forEach? this is my code:
public Double accumulate() {
Double accum = 0d;
numbers.stream().forEach(p-> {
// if condition
accum = accum + numbers.getAmount();
// else
accum = accum + numbers.getAmountWithInterest();
});
return accum;
}
Maybe I should use map instead of forEach, I tried a couple of things but it didn't work. Thanks
I do not think it is a good idea to make side effect when using lambda. It is a bad way of mixing functional and imperative programming. You can do it easier by
numbers.stream().mapToInt(p-> {
// if condition
return numbers.getAmount();
// else
return numbers.getAmountWithInterest();
}).sum();
You can use mapToDouble and sum instead of forEach with Java Streams:
#Test
public void sumOfDoubleWithStreams()
{
List<Amount> numbers =
Arrays.asList(new Amount(10.0, 0.0), new Amount(20.0, 0.05), new Amount(30.0, 0.1));
double sum = numbers.stream()
.mapToDouble(
amount -> amount.hasInterest() ? amount.getAmountWithInterest() : amount.getAmount())
.sum();
Assert.assertEquals(64.0d, sum, 0.0d);
}
If you really want to use forEach, you need to have an object that can be mutated inside of the forEach call. You can use AtomicDouble here, or another class like DoubleSummaryStatistics or your own accumulating class.
#Test
public void sumOfDoubleWithForEach()
{
List<Amount> numbers =
Arrays.asList(new Amount(10.0, 0.0), new Amount(20.0, 0.05), new Amount(30.0, 0.1));
AtomicDouble sum = new AtomicDouble();
numbers.forEach(amount ->
sum.addAndGet(amount.hasInterest() ? amount.getAmountWithInterest() : amount.getAmount()));
Assert.assertEquals(64.0d, sum.get(), 0.0d);
}
If you are open to using a third-party library, you can use sumOfDouble on any container from Eclipse Collections.
#Test
public void sumOfDouble()
{
MutableList<Amount> numbers =
Lists.mutable.with(new Amount(10.0, 0.0), new Amount(20.0, 0.05), new Amount(30.0, 0.1));
double sum = numbers.sumOfDouble(
amount -> amount.hasInterest() ? amount.getAmountWithInterest() : amount.getAmount());
Assert.assertEquals(64.0d, sum, 0.0d);
}
I created an Amount class to hold the amount and interest rate:
public static final class Amount
{
private final double amount;
private final double interest;
public Amount(double amount, double interest)
{
this.amount = amount;
this.interest = interest;
}
public double getAmountWithInterest()
{
return this.amount * (1.0 + this.interest);
}
public boolean hasInterest()
{
return this.interest > 0.0d;
}
public double getAmount()
{
return amount;
}
}
Note: I am a committer for Eclipse Collections.
i have this algorithm that i want to change it with parallel streams so it makes less time for the calculation, but when i did it i got the error Local variable defined in an enclosing scope must be final for realiseValeur, nbValid and invalidite.So how can i work with parelle streams in this algorithm.
This is my algorithm in which i want to work with parallelstreams :
#Override
public Map<String, Double> getMapRealise(Date date, String code, Long pc) {
Map<String, Double> map = new HashMap<>();
List<Period> periodList = this.getListPeriod(date, code);
Double realiseValeur = 0.0;
Double invalidite = 0.0;
if (periodList != null) {
for (Period period : periodList) {
Double periode = this.getResolutionTraduiteEnHeures(period.getResolution().getV());
// Date dateDebutPrevisionnel =
// this.getDateDebutPrevisionnel(period.getTimeInterval().getV());
Double nbValid = 0.0;
for (Pt pt : period.getListPt()) {
realiseValeur += periode * pt.getQ().getV() / pcnTranche / NBR_HEURES_PAR_JOURS;
nbValid = nbValid + pt.getCq().getV();
}
if ((nbValid * periode) < NBR_HEURES_MINE_PAR_JOURS) {
invalidite++;
}
}
}
else {
LOGGER.warn( "n existe pas ");
}
map.put(REALISE_VALEUR, realiseValeur);
map.put(REALISE_INVALIDITE, invalidite);
return map;}
I tried this but i got the error Local variable defined in an enclosing scope must be final for realiseValeur, nbValid and invalidite:
#Override
public Map<String, Double> getMapRealise(Date date, String code, Long pc) {
Map<String, Double> map = new HashMap<>();
List<Period> periodList = this.getListPeriod(date, code);
Double realiseValeur = 0.0;
Double invalidite = 0.0;
if (periodList != null) {
periodList.parallelStream().forEach(period -> {
Double periode = this.getResolutionTraduiteEnHeures(period.getResolution().getV());
// Date dateDebutPrevisionnel =
// this.getDateDebutPrevisionnel(period.getTimeInterval().getV());
Double nbValid = 0.0;
period.getListPt().parallelStream().forEach(pt -> {
realiseValeur += periode * pt.getQ().getV() / pcnTranche / NBR_HEURES_PAR_JOURS;
nbValid = nbValid + pt.getCq().getV();
});
if ((nbValid * periode) < NBR_HEURES_MINE_PAR_JOURS) {
invalidite++;
}
});
}
else {
LOGGER.warn("n existe pas ");
}
map.put(REALISE_VALEUR, realiseValeur);
map.put(REALISE_INVALIDITE, invalidite);
return map;
}
You need to return something from each iteration of the outer loop which has the incremental values for each of the iterations.
Stream<Map<String, Double>> fromParallel =
periodList.parallelStream()
.map(period -> {
double realiseValeurInc = 0.0;
double invaliditeInc = 0.0;
// Increment these variables instead of realiseValeur and invalidite
return Map.of(REALISE_VALEUR, realiseValeurInc, REALISE_INVALIDITE, invaliditeInc);
});
Now just merge all of these maps together:
Map<String, Double> result =
fromParallel.reduce(
(a, b) -> Map.of(
REALISE_VALEUR, a.get(REALISE_VALEUR) + b.get(REALISE_VALEUR),
REALISE_INVALIDITE, a.get(REALISE_INVALIDITE) + b.get(REALISE_INVALIDITE)));
(You don't have to do this in two separate steps, that's merely to explain the process.)
And you can use this as the return value (or copy this to a HashMap, if that's what you really need).
Of course, this is wildly inefficient, because of all the allocation of both Double and Map. It will probably be slower than the existing loop-based code. But it will at least give you the correct answer (eventually).
You can use AtomicInteger for this operation. since you are using Double for your opeartions. create your own AtomicFloat and use it instead of Double and your problem will be solved :)
import java.util.concurrent.atomic.AtomicInteger;
import static java.lang.Float.*;
class AtomicFloat extends Number {
private AtomicInteger bits;
public AtomicFloat() {
this(0f);
}
public AtomicFloat(float initialValue) {
bits = new AtomicInteger(floatToIntBits(initialValue));
}
public final boolean compareAndSet(float expect, float update) {
return bits.compareAndSet(floatToIntBits(expect),
floatToIntBits(update));
}
public final void set(float newValue) {
bits.set(floatToIntBits(newValue));
}
public final float get() {
return intBitsToFloat(bits.get());
}
public float floatValue() {
return get();
}
public final float getAndSet(float newValue) {
return intBitsToFloat(bits.getAndSet(floatToIntBits(newValue)));
}
public final boolean weakCompareAndSet(float expect, float update) {
return bits.weakCompareAndSet(floatToIntBits(expect),
floatToIntBits(update));
}
public double doubleValue() { return (double) floatValue(); }
public int intValue() { return (int) get(); }
public long longValue() { return (long) get(); }
}
I have a custom Object Itemized which has two fields amount and tax. I have an array of Itemized objects and I am looking to sum the two fields in the same stream. Below is how I am calculating the sum of both the fields.
double totalAmount = Arrays.stream(getCharges()).map(Itemized::getAmount).reduce(0.0, Double::sum));
double totalTax = Arrays.stream(getCharges()).map(Itemized::getTax).reduce(0.0, Double::sum));
Is there any way I don't have to parse the stream two times and can sum the two fields in one go ? I am not looking to sum totalTax and totalAmount but want their sum separately. I was looking at Collectors but was not able to find any example which would allow aggregating of multiple fields in one go.
use a for loop ?
double taxSum = 0;
double amountSum = 0;
for (Itemized itemized : getCharges()) {
taxSum += itemized.getTax();
amountSum += itemized.getAmount();
}
You can try to use the teeing Collector, like so:
Arrays.stream(getCharges()) // Get the charges as a stream
.collect(Collectors // collect
.teeing( // both of the following:
Collectors.summingDouble(Itemized::getAmount), // first, the amounts
Collectors.summingDouble(Itemized::getTax), // second, the sums
Map::entry // and combine them as an Entry
)
);
This should give you a Map.Entry<Double, Double> with the sum of amounts as the key and the sum of tax as the value, which you can extract.
Edit:
Tested and compiled it - it works. Here we go:
ItemizedTest.java
public class ItemizedTest
{
static Itemized[] getCharges()
{
// sums should be first param = 30.6, second param = 75
return new Itemized[] { new Itemized(10, 20), new Itemized(10.4,22), new Itemized(10.2, 33) };
}
public static void main(String[] args)
{
Map.Entry<Double, Double> sums = Arrays.stream(getCharges())
.collect(Collectors
.teeing(
Collectors.summingDouble(Itemized::getAmount),
Collectors.summingDouble(Itemized::getTax),
Map::entry
)
);
System.out.println("sum of amounts: "+sums.getKey());
System.out.println("sum of tax: "+sums.getValue());
}
}
Itemized.java
public final class Itemized
{
final double amount;
final double tax;
public double getAmount()
{
return amount;
}
public double getTax()
{
return tax;
}
public Itemized(double amount, double tax)
{
super();
this.amount = amount;
this.tax = tax;
}
}
Output:
sum of amounts: 30.6
sum of tax: 75.0
P.S.: teeing Collector is only available in Java 12+.
Instead of summing by field, you define a custom object to hold both field's sum values:
ItemizedValues {
private double amount;
private double tax;
public static final ItemizedValues EMPTY = new ItemizedValues(0, 0);
// Constructor - getter - setter
public static ItemizedValues of(Itemized item) {
return new ItemizedValues(amount, tax);
}
public static ItemizedValues sum(ItemizedValues a, ItemizedValues b) {
// Sum the fields respectively
// It's your choice to reuse the existing instances, modify the values or create a brand new one
}
}
Arrays.stream(getCharges())
.map(ItemizedValues::of)
.reduce(ItemizedValues.EMPTY, ItemizedValues::sum);
With a data structure that can allow one to accumulate both sums, you can reduce the stream to a single object.
This is using double[] to hold totalAmount at index 0 and totalTax at index 1 (other options include SimpleEntry, Pair):
double[] res = Arrays.stream(getCharges())
.map(ch -> new double[] { ch.getAmount(), ch.getTax() })
.reduce(new double[] { 0, 0 },
(a1, a2) -> new double[] { a1[0] + a2[0], a1[1] + a2[1] });
double totalAmount = res[0],
totalTax = res[1];
You can do it by using Entry but still you will end up in creating lot of objects, the best solution i would suggest is for loop answered by NimChimpsky
Entry<Double, Double> entry = Arrays.stream(new Itemized[] {i1,i2})
.map(it->new AbstractMap.SimpleEntry<>(it.getAmount(), it.getTax()))
.reduce(new AbstractMap.SimpleEntry<>(0.0,0.0),
(a,b)->new AbstractMap.SimpleEntry<>(a.getKey()+b.getKey(),a.getValue()+b.getValue()));
System.out.println("Amount : "+entry.getKey());
System.out.println("Tax : "+entry.getValue());
In your specific case, it's could be done by using your Itemized class as value holder.
Itemized result = Arrays.stream(getCharges())
.reduce(new Itemized(), (acc, item) -> {
acc.setAmount(acc.getAmount() + item.getAmount());
acc.setTax(acc.getTax() + item.getTax());
return acc;
});
double totalAmount = result.getAmount();
double totalTax = result.getTax();
how do i store the result of "week_result" i calculate into an array
public double calculation() {
`week_result = round(((inWatts/100)*use_hours*3600/1000)*0.54,2);
Toast.makeText(getApplicationContext(),
"weekly : " + week_result, Toast.LENGTH_LONG).show();
return week_result;
}
public static double round(double value, int places) {
if (places < 0) throw new IllegalArgumentException();
BigDecimal bd = new BigDecimal(value);
bd = bd.setScale(places, RoundingMode.HALF_UP);
return bd.doubleValue();
}
}
and how can i add up all the values stored in this array in another class ....
Use an ArrayList as it will increase in size as and when more space is needed
ArrayList<Double> results = new ArrayList<Double>();
Add a result to the list using
results.add(result);
When you want to find the sum of the ArrayList use a foreach loop
sumList(ArrayList<Double> results) {
double total = 0.0;
for(double result : results) {
total += result;
}
return total;
}
And call sumList(results);
You can do:
ArrayList<Double> arr = new ArrayList<Double>();
To add elements to array in your case
double week_result = round(((inWatts/100)*use_hours*3600/1000)*0.54,2);
arr.add(week_result);
And to access this array from other classes, declare a getter method to return this ArrayList
public ArrayList<Double> getArray() {
return arr;
}
As your function calculation() returns a value of type double, you can simply store this value in an array, or any variable, of type double, for example:
public double[] values = new double[10];
values[0] = calculation();
Do you see anything wrong in this code? in thosen't work well it returns a NaN.
public class Method2 extends GUIct1
{
double x=0,y=0;
void settype1 (double conv1)
{
x = conv1;
}
void settype2 (double conv2)
{
y = conv2;
}
double conversion ( double amount)
{
double converted = (amount*y)/x;
return converted;
}
}
Way it is used an i already changed the set part
Method2 convert = new Method2(); \\ method is called
.....
convert.settype1(j);
.....
convert.settype2(k);
.....
double x = convert.conversion(i);
System.out.println(x);
Well, the fact that you've got methods which set variables called get-something is pretty obviously not a good idea, and there's no indentation... but it should work. But then, you haven't shown how you're using it. Perhaps you're not actually called the setter methods?
Here's an example of the same code but with different names, and a sample of using it:
class Converter
{
double multiplier = 0;
double divisor = 0;
void setMultiplier(double multiplier)
{
this.multiplier = multiplier;
}
void setDivisor(double divisor)
{
this.divisor = divisor;
}
double convert(double amount)
{
return (amount * multiplier) / divisor;
}
}
public class Test
{
public static void main(String[] args)
{
Converter converter = new Converter();
converter.setMultiplier(3.5);
converter.setDivisor(8.5);
System.out.println(converter.convert(2)); // Prints 0.8235294117647058
}
}
Personally I'd probably make the variables final and set them in the constructor, but that's another matter...
It doesn't look like you ever call gettype1 or gettype2 so the x/y is 0/0 resulting in NaN
This can happen when x=0. Division by zero is not defined. Print out x and y before you start to calculate to see whetther they are not zero.