I have already look at this questions, but my problem is a little different.
I have a "baseString", n HashMap and an output string.
I want to fill the baseString with the hashmap, to construct a json call parameters.
I have already done it with Java 7, in this way:
HashMap<String,Integer> element= getAllElement();
String baseElem="{\"index\":{}}\r\n" +
"{\"name\":\"$name$\",\"age\":$age$}";
String result=baseElem;
for (Map.Entry<String, Integer> entry : element.entrySet()) {
result=result.replace("$name$", entry.getKey());
result=result.replace("$age$", entry.getValue().toString());
result=result+baseElem;
}
result= result.replace(baseElem, "");
Now I want to the same with Java 8,
I have tried in this way:
element.forEach((k,v)->{
result=result.replaceAll("$name$", k);
result=result.replaceAll("$age$", v.toString());
result=result+baseElem;
});
But for each result I have an error
"Local variable result defined in an enclosing scope must be final or
effectively final"
So the question is: I can do that in some kind of way with Java 8 and streams? Or there is no way, and so I can use the simple Java 7 for?
Your approach is going entirely into the wrong direction. This is not only contradicting the functional programming style, the Stream API adopts. Even the loop is horribly inefficient, performing repeated string concatenation and replace operations on the growing result string.
You did a Shlemiel the painter’s algorithm
You actually only want to perform a replace operation on the baseElem template for each map entry and join all results. This can be expressed directly:
Map<String,Integer> element = getAllElement();
String baseElem = "{\"index\":{}}\r\n{\"name\":\"$name$\",\"age\":$age$}";
String result = element.entrySet().stream()
.map(e -> baseElem.replace("$name$", e.getKey())
.replace("$age$", e.getValue().toString()))
.collect(Collectors.joining());
As for "Local variable result defined in an enclosing scope must be final or effectively final" see this answer for further explanation.
As for:
So the question is: I can do that in some kind of way with Java 8 and
streams? Or there is no way, and so I can use the simple Java 7 for?
The logic you're performing with the iterative approach is known as "fold" or "reduce" in the functional world i.e. streams.
So, what you want to do is:
String result = element.entrySet()
.stream()
.reduce(baseElem,
(e, a) -> e.replace("$name$", a.getKey()).replace("$age$",
a.getValue().toString()),
(x, y) -> {
throw new RuntimeException();
});
the third input argument to reduce is known as the combiner function which should be an associative, non-interfering, stateless function for combining two values, which must be compatible with the accumulator function.
if you don't plan on using a parallel stream then the current logic should suffice otherwise you'll need to replace (x, y) -> {...} with the actual logic.
It think, you could use this one:
private static Map<String, Integer> getAllElement() {
Map<String, Integer> map = new HashMap<>();
map.put("\\$name\\$", 666);
map.put("\\$age\\$", 777);
return map;
}
Map<String, Integer> map = getAllElement();
String[] json = { "{\"index\":{}}\r\n{\"name\":\"$name$\",\"age\":$age$}" };
map.forEach((key, val) -> json[0] = json[0].replaceAll(key, String.valueOf(val)));
System.out.println(json[0]);
Output:
{"index":{}}
{"name":"666","age":777}
From the docs,
Any local variable, formal parameter, or exception parameter used but
not declared in a lambda expression must either be declared final or
be effectively final (§4.12.4), or a compile-time error occurs where
the use is attempted.
Similar rules on variable use apply in the body of an inner class (§8.1.3). The restriction to effectively final variables prohibits access to dynamically-changing local variables, whose capture would likely introduce concurrency problems. Compared to the final restriction, it reduces the clerical burden on programmers.
What is to be a constant here is the reference, but not the values.
You are getting this exception because, you are changing the reference of result. You are re-assigning result to some point to some other String inside your lambda. Thus, conflicting with the JLS, dynamically-changing local variables
Also, Adding to this, You can use Jackson ObjectMapper for producing JSON from Java object(s) instead of hardcoding and replacing stuff.
Define the result variable outside your method something like below
Class A{
String result=null;
Method a(){
//method implementation
}
}
Related
I have the following case:
//Case 1
final Set<String> first = A.stream().filter().map().collect()
//Case 2
final Set<String> second = B.stream().filter().map().collect()
As you can see I call the same lambda expression twice but with different "consumer" (A or B). Is there a solution to avoid this duplication ?
Define a method, taking a Collection of the appropriate type (or a Stream, if you don't mind calling parameter.stream() when calling the method):
Set<String> reuse(Collection<SomeType> collection) {
return collection.stream().filter().map().collect();
}
Then invoke like:
final Set<String> first = reuse(A);
final Set<String> second = reuse(B);
First of all: there are no lambdas in your example code.
You are only showing two chained method calls creating a Set as final result.
In that sense, the only thing that makes sense given the example code: putting the common parts
return someCollection.stream().filter().map().collect()
into a small helper method. Maybe a bit more complex than outlined by Andy - as I expect that the lambdas you are talking about are actually used as paramters for the calls to filter() and map(). In that case you could make those parameters of the helper method.
But as much as I am about avoiding code duplication - you should only create such a helper method when the underlying "semantics" are really identical for the two use cases.
You can use Predicate as a type e.g.
Predicate<String> someFilter = e -> e == e; // or something more smart
and use it like
A.stream().filter(someFilter);
I'm try to convert a list to a map using the Collectors.toMap call. The list consists of ActivityReconcile objects. I want to pass an instance for every entry in the list into the toMap call.
The code is below and where I need the instances is denoted by ??.
final List<ActivityReconcile> activePostedList = loader.loadActivePosted(accessToken);
Map<AccountTransactionKey, ActivityReconcile> postedActiveMap =
activePostedList.stream().collect(
Collectors.toMap(
AccountTransactionKey.createNewAccountTransactionKeyFromActivityReconcileRecord(??),??));
If I understood you correctly, you will need something like
Map<AccountTransactionKey, ActivityReconcile> result = choices
.stream()
.collect(Collectors.toMap(
AccountTransactionKey::generate,
Function.identity()));
And the method (in AccountTransactionKey class) will look like
public static AccountTransactionKey generate(ActivityReconcile reconcile) {...}
I've replaced createNewAccountTransactionKeyFromActivityReconcileRec by generate for making the answer more readable and understandable.
To "fix" your code with the least changes, add a lambda parameter:
activePostedList.stream().collect(Collectors.toMap(
ar -> AccountTransactionKey.createNewAccountTransactionKeyFromActivityReconcileRecord(ar)),
o -> o));
or use a method reference:
activePostedList.stream().collect(Collectors.toMap(
AccountTransactionKey::createNewAccountTransactionKeyFromActivityReconcileRecord, o -> o));
btw, I can't recall ever seeing a method name as long as createNewAccountTransactionKeyFromActivityReconcileRecord - for readability, consider reducing it to just create(), since the return type and parameter type are enough to distinguish it from other factory methods you may have.
I'm trying to translate this (simplified) code to use Java-8 streams:
Map<String, String> files = new ConcurrentHashMap<String, String>();
while(((line = reader.readLine()) != null) {
if(content != null)
files.put("not null"+line, "not null"+line);
else
files.put("its null"+line, "its null"+line);
}
reader.close();
Here is what I've tried:
files = reader.lines().parallel().collect((content != null)?
(Collectors.toConcurrentMap(line->"notnull"+line, line->line+"notnull")) :
(Collectors.toConcurrentMap(line->line+"null", line->line+"null")));
But the above gives a "cyclic inference" message for all the line->line+"..." on intelliJ. What is cyclic inference? Is there an error in this logic?
I noted some similar issues on SO. But they suggest to use interface(Map) instead of its implementations. But files here is declared as a Map.
Update: Adding more context, content is a String that holds the name of a directory. files is a map that holds multiple file paths. What file paths need to go into the files map depends on content directory-name is populated or not.
Another way to fix this is to introduce the intermediate variable for collector:
Collector<String, ?, ConcurrentMap<String, String>> collector = (content != null) ?
(Collectors.toConcurrentMap(line->"notnull"+line, line->line+"notnull")) :
(Collectors.toConcurrentMap(line->line+"null", line->line+"null"));
Map<String, String> files = reader.lines().parallel().collect(collector);
This solution (unlike one presented by #JanXMarek) does not allocate intermediate arrays and does not check the content for every input line.
The cyclic inference is the situation in the type inference procedure when to determine the type of inner subexpression, the type of outer subexpression must be determined, but it cannot be determined without knowing the type of inner subexpression. Type inference in Java-8 can infer that in case of Stream<String>.collect(Collectors.toConcurrentMap(line->line+"null", line->line+"null")) the type of Collector is Collector<String, ?, ConcurrentMap<String, String>>. Normally when subexpression type (here we're speaking about toConcurrentMap(...) subexpression) cannot be explicitly determined, it can be reduced using the outer context if the outer context is method invocation, cast or assignment. Here however the outer context is ?: operator which has its own complex type inference rules, so this becomes too much and you should help the type inference system specifying the explicit type somewhere.
You can do it like this
reader.lines().parallel()
.map(line -> content == null ?
new String[]{"notnull"+line, line+"notnull"} :
new String[]{line+"null", line+"null"})
.collect(Collectors.toConcurrentMap(pair -> pair[0], pair -> pair[1]));
First, you map the line to a (key,value) pair stored in an array (or in some kind of a Pair object), and then, in the collector, you split it again into a key and a value.
Just a side-note. I doubt that .parallel() is of any good in this context. If you are using the standard Java API for reading files, the iterator underneath will still read the file sequentially. The only thing that will be executed in parallel will be transforming the lines. I just tried it on my PC, out of my curiosity, and it was about 10% faster without .parallel().
Parallelisation makes sense if the processing is an order of magnitude slower than reading the input of the stream, which is not the case here.
I have two Lists, for example:
List<Foo> list1 = Lists.newArrayList(new Foo(...),...);
List<Bar> list2 = Lists.newArrayList(new Bar(...),...);
In Bar there is a property, fooId. Suppose list1.size() == list2.size().
I want to set fooIds of the Bar instances in order.
I tried below code:
int index = 0;
list2.forEach(b -> b.fooId = list1.get(index++).getId());
but compilation failed
Local variable index defined in an enclosing scope must be final or effectively final
Does Java 8 have some convenient manner to process this?
You can't modify the index variable from inside the lambda expression.
You can iterate over the indices using an IntStream (though that's not much of an improvement compared to a simple for loop) :
IntStream.range(0,list1.size())
.forEach(i -> list2.get(i).setFooId(list1.get(i).getId()));
You could do:
IntStream.range(0, list1.size())
.forEach(i -> list2.get(i).fooId = list1.get(i).getId());
but do note that this is not good functional code. You would be better to write an explicit for loop and not use Stream.
The code in your question doesn't work because you can't reference a variable inside a lambda expression that isn't final (or effectively final).
If you don't mind about list1 after this operation and its element can be removed, you could also use this:
list2.forEach(bar -> bar.fooId = list1.remove(0).getId());
Unfortunately there is no zip operation in Java 8 streams API. But you can use protonpack library.
List<Bar> upd = StreamUtils.zip(list1.stream(), list2.stream(), (foo, bar) -> {
bar.setId(foo.getId());
return bar;
}).collect(Collectors.toList());
I also want to add that using mutable data objects is an imperative style. In functional style it is preferable to use immutable data and return new objects instead if you need to change anything.
Using my StreamEx library you can reduce the boilerplate:
EntryStream.zip(list1, list2).forKeyValue((foo, bar) -> bar.fooId = foo.getId());
EntryStream.zip creates a stream of Map.Entry<Foo, Bar> for each corresponding pair of list1 and list2. The forKeyValue method accepts BiConsumer (it's a syntactic sugar for forEach). Internally it's close to #Eran solution.
I have a basic question about using the "Best Practices" in coding. (I'm using Java, but the question is general to OOP.) When writing method's for a class that are intended to be used in the long run, is it best to leave the return object with or without generics?
To be specific in my case, I'm returning a Map<String, Integer> with the method. Should I specify this in the return statement, or should I simply return a Map?
It is best to use generics whenever possible. It will help avoid runtime exceptions, and it won't force the people using your code to do a bunch of ugly type casting. For example, if you use the following signature:
Map<String, Integer> getMap();
... then the consuming code might look like this:
Map<String, Integer> map = getMap();
Integer val = map.get(key);
... but if you use a signature like this:
Map getMap();
... the consuming code might look like this:
Map<String, Integer> map = (Map<String, Integer)getMap();
Integer val = map.get(key);
By using generics, not only do you save that (Map<String, Integer>) cast, but in the event that you change getMap to actually return a Map<String, Object>, you will get a compile-time error (which is easy to catch and fix), rather than possibly getting an exception when you call map.get(key) and the JRE tries to do an automatic cast of some random Object into an Integer.
You should definitely return a Map<String, Integer> instead of a plain Map if it makes sense in your method, as this will make it easier for others to use said method - after getting the Map<String, Integer> they will be able to retrieve String keys and Integer values without having to cast them from a generic Object every time (this also makes it a little more typesafe as this way they will know what the keys and values are without even reading the javadoc for your method).
So in short, definitely, return generics.
If you are returning a collection, you should include the generic type that is contained by the collection. For example:
public Map<String, Blammo> getBlammoMap(...)
is (IMHO) preferred to
public Map getBlammoMap(...)
Because it
Limits the options of a bad cast (i.e. kapowMap = (Map<String, Kapow> getBlammoMap()).
Tells the consumer what the method is actually returning.
If the method is clearly intended to work with a certain type (i.e. only String), then it's ok to return a List<String>. If the method is generic taking a type parameter T, you can return List<T>.
I would not simply return a Map, because usually it causes confusion and more boiler-plate code to convert to the desired type.
In general, your type parameters, both input and output, should capture the level of specificity of the precise function. In functional programming, they go so far as to say "the types are the documentation." If I were to see Map foo(Arg args) I would think that foo is in no way concerned with the types in the Map it returns, but somehow relies on Args for something." If I were to see Map<T,String> foo(T t, Arg arg) or Map<T, U> foo(T t, U u) I would think "OK, foo produces a Map based on the type of its t and with a String produced by Arg (or by the U from u)."
In terms of preference, clearly you want to be as clear as possible to the future programmer (yourself or others). Just as returning Map without type-params is obscure, so too would returning Map<String, Integer> might be overly restrictive (and thus misleading) if your foo would work equally well with, say, Map<String, Long>
I believe that more specific, the better. If your method is return a map that always has Strings as the key, and Integers as the value, then definitely use the Map has the return type. That way, any calling code knows exactly what they're getting. If the return type was just Map, then the calling code would have no idea what the class the keys and values are (Other than Object).
In general, you should probably always specify paramerize Maps, Lists, etc., so it's known exactly what it contains. This is very helpful when iterating over them and you can use a java foreach.
for (String currKey : myMap.keySet())
{
System.out.println("curr Key: " + currKey + " curr Value: " + myMap.get(currKey));
}
This eliminates any extra iterators or casting.
Ho-ho-ho! A pretty New Year question.
You generally must (MUST) return a proper generic Map<Whatever, YouNeed>. It may sound crazy, but as soon as you use any generic type without type parameters, you're getting into trouble.
The trouble will be as follows: raw types, being used in the code, change the way methods (even seemingly non-related ones) are resolved. Find a presentation by Joshua Bloch and Bill Pugh called "Java Puzzlers: Scraping the Bottom of the Barrel" for details whle I'm preparing an example :) The video with details is at http://www.parleys.com/#id=2168&st=5 (you may want to scroll to slide 44, 5. "Glommer Pile")
So here's an example:
/**
* (c) (as far as I know) Joshua Bloch and Bill Pugh, 2010
*/
public class Glommer<T> {
String glom(Collection<?> objs) {
String result = "";
for (Object o : objs) result += o;
return result;
}
int glom(List<Integer> ints) {
int result = 0;
for (int i : ints) result += i;
return result;
}
public static void main(String args[]) {
List<String> strings = Arrays.asList("1", "2", "3");
System.out.println(new Glommer().glom(strings));
}
}
Question is, whether it
prints 6
prints 123
throws an exception,
or does something else.
Try to guess. Then compile (yes it compiles) and see what happens.
Now that does not apply to your case. But having a habit of always specifying the type, even if it will be just Map<?,?>, is extremely helpful. You won't lose.
The obligatory Java Generics FAQ link