Modifying local variable from inside lambda - java

Modifying a local variable in forEach gives a compile error:
Normal
int ordinal = 0;
for (Example s : list) {
s.setOrdinal(ordinal);
ordinal++;
}
With Lambda
int ordinal = 0;
list.forEach(s -> {
s.setOrdinal(ordinal);
ordinal++;
});
Any idea how to resolve this?

Use a wrapper
Any kind of wrapper is good.
With Java 10+, use this construct as it's very easy to setup:
var wrapper = new Object(){ int ordinal = 0; };
list.forEach(s -> {
s.setOrdinal(wrapper.ordinal++);
});
With Java 8+, use either an AtomicInteger:
AtomicInteger ordinal = new AtomicInteger(0);
list.forEach(s -> {
s.setOrdinal(ordinal.getAndIncrement());
});
... or an array:
int[] ordinal = { 0 };
list.forEach(s -> {
s.setOrdinal(ordinal[0]++);
});
Note: be very careful if you use a parallel stream. You might not end up with the expected result. Other solutions like Stuart's might be more adapted for those cases.
For types other than int
Of course, this is still valid for types other than int.
For instance, with Java 10+:
var wrapper = new Object(){ String value = ""; };
list.forEach(s->{
wrapper.value += "blah";
});
Or if you're stuck with Java 8 or 9, use the same kind of construct as we did above, but with an AtomicReference...
AtomicReference<String> value = new AtomicReference<>("");
list.forEach(s -> {
value.set(value.get() + s);
});
... or an array:
String[] value = { "" };
list.forEach(s-> {
value[0] += s;
});

This is fairly close to an XY problem. That is, the question being asked is essentially how to mutate a captured local variable from a lambda. But the actual task at hand is how to number the elements of a list.
In my experience, upward of 80% of the time there is a question of how to mutate a captured local from within a lambda, there's a better way to proceed. Usually this involves reduction, but in this case the technique of running a stream over the list indexes applies well:
IntStream.range(0, list.size())
.forEach(i -> list.get(i).setOrdinal(i));

If you only need to pass the value from the outside into the lambda, and not get it out, you can do it with a regular anonymous class instead of a lambda:
list.forEach(new Consumer<Example>() {
int ordinal = 0;
public void accept(Example s) {
s.setOrdinal(ordinal);
ordinal++;
}
});

As the used variables from outside the lamda have to be (implicitly) final, you have to use something like AtomicInteger or write your own data structure.
See
https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#accessing-local-variables.

An alternative to AtomicInteger is to use an array (or any other object able to store a value):
final int ordinal[] = new int[] { 0 };
list.forEach ( s -> s.setOrdinal ( ordinal[ 0 ]++ ) );
But see the Stuart's answer: there might be a better way to deal with your case.

Yes, you can modify local variables from inside lambdas (in the way shown by the other answers), but you should not do it. Lambdas have been made for functional style of programming and this means: No side effects. What you want to do is considered bad style. It is also dangerous in case of parallel streams.
You should either find a solution without side effects or use a traditional for loop.

If you are on Java 10, you can use var for that:
var ordinal = new Object() { int value; };
list.forEach(s -> {
s.setOrdinal(ordinal.value);
ordinal.value++;
});

You can wrap it up to workaround the compiler but please remember that side effects in lambdas are discouraged.
To quote the javadoc
Side-effects in behavioral parameters to stream operations are, in general, discouraged, as they can often lead to unwitting violations of the statelessness requirement
A small number of stream operations, such as forEach() and peek(), can operate only via side-effects; these should be used with care

I had a slightly different problem. Instead of incrementing a local variable in the forEach, I needed to assign an object to the local variable.
I solved this by defining a private inner domain class that wraps both the list I want to iterate over (countryList) and the output I hope to get from that list (foundCountry). Then using Java 8 "forEach", I iterate over the list field, and when the object I want is found, I assign that object to the output field. So this assigns a value to a field of the local variable, not changing the local variable itself. I believe that since the local variable itself is not changed, the compiler doesn't complain. I can then use the value that I captured in the output field, outside of the list.
Domain Object:
public class Country {
private int id;
private String countryName;
public Country(int id, String countryName){
this.id = id;
this.countryName = countryName;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCountryName() {
return countryName;
}
public void setCountryName(String countryName) {
this.countryName = countryName;
}
}
Wrapper object:
private class CountryFound{
private final List<Country> countryList;
private Country foundCountry;
public CountryFound(List<Country> countryList, Country foundCountry){
this.countryList = countryList;
this.foundCountry = foundCountry;
}
public List<Country> getCountryList() {
return countryList;
}
public void setCountryList(List<Country> countryList) {
this.countryList = countryList;
}
public Country getFoundCountry() {
return foundCountry;
}
public void setFoundCountry(Country foundCountry) {
this.foundCountry = foundCountry;
}
}
Iterate operation:
int id = 5;
CountryFound countryFound = new CountryFound(countryList, null);
countryFound.getCountryList().forEach(c -> {
if(c.getId() == id){
countryFound.setFoundCountry(c);
}
});
System.out.println("Country found: " + countryFound.getFoundCountry().getCountryName());
You could remove the wrapper class method "setCountryList()" and make the field "countryList" final, but I did not get compilation errors leaving these details as-is.

To have a more general solution, you can write a generic Wrapper class:
public static class Wrapper<T> {
public T obj;
public Wrapper(T obj) { this.obj = obj; }
}
...
Wrapper<Integer> w = new Wrapper<>(0);
this.forEach(s -> {
s.setOrdinal(w.obj);
w.obj++;
});
(this is a variant of the solution given by Almir Campos).
In the specific case this is not a good solution, as Integer is worse than int for your purpose, anyway this solution is more general I think.

Related

Refactoring Java8 code with Functional Interface

Input:
public BigDecimal getMaxValuation(ServiceData data) {
System.out.println("getMaxValuation()");
BigDecimal calculatedAmount;
//4 String returnValue = getReturnValue(data);
Function<ServiceData,String> returnValueFn = this::getReturnValue;
BigDecimal orderSize = getOrderSize(returnValueFn.apply(data),60);
Predicate<String> gasPredicate = "GAS"::equalsIgnoreCase;
Predicate<String> oilPredicate = "OIL"::equalsIgnoreCase;
if(gasPredicate.test(returnValueFn.apply(data)))
calculatedAmount = callA(data.getValuation())
else if(oilPredicate.test(returnValueFn.apply(data)))
calculatedAmount = callB(data.getValuation())
else
calculatedAmount = callC(data.getValuation())
return calculatedAmount;
}
public String getReturnValue(ServiceData data){
System.out.println("getReturnValue()");
return returnValue;
}
In the above function getMaxValuation(), when we comment line#4 and replace it with a Function<ServiceData,String>,
getReturnValue() is getting called 3 times during the execution. But when we uncomment the line #4 and remove all the
Function<ServiceData,String> related change the getReturnValue() is getting called only once.
When we use Function is there any way to achieve the same behavior ?
Whether you invoke a method directly or using a functional interface, the logic of invoking it once and storing the result in a local variable to avoid repeated evaluation doesn’t change.
So far, your rewriting of the direct invocations into uses of a functional interface looks like an end in itself, without actually improving anything but only making the code more complicated.
One way of using functional programming to improve your code would be using a map of functions to replace the if-else ladder by a single lookup:
static final Map<String, Function<Valuation,BigDecimal>> METHOD;
static {
Map<String, Function<Valuation,BigDecimal>> m
= new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
m.put("GAS", ContainingClass::callA);
m.put("OIL", ContainingClass::callB);
METHOD = Collections.unmodifiableMap(m);
}
public BigDecimal getMaxValuation(ServiceData data) {
// don't know how to incorporate this, as it was entirely unused
// BigDecimal orderSize = getOrderSize(getReturnValue(data), 60);
return METHOD.getOrDefault(getReturnValue(data), ContainingClass::callC)
.apply(data.getValuation());
}
Where Valuation refers to the return type of ServiceData.getValuation() and ContainingClass is the declaring class of callA, callB, and callC, assuming static methods.
If these methods are non-static, the code would have to look like
static final Map<String, BiFunction<ContainingClass,Valuation,BigDecimal>> METHOD;
static {
Map<String, BiFunction<ContainingClass,Valuation,BigDecimal>> m
= new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
m.put("GAS", ContainingClass::callA);
m.put("OIL", ContainingClass::callB);
METHOD = Collections.unmodifiableMap(m);
}
public BigDecimal getMaxValuation(ServiceData data) {
return METHOD.getOrDefault(getReturnValue(data), ContainingClass::callC)
.apply(this, data.getValuation());
}

Averaging across multiple fields with IntSummaryStatistics

I'm trying to use Java 8 streams to create a single CarData object, which consists of an average of all the CarData fields in the list coming from getCars;
CarData = new CarData();
CarData.getBodyWeight returns Integer
CarData.getShellWeight returns Integer
List<CarData> carData = carResults.getCars();
IntSummaryStatistics averageBodyWeight = carData.stream()
.mapToInt((x) -> x.getBodyWeight())
.summaryStatistics();
averageBodyWeight.getAverage();
IntSummaryStatistics averageShellWeight = carData.stream()
.mapToInt((x) -> x.getShellWeight())
.summaryStatistics();
getShellWeight.getAverage();
I don't want to have to put each of these back together in my final returned result.
Visually, this is my list
getCars() : [
{CarData: { getBodyWeight=10, getShellWeight=3 } }
{CarData: { getBodyWeight=6, getShellWeight=5 } }
{CarData: { getBodyWeight=8, getShellWeight=19 } }
]
and the output I'm trying to achieve is a single object that has the average of each of the fields I specify. not sure If I need to use Collectors.averagingInt or some combo of IntSummaryStatistics to achieve this. Easy to do across one field for either of these techniques, just not sure what I'm missing when using multiple integer fields.
{CarData: { getBodyWeight=8, getShellWeight=9 } }
Starting with JDK 12, you can use the following solution:
CarData average = carData.stream().collect(Collectors.teeing(
Collectors.averagingInt(CarData::getBodyWeight),
Collectors.averagingInt(CarData::getShellWeight),
(avgBody, avgShell) -> new CarData(avgBody.intValue(), avgShell.intValue())));
For older Java versions, you can do either, add the teeing implementation of this answer to your code base and use it exactly as above or create a custom collector tailored to your task, as shown in Andreas’ answer.
Or consider that streaming twice over a List in memory is not necessarily worse than doing two operations in one stream, both, readability- and performance-wise.
Note that calling intValue() on Double objects has the same behavior as the (int) casts in Andreas’ answer. So in either case, you have to adjust the code if other rounding behavior is intended.
Or you consider using a different result object, capable of holding two floating point values for the averages.
You need to write your own Collector, something like this:
class CarDataAverage {
public static Collector<CarData, CarDataAverage, Optional<CarData>> get() {
return Collector.of(CarDataAverage::new, CarDataAverage::add,
CarDataAverage::combine,CarDataAverage::finish);
}
private long sumBodyWeight;
private long sumShellWeight;
private int count;
private void add(CarData carData) {
this.sumBodyWeight += carData.getBodyWeight();
this.sumShellWeight += carData.getShellWeight();
this.count++;
}
private CarDataAverage combine(CarDataAverage that) {
this.sumBodyWeight += that.sumBodyWeight;
this.sumShellWeight += that.sumShellWeight;
this.count += that.count;
return this;
}
private Optional<CarData> finish() {
if (this.count == 0)
return Optional.empty();
// adjust as needed if averages should be rounded
return Optional.of(new CarData((int) (this.sumBodyWeight / this.count),
(int) (this.sumShellWeight / this.count)));
}
}
You then use it like this:
List<CarData> list = ...
Optional<CarData> averageCarData = list.stream().collect(CarDataAverage.get());

How we I achieve add(4)(10)(20)(3)(1).total using Java Function?

I am learning usage of,
java.util.function.Function
I wrote a code which uses java Function to add 4 to itself,
the code is as follows,
public class Test01 {
public static void main(String[] args) {
Function<Integer,Integer> addFunction = new Function<Integer,Integer>() {
private int total = 0;
public Integer apply(Integer value) {
this.total += value;
return this.total;
}
};
int finalTotal = addFunction.andThen(addFunction)
.andThen(addFunction)
.andThen(addFunction)
.apply(4);
System.out.println(finalTotal);
}
}
When I run the above code the output which I get is
32
How can I achieve something which I did in javaScript which is as follows,
var fn19 = function(){
var addNum = function(num){
var fn = function(num2){
fn.sum += num2;
return fn;
};
fn.sum = num;
return fn;
};
print("addNum(3)(4)(3)(10) ==> "+addNum(3)(4)(3)(10).sum);
};
fn19();
The output of the above code is
addNum(3)(4)(3)(10) ==> 20
Can I have the same kind of java function call where I can pass as many number arguments and the addFunction will add those many numbers.
An example, as close as possible to your JavaScript code, would be
class QuestionableConstruct {
int sum;
QuestionableConstruct add(int num2) {
sum += num2;
return this;
}
}
Runnable fn19 = () -> {
IntFunction<QuestionableConstruct> addNum = num -> {
QuestionableConstruct fn = new QuestionableConstruct();
fn.sum = num;
return fn;
};
System.out.println("addNum(3)(4)(3)(10)==> "+addNum.apply(3).add(4).add(3).add(10).sum);
};
fn19.run();
A more Java like solution would be
interface Add {
int sum();
default Add add(int num) {
int sum = sum() + num;
return () -> sum;
}
static Add num(int num) {
return () -> num;
}
}
usable as
System.out.println("addNum(3)(4)(3)(10) ==> "+Add.num(3).add(4).add(3).add(10).sum());
Unlike the JavaScript construct, this uses real immutable functions. Consider
Add a = Add.num(1).add(2).add(3);
System.out.println("1+2+3+4+5 = "+a.add(4).add(5).sum());
System.out.println("1+2+3+10+20 = "+a.add(10).add(20).sum());
which works smoothly without interference.
But of course, if you just want to sum a variable number of items, use
System.out.println("addNum(3)(4)(3)(10) ==> "+IntStream.of(3, 4, 3, 10).sum());
or if you want a mutable accumulator, use
System.out.println("addNum(3)(4)(3)(10) ==> "+
IntStream.builder().add(3).add(4).add(3).add(10).build().sum());
which allows to keep the builder and pass it around.
You can't exactly do that in Java, what you are looking for is reducing a stream of values.
In other words: the "real" functional solution here isn't to call one method with multiple arguments. You rather have the values in some list, and then you define the function that "accumulates" over the content of that list, by applying a function on the elements.
See here for some examples.
The technique you're describing in the JavaScript world is taking advantage of a closure.
This is a nice side-effect of functions in JavaScript being first-class citizens. It's a way of associating functions with data in the enclosing scope and then being able to pass this association around without losing inner context. The most common/simple use of which is caching(its formal name being memoisation).
You would need functions(methods) in Java to be first class, but doing so would be redundant as classes by design are an entity which associates data and methods, making the whole concept of a closure in this context redundant.

Decrease and Increase Enum value [duplicate]

This question already has answers here:
What's the best way to implement `next` and `previous` on an enum type?
(7 answers)
Closed 6 years ago.
I would like to create a method called increaseValue, which it's signature is as following:
public Size increaseValue(Size s)
Also I have the following statement:
protected enum Size {XS, S, M, L, XL}
I need to know, how can I make the method return correct value (i.e. XL when input is L... etc.) while not using Switch-Case statement ?
Thanks !
You could assume they are in increasing ordinal() order. I would add this a method on Size.
protected enum Size {
XS, S, M, L, XL;
static final Size[] VALUES = values();
public Size incrementSize() { return VALUES[ordinal()+1]; }
public Size decrementSize() { return VALUES[ordinal()-1]; }
}
Note: I wouldn't assume that XS is after XL, but rather you get an error (though not a very clear one)
Note: every time you call values() it creates a new array. It has to do this because the array is mutable and you might change it. I highly recommend saving a copy and avoid calling values() each time.
You can make the error messages clearer by overriding those methods.
protected enum Size {
XS {
public Size decrementSize() { throw new UnsupportedOperationException("No smaller size"); }
},
S,
M,
L,
XL {
public Size incrementSize() { throw new UnsupportedOperationException("No larger size"); }
};
static final Size[] VALUES = values();
public Size incrementSize() { return VALUES[ordinal()+1]; }
public Size decrementSize() { return VALUES[ordinal()-1]; }
}
Here's why you should not do it: if you perform arithmetic on your enum, you can end up with invalid values, for instance what would happen if you added one to XL?
Here's how you do it:
Size.values()[s.ordinal()+1]
In good OO design you want to internalize such things. Meaning: you want to provide a method like "nextSize()" within that enum, like:
public enum Size {
XS, ...;
public Size nextSize() {
switch (this) ...
In this situation, the values are probably "fixed"; but in other situations, you might later want to insert new constants; thus I prefer an explicit mapping here; instead of relying on calls to ordinal().
And as mentioned in the other answers: you need to define what largestSize().nextSize() means. It could throw an exception, or return null (baaad idea). Alternatively, that method could return Optional<Size>; to make it clear to the caller that this method doesn't always return a valid result.
public Size increaseValue(Size s) {
Size[] allValues = Size.values();
var newOrdinal = s.ordinal() + 1;
return (newOrdinal >= allValues.length) ? null) : allValues[newOrdinal];
}
public Size decreaseValue(Size s) {
var newOrdinal = s.ordinal() - 1;
return (newOrdinal < 0) ? null : values()[newOrdinal];
}
You van modify this enum like this:
protected enum Size {
XS,(1)
S(2),
M(3),
L(4),
XL(5);
private int sizeNo;
Size(int sizeNo) {this.sizeNo = sizeNo;}
Size getBySizeNo(int sizeNo){
for(size : Size.values()) {
if (sizeNo == size.getSizeNo() ) {
return size;
}
}
throw new IllegalArgumentException() ;
}
public Size increaseValue(Size s){
return getBySizeNo(s.getSizeNo() +1) ;
}
}
Try this:
public Size increaseValue(Size s) {
return Size.values()[s.ordinal() + 1]
}

Return all instance one variables in one call

I have this class that serves as a container which I will use the instance variable for processing later
class Data{
static int counter= 0;
boolean boolean1;
String string1;
public Data() {
counter++;
}
}
And I have this method that sets the values of Data
public Data setData()
{
Data data = null;
for (int i = 0; i < somecoutnerhere; i++) {
Data = new Data();
Data.boolean1 = some boolean put here;
Data.string1 = "some string to be put here";
}
return ProcessData(Data);
}
I also have this class ProcessData that will make use of Data and will construct the response
private class ProcessData
{
private final Map<String, List<?>> map = new HashMap<String, List<?>>();
int counter;
public ProcessData(Data data)
{
map.put("boolean1", data.boolean1);
map.put("String1", data.string1);
counter = data.counter;
}
public String someMethodToGenerateReturnData(){
// some code here to make use of the Data collected. Will basically use map to construct the return String
}
}
My problem is that I couldn't figure out how can I return all the instance variables created on the for-loop for Data on setData(). Any thoughts?
My problem is that I couldn't figure out how can I return all the instance variables created on the for-loop for Data on setData(). Any thoughts?
According to this your problem is not "returning all instance one variables in one call", as your title states, but rather a question about how returning all Data-Objects created in your for-loop, which is easier.
Your code is erronous though, so I went ahead & corrected it (I hope I didn't mess up). I also renamed a few things.
The changes I made are:
renamed "boolean1" and "string1" to "trueOrFalse" and "string"
added a public, fully parameterized constructor to the Data-class
added a ProcessData-list to the setData()-method, which is filled in the for-loop
(+ a comment)
However, I'd strongly recommend you to check your architecture, and also to learn a bit about naming conventions, or coding conventions in general. Names should point out the purpose or content of the method/variable/class, and "boolean1" isn't really doing that.
Regarding the architecture: The Data-class seems to exist solely for the counter, and you could easily change that, making the Data-class obsolete (unless it's used somewhere else).
Data class:
class Data {
static int counter = 0;
boolean trueOrFalse;
String string;
public Data() {
counter++;
}
public Data(boolean someBoolean, String someString) {
this.trueOrFalse= someBoolean;
this.string = someString;
counter++;
}
}
setData()-Method:
public List<ProcessData> setData() {
List<ProcessData> processedDataList = new ArrayList<ProcessData>();
for (int i = 0; i < someCounterHere; i++) {
processedDataList.add(new ProcessData(new Data(true, "testString"));
// a new Data-object is created (parameters true and "testString")
// a new ProcessData-object is created (parameter is the newly created Data-Object)
// the newly created ProcessData-object is added to the list
}
return processedDataList;
}
ProcessData-class:
private class ProcessData {
private final Map<String, List<?>> map = new HashMap<String, List<?>>();
int counter;
public ProcessData(Data data) {
map.put("trueOrFalse", data.trueOrFalse);
map.put("string", data.string);
counter = data.counter;
}
public String someMethodToGenerateReturnData() {
// some code here to make use of the Data collected. Will basically use map to construct the return String
}
}

Categories

Resources