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.
Related
i am trying to test a Rest API PUT request. It is a spring boot application.
PUT request is used to do an update in the existing list of objects
traditional way of writing is working.
data is the data in the memory - which is a List<Bean> and
name (string type) is the key to find the object in the data and objectBean is the one to replace once we find with the key(that is name)
public void update(Bean objectBean, String name) {
for(int i = 0; i < data.size() ; i++) {
Bean l = data.get(i);
if(l.getName().equals(name)) {
data.set(i, objectBean);
return;
}
}
};
but i tried to write using Stream in java 8 . below is the code
Data.stream().map(p -> p.getName().equals(name) ? objectBean: p );
but this gives empty list.
Using streams here makes code only more complicated.
If you really wants you can introduce it to find the index i value. After that you can do the replacement.
IntStream.range(0, data.size())
.filter(i -> data.get(i).getName().equals(name)).findFirst()
.ifPresent(i -> data.set(i, objectBean));
Given that data is some List with Bean objects, you'd need to return your collected stream:
return data.stream()
.map(bean -> bean.getName().equals(name) ? objectBean : bean)
.collect(Collectors.toList());
If data is a non-empty Iterable then the output must be as well as map takes a Function object. However, this is not a good use case for the Stream API:
Firstly, streams are designed for side-effect-free purposes (i.e., creating new data structures rather updating them). The stream API supports forEach(Consumer<super T>) which is designed for side effects, but so do many other collections, in fact, all Iterables, whereas the immutable operations such as map and flatMap are not.
Second, I can't see the rest of your program, but at least in this snippet you seem to be updating your data structure based on the name, and you assume the name is unique because you stopped as soon as you reached the first Bean with the name you're looking for. Consider using Map<String, Bean> as your data structure.
Lastly, streams are lazy data structures, meaning that all the chained operations get computed when you collect. This provides incentive to chain a lot of computations together - chaining just a single map doesn't give you any performance advantages (tho it does give you referential transparency).
return data.stream()
.filter(bean -> bean.getName().equals(name))
.findAny()
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
}
}
I'm trying to learn the Java Set interface and have encountered the following code online, I understand the purpose of this code is to convert a Collection<Object> to a TreeSet, but I do not understand how the statement works because the syntax is complex and foreign to me. Could someone walk me through the process step by step?
Set<String> set = people.stream()
.map(Person::getName)
.collect(Collectors.toCollection(TreeSet::new));
And also, under what kind of circumstances should we prefer the above syntax over the one below?
Set<Integer> s1 = new TreeSet(c1); //where c1 is an instance of Collection interface type
people.stream()
Takes a set of people and obtains a stream.
.map(Person::getName)
Takes a stream of people and invokes the getName method on each one, returning a list with all the results. This would be "equivalent" to
for(Person person : people){
setOfNames.add(person.getName())
}
.collect(Collectors.toCollection(TreeSet::new));
Takes a stream of strings and converts it in a set.
The streams are very useful when you need to apply several transformations. They can also perform very well if you make use of parallel streams, since each transformation (in your case each getName) can be done in parallel instead of sequentially.
peopele.stream() create a stream of elements
.map(Person::getName) takes each object from people collection and calls getName , coverts to string then
.collect(Collectors.toCollection(TreeSet::new)) - Collects these String elements and create a TreeSet out of it.
Hope its clear
I have similar problem to one discussed here, but with stronger practical usage.
For example, I have a Map<String, Integer>, and I have some function, which is given a key and in case the mapped integer value is negative, puts NULL to the map:
Map<String, Integer> map = new HashMap<String, Integer>();
public void nullifyIfNegative(String key) {
Integer value = map.get(key);
if (value != null && value.intValue() < 0) {
map.put(key, null);
}
}
I this case, the lookup (and hence, hashCode calculation for the key) is done twice: one for lookup and one for replacement. It would be nice to have another method (which is already in HashMap) and allows to make this more effective:
public void nullifyIfNegative(String key) {
Map.Entry<String, Integer> entry = map.getEntry(key);
if (entry != null && entry.getValue().intValue() < 0) {
entry.setValue(null);
}
}
The same concerns cases, when you want to manipulate immutable objects, which can be map values:
Map<String, String>: I want to append something to the string value.
Map<String, int[]>: I want to insert a number into the array.
So the case is quite common. Solutions, which might work, but not for me:
Reflection. Is good, but I cannot sacrifice performance just for this nice feature.
Use org.apache.commons.collections.map.AbstractHashedMap (it has at least protected getEntry() method), but unfortunately, commons-collections do not support generics.
Use generic commons-collections, but this library (AFAIK) is out-of-date (not in sync with latest library version from Apache), and (what is critical) is not available in central maven repository.
Use value wrappers, which means "making values mutable" (e.g. use mutable integers [e.g. org.apache.commons.lang.mutable.MutableInt], or collections instead of arrays). This solutions leads to memory loss, which I would like to avoid.
Try to extend java.util.HashMap with custom class implementation (which should be in java.util package) and put it to endorsed folder (as java.lang.ClassLoader will refuse to load it in Class<?> defineClass(String name, byte[] b, int off, int len), see sources), but I don't want to patch JDK and it seems like the list of packages that can be endorsed, does not include java.util.
The similar question is already raised on sun.com bugtracker, but I would like to know, what is the opinion of the community and what can be the way out taking in mind the maximum memory & performance effectiveness.
If you agree, this is nice and beneficiary functionality, please, vote this bug!
As a logical matter, you're right in that the single getEntry would save you a hash lookup. As a practical matter, unless you have a specific use case where you have reason to be concerned about the performance hit( which seems pretty unlikely, hash lookup is common, O(1), and well optimized) what you're worrying about is probably negligible.
Why don't you write a test? Create a hashtable with a few 10's of millions of objects, or whatever's an order of magnitude greater than what your application is likely to create, and average the time of a get() over a million or so iterations (hint: it's going to be a very small number).
A bigger issue with what you're doing is synchronization. You should be aware that if you're doing conditional alterations on a map you could run into issues, even if you're using a Synchronized map, as you'd have to lock access to the key covering the span of both the get() and set() operations.
Not pretty, but you could use lightweight object to hold a reference to the actual value to avoid second lookups.
HashMap<String, String[]> map = ...;
// append value to the current value of key
String key = "key";
String value = "value";
// I use an array to hold a reference - even uglier than the whole idea itself ;)
String[] ref = new String[1]; // lightweigt object
String[] prev = map.put(key, ref);
ref[0] = (prev != null) ? prev[0] + value : value;
I wouldn't worry about hash lookup performance too much though (Steve B's answer is pretty good in pointing out why). Especially with String keys, I wouldn't worry too much about hashCode() as its result is cached. You could worry about equals() though as it might be called more than once per lookup. But for short strings (which are often used as keys) this is negligible too.
There are no performance gain from this proposal, because performance of Map in average case is O(1). But enabling access to the raw Entry in such case will raise another problem. It will be possible to change key in entry (even if it's only possible via reflection) and therefore break order of the internal array.
So I have two questions about HashMaps in Java:
What is the correct way to initialize a HashMap? I think it might be best in my situation to use:
HashMap x = new HashMap();
But Eclipse keeps suggesting that I use:
HashMap<something, something> map = new HashMap();
Which is better?
Can a HashMap hold different types of objects/data types as values? For example, would this work and be OK:
map.put("one", 1);
map.put("two", {1, 2});
map.put("three", "hello");
In the first put(), I want an int as a value, in the second an int[], and third a string. Is this okay to do in Java with HashMaps? Also, is it okay to store a HashMap as a value within a HashMap?
It really depends on what kind of type safety you need. The non-generic way of doing it is best done as:
Map x = new HashMap();
Note that x is typed as a Map. this makes it much easier to change implementations (to a TreeMap or a LinkedHashMap) in the future.
You can use generics to ensure a certain level of type safety:
Map<String, Object> x = new HashMap<String, Object>();
In Java 7 and later you can do
Map<String, Object> x = new HashMap<>();
The above, while more verbose, avoids compiler warnings. In this case the content of the HashMap can be any Object, so that can be Integer, int[], etc. which is what you are doing.
If you are still using Java 6, Guava Libraries (although it is easy enough to do yourself) has a method called newHashMap() which avoids the need to duplicate the generic typing information when you do a new. It infers the type from the variable declaration (this is a Java feature not available on constructors prior to Java 7).
By the way, when you add an int or other primitive, Java is autoboxing it. That means that the code is equivalent to:
x.put("one", Integer.valueOf(1));
You can certainly put a HashMap as a value in another HashMap, but I think there are issues if you do it recursively (that is put the HashMap as a value in itself).
This is a change made with Java 1.5. What you list first is the old way, the second is the new way.
By using HashMap you can do things like:
HashMap<String, Doohickey> ourMap = new HashMap<String, Doohickey>();
....
Doohickey result = ourMap.get("bob");
If you didn't have the types on the map, you'd have to do this:
Doohickey result = (Doohickey) ourMap.get("bob");
It's really very useful. It helps you catch bugs and avoid writing all sorts of extra casts. It was one of my favorite features of 1.5 (and newer).
You can still put multiple things in the map, just specify it as Map, then you can put any object in (a String, another Map, and Integer, and three MyObjects if you are so inclined).
Eclipse is recommending that you declare the type of the HashMap because that enforces some type safety. Of course, it sounds like you're trying to avoid type safety from your second part.
If you want to do the latter, try declaring map as HashMap<String,Object>.
The way you're writing it is equivalent to
HashMap<Object, Object> map = new HashMap<Object, Object>();
What goes inside the brackets is you communicating to the compiler what you're going to put in the HashMap so that it can do error checking for you. If Object, Object is what you actually want (probably not) you should explicitly declare it. In general you should be as explicit as you can with the declaration to facilitate error checking by the compiler. What you've described should probably be declared like this:
HashMap<String, Object> map = new HashMap<String, Object>();
That way you at least declare that your keys are going to be strings, but your values can be anything. Just remember to use a cast when you get a value back out.
The 2nd one is using generics which came in with Java 1.5. It will reduce the number of casts in your code & can help you catch errors at compiletime instead of runtime. That said, it depends on what you are coding. A quick & dirty map to hold a few objects of various types doesn't need generics. But if the map is holding objects all descending from a type other than Object, it can be worth it.
The prior poster is incorrect about the array in a map. An array is actually an object, so it is a valid value.
Map<String,Object> map = new HashMap<String,Object>();
map.put("one",1); // autoboxed to an object
map.put("two", new int[]{1,2} ); // array of ints is an object
map.put("three","hello"); // string is an object
Also, since HashMap is an object, it can also be a value in a HashMap.
A HashMap can hold any object as a value, even if it is another HashMap. Eclipse is suggesting that you declare the types because that is the recommended practice for Collections. under Java 5. You are free to ignore Eclipse's suggestions.
Under Java 5, an int (or any primitive type) will be autoboxed into an Integer (or other corresponding type) when you add it to a collection. Be careful with this though, as there are some catches to using autoboxing.
Eclipse is suggesting you to define generic type so that you can have type safety. You can write
Map m = new HashMap();
which does not ensure type safety but following will ensure type safety
Map<Object,Object> = new HashMap<Object,Object>();
The Object can be any type such as String, Integer etc.
Map.of literals
As of Java 9, there is yet another way to instantiate a Map. You can create an unmodifiable map from zero, one, or several pairs of objects in a single-line of code. This is quite convenient in many situations.
For an empty Map that cannot be modified, call Map.of(). Why would you want an empty set that cannot be changed? One common case is to avoid returning a NULL where you have no valid content.
For a single key-value pair, call Map.of( myKey , myValue ). For example, Map.of( "favorite_color" , "purple" ).
For multiple key-value pairs, use a series of key-value pairs. ``Map.of( "favorite_foreground_color" , "purple" , "favorite_background_color" , "cream" )`.
If those pairs are difficult to read, you may want to use Map.of and pass Map.Entry objects.
Note that we get back an object of the Map interface. We do not know the underlying concrete class used to make our object. Indeed, the Java team is free to used different concrete classes for different data, or to vary the class in future releases of Java.
The rules discussed in other Answers still apply here, with regard to type-safety. You declare your intended types, and your passed objects must comply. If you want values of various types, use Object.
Map< String , Color > preferences = Map.of( "favorite_color" , Color.BLUE ) ;
In answer to your second question: Yes a HashMap can hold different types of objects. Whether that's a good idea or not depends on the problem you're trying to solve.
That said, your example won't work. The int value is not an Object. You have to use the Integer wrapper class to store an int value in a HashMap