Possible Java1.8 stream anomaly - java

Can someone explain the behavior of the following code? In particular why does the forEach in the stream change the original List?:
import java.util.ArrayList;
import java.util.List;
public class foreachIssue {
class justanInt {
public int anint;
public justanInt(int t){
anint=t;
}
}
public static void main(String[] args){
new foreachIssue();
}
public foreachIssue(){
System.out.println("The Stream Output:");
List<justanInt> lst = new ArrayList<>();
justanInt j1=new justanInt(2);
justanInt j2=new justanInt(5);
lst.add(j1);lst.add(j2);
lst.stream()
.map((s)->{
s.anint=s.anint*s.anint;
return s;
})
.forEach((s)->System.out.println("Anything"));
System.out.println(" lst after the stream:");
for(justanInt il:lst)
System.out.println(il.anint);
List<justanInt> lst1 = new ArrayList<>();
justanInt j3=new justanInt(2);
justanInt j4=new justanInt(5);
lst1.add(j3);lst1.add(j4);
lst1.stream()
.map((s)->{
s.anint=s.anint*s.anint;
return s;
});
System.out.println(" lst1 after the stream without forEach:");
for(justanInt il:lst1)
System.out.println(il.anint);
}
}
The output is:
The Stream Output:
Anything
Anything
lst after the stream:
4
25
lst1 after the stream without forEach:
2
5

map is an intermediate operation.
Stream operations are divided into intermediate (Stream-producing)
operations and terminal (value- or side-effect-producing) operations.
Intermediate operations are always lazy.
So the Function you've provided to map doesn't get applied until you consume the Stream. In the first case, you do that with forEach, which is a terminal operation. In the second, you don't.

Related

How to find the max number of an unique string element in a alphanumeric Array list in java

I have list that has alphanumeric elements. I want to find the maximum number of each elements individually.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Collect {
public static void main(String[] args) {
List<String> alphaNumericList = new ArrayList<String>();
alphaNumericList.add("Demo.23");
alphaNumericList.add("Demo.1000");
alphaNumericList.add("Demo.12");
alphaNumericList.add("Demo.12");
alphaNumericList.add("Test.01");
alphaNumericList.add("Test.02");
alphaNumericList.add("Test.100");
alphaNumericList.add("Test.99");
Collections.sort(alphaNumericList);
System.out.println("Output "+Arrays.asList(alphaNumericList));
}
I need filter only below values. For that I am sorting the list but it filters based on the string rather than int value. I want to achieve in an efficient way. Please suggest on this.
Demo.1000
Test.100
Output [[Demo.1000, Demo.12, Demo.12, Demo.23, Test.01, Test.02, Test.100, Test.99]]
You can either create a special AlphaNumericList type, wrapping the array list or whatever collection(s) you want to use internally, giving it a nice public interface to work with, or for the simplest case if you want to stick to the ArrayList<String>, just use a Comparator for sort(..):
package de.scrum_master.stackoverflow.q60482676;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static java.lang.Integer.parseInt;
public class Collect {
public static void main(String[] args) {
List<String> alphaNumericList = Arrays.asList(
"Demo.23", "Demo.1000", "Demo.12", "Demo.12",
"Test.01", "Test.02", "Test.100", "Test.99"
);
Collections.sort(
alphaNumericList,
(o1, o2) ->
((Integer) parseInt(o1.split("[.]")[1])).compareTo(parseInt(o2.split("[.]")[1]))
);
System.out.println("Output " + alphaNumericList);
}
}
This will yield the following console log:
Output [Test.01, Test.02, Demo.12, Demo.12, Demo.23, Test.99, Test.100, Demo.1000]
Please let me know if you don't understand lambda syntax. You can also use an anonymous class instead like in pre-8 versions of Java.
Update 1: If you want to refactor the one-line lambda for better readability, maybe you prefer this:
Collections.sort(
alphaNumericList,
(text1, text2) -> {
Integer number1 = parseInt(text1.split("[.]")[1]);
int number2 = parseInt(text2.split("[.]")[1]);
return number1.compareTo(number2);
}
);
Update 2: If more than one dot "." character can occur in your strings, you need to get the numeric substring in a different way via regex match, still not complicated:
Collections.sort(
alphaNumericList,
(text1, text2) -> {
Integer number1 = parseInt(text1.replaceFirst(".*[.]", ""));
int number2 = parseInt(text2.replaceFirst(".*[.]", ""));
return number1.compareTo(number2);
}
);
Update 3: I just noticed that for some weird reason you put the sorted list into another list via Arrays.asList(alphaNumericList) when printing. I have replaced that by just alphaNumericList in the code above and also updated the console log. Before the output was like [[foo, bar, zot]], i.e. a nested list with one element.
Check below answer:
public static void main(String[] args) {
List<String> alphaNumericList = new ArrayList<String>();
alphaNumericList.add("Demo.23");
alphaNumericList.add("Demo.1000");
alphaNumericList.add("Demo.12");
alphaNumericList.add("Demo.12");
alphaNumericList.add("Test.01");
alphaNumericList.add("Test.02");
alphaNumericList.add("Test.100");
alphaNumericList.add("Test.99");
Map<String, List<Integer>> map = new HashMap<>();
for (String val : alphaNumericList) {
String key = val.split("\\.")[0];
Integer value = Integer.valueOf(val.split("\\.")[1]);
if (map.containsKey(key)) {
map.get(key).add(value);
} else {
List<Integer> intList = new ArrayList<>();
intList.add(value);
map.put(key, intList);
}
}
for (Map.Entry<String, List<Integer>> entry : map.entrySet()) {
List<Integer> valueList = entry.getValue();
Collections.sort(valueList, Collections.reverseOrder());
System.out.print(entry.getKey() + "." + valueList.get(0) + " ");
}
}
Using stream and toMap() collector.
Map<String, Long> result = alphaNumericList.stream().collect(
toMap(k -> k.split("\\.")[0], v -> Long.parseLong(v.split("\\.")[1]), maxBy(Long::compare)));
The result map will contain word part as a key and maximum number as a value of the map(in your example the map will contain {Demo=1000, Test=100})
a. Assuming there are string of type Demo. and Test. in your arraylist.
b. It should be trivial to filter out elements with String Demo. and then extract the max integer for same.
c. Same should be applicable for extracting out max number associated with Test.
Please check the following snippet of code to achieve the same.
Set<String> uniqueString = alphaNumericList.stream().map(c->c.replaceAll("\\.[0-9]*","")).collect(Collectors.toSet());
Map<String,Integer> map = new HashMap<>();
for(String s:uniqueString){
int max= alphaNumericList.stream().filter(c -> c.startsWith(s+".")).map(c -> c.replaceAll(s+"\\.","")).map(c-> Integer.parseInt(c)).max(Integer::compare).get();
map.put(s,max);
}

How to iterate through a 2D LinkedHashSet and print its contents?

I am using a 2D LinkedHashSet for my program. I was wondering how I can iterate through the two dimensional HashSet and print its contents without doing this:
System.out.println(name of initialized HashSet)
Here is my code for initialization of the 2D LinkedHashSet:
LinkedHashSet<LinkedHashSet<String>> block = new LinkedHashSet<LinkedHashSet<String>>();
You can use 2 loops for this, similar to how you would for an array:
for (Set<String> innerSet : block) {
for (String string : innerSet) {
System.out.println(string);
}
}
You can also use streams to print each element:
block.stream()
.flatMap(Collection::stream)
.forEach(System.out::println);
If one wants to use a functional solution, one could use the following:
Ideone demo
import java.util.LinkedHashSet;
public class Streamify {
public static void main (final String... args) {
final LinkedHashSet<LinkedHashSet<String>> block = new LinkedHashSet<>();
final LinkedHashSet<String> lineOne = new LinkedHashSet<>();
lineOne.add("Hello");
lineOne.add("World");
block.add(lineOne);
final LinkedHashSet<String> lineTwo = new LinkedHashSet<>();
lineTwo.add("Hi");
lineTwo.add("Universe");
block.add(lineTwo);
block.forEach(line -> {
line.forEach(System.out::print);
System.out.println();
});
}
}

create empty stream and add elements to it

I want to have a stream where I can put elements after creation:
Stream<Task> all2 = Stream.of();
Because the return type of the method is also a Stream and I want to avoid the extra cost by using a list and the need for return list.stream():
Stream<Task> getAll(){
Stream<Task> all2 = Stream.of();
all2.add(item1);
..
return all2
}
But how can I add elements to all2 for further processing?
Is this the only way to do this:
Stream<Task> all2 = Stream.of();
all2 = Stream.concat(all2, Stream.of(new Task("hello")));
...
all2 = Stream.concat(all2, Stream.of(new Task("hello_1000000")));
Is this really cheaper than using a list and add elements to it?
List<Task> all = new ArrayList<>();
all.add(new Task("hello");
...
all.add(new Task("hello_1000000");
return all.stream();
Reading the comments you probably need a Stream of infinitely generated elements. Stream::generate does that:
import java.util.Scanner;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
getStream()
.forEach(System.out::println);
}
static String getUserInput(Scanner scanner) {
return scanner.nextLine();
}
static Stream<String> getStream() {
Scanner sc = new Scanner(System.in);
return Stream.generate(() -> getUserInput(sc)).takeWhile(Predicate.not(String::isEmpty));
}
}
With this approach you have a Stream<String>, which will wait for the user to input elements (inifitely, or up to the first error or empty() String) and apply the stream operations to every element (in this case, forEach - as shown in main()).
It looks like the perfect case for a Stream.Builder
Stream<Task> getAll(){
Stream.Builder<Task> all2 = Stream.builder();
all2.add(item1);
..
return all2.build();
}

How peek and map works in streams

I am trying to understand how different peek and map in java 8 streams.I have tried the following
public static void main(String[] args) {
List<String> arr = new ArrayList<String>();
arr.add("A");
arr.add("B");
List<String> a = arr.stream().peek(t->t.toLowerCase()).collect(Collectors.toList());
System.out.println(a);
}
The above code is not changing the alphabets to lower case.But when i try the following
public static void main(String[] args) {
List<String> arr = new ArrayList<String>();
arr.add("A");
arr.add("B");
List<String> a = arr.stream().map(t->t.toLowerCase()).collect(Collectors.toList());
System.out.println(a);
}
The alphabets are converted to smaller case.My doubt here is if i use both map and peek like below
public static void main(String[] args) {
List<String> arr = new ArrayList<String>();
arr.add("A");
arr.add("B");
List<String> a = arr.stream().map(t->t.toLowerCase()).peek(t->toUpper()).collect(Collectors.toList());
System.out.println(a);
}
public static Function<String, String> toUpper(){
return t->{
return t.toUpperCase();
};
}
The map method converts A,B to lower and Peek does nothing.So if there is any calculation involved while streaming cant i make use of peek?Can someone explain
MOdified code
static List<Employee> e = new ArrayList<>();
public static void main(String[] args) {
List<String> arr = new ArrayList<String>();
arr.add("Pavan");
arr.add("Kumar");
System.out.println("Size of emp"+e.size());
List<String> a = arr.stream().map(t->t.toLowerCase()).peek(t->populateEmp()).collect(Collectors.toList());
System.out.println("Size of emp"+e.size());
System.out.println(a);
}
public static Function<String, Employee> populateEmp(){
Employee ee = new Employee();
return t->{
System.out.println(t);
ee.setName(t);
e.add(ee);
return ee;
};
}
This is still not adding the Emp to list
Peek expects a Consumer, so if you are using toLowerCase() you are creating a new String, which is put into void. You may modify this object inside of a consumer, but String is immutable, so peek has no effect.
When you use map, then you expect to pass a Function or UnaryOperator, that receives single object and returns single object. So new String that is lower-cased is returned.
In both cases, objects are not cloned. So you could modify an object that is mutable inside of a peek function, but that is just the wrong way to do it:) Try passing a Date, then you can set hours inside a peek function because it's mutable.
In short:
use map to transform model to another model
use peek, to do something that consumes this object, but does not modify it (send a notification, print model, etc)
UPDATE:
public static Function<String, Employee> populateEmp(){
Employee ee = new Employee();
System.out.print("I am executed");
return t->{
System.out.print("I am not");
return null;
};
}
Try with this code. In your update, you are passing a consumer, that ignores passed argument, and you execute populateEmp() method, which returns a function, that adds to a map transformed object. But you NEVER execute this function, tus-> list is empty:)
In non-lambda word it looks like this:
for(String value: arr){
populateEmp(); // execute method but you do nothing with this Function.
}
So replace your peek with this:
.peek(t->populateEmp().apply(t))

Python-like list comprehension in Java

Since Java doesn't allow passing methods as parameters, what trick do you use to implement Python like list comprehension in Java ?
I have a list (ArrayList) of Strings. I need to transform each element by using a function so that I get another list. I have several functions which take a String as input and return another String as output. How do I make a generic method which can be given the list and the function as parameters so that I can get a list back with each element processed. It is not possible in the literal sense, but what trick should I use ?
The other option is to write a new function for each smaller String-processing function which simply loops over the entire list, which is kinda not so cool.
In Java 8 you can use method references:
List<String> list = ...;
list.replaceAll(String::toUpperCase);
Or, if you want to create a new list instance:
List<String> upper = list.stream().map(String::toUpperCase).collect(Collectors.toList());
Basically, you create a Function interface:
public interface Func<In, Out> {
public Out apply(In in);
}
and then pass in an anonymous subclass to your method.
Your method could either apply the function to each element in-place:
public static <T> void applyToListInPlace(List<T> list, Func<T, T> f) {
ListIterator<T> itr = list.listIterator();
while (itr.hasNext()) {
T output = f.apply(itr.next());
itr.set(output);
}
}
// ...
List<String> myList = ...;
applyToListInPlace(myList, new Func<String, String>() {
public String apply(String in) {
return in.toLowerCase();
}
});
or create a new List (basically creating a mapping from the input list to the output list):
public static <In, Out> List<Out> map(List<In> in, Func<In, Out> f) {
List<Out> out = new ArrayList<Out>(in.size());
for (In inObj : in) {
out.add(f.apply(inObj));
}
return out;
}
// ...
List<String> myList = ...;
List<String> lowerCased = map(myList, new Func<String, String>() {
public String apply(String in) {
return in.toLowerCase();
}
});
Which one is preferable depends on your use case. If your list is extremely large, the in-place solution may be the only viable one; if you wish to apply many different functions to the same original list to make many derivative lists, you will want the map version.
The Google Collections library has lots of classes for working with collections and iterators at a much higher level than plain Java supports, and in a functional manner (filter, map, fold, etc.). It defines Function and Predicate interfaces and methods that use them to process collections so that you don't have to. It also has convenience functions that make dealing with Java generics less arduous.
I also use Hamcrest** for filtering collections.
The two libraries are easy to combine with adapter classes.
** Declaration of interest: I co-wrote Hamcrest
Apache Commons CollectionsUtil.transform(Collection, Transformer) is another option.
I'm building this project to write list comprehension in Java, now is a proof of concept in https://github.com/farolfo/list-comprehension-in-java
Examples
// { x | x E {1,2,3,4} ^ x is even }
// gives {2,4}
Predicate<Integer> even = x -> x % 2 == 0;
List<Integer> evens = new ListComprehension<Integer>()
.suchThat(x -> {
x.belongsTo(Arrays.asList(1, 2, 3, 4));
x.is(even);
});
// evens = {2,4};
And if we want to transform the output expression in some way like
// { x * 2 | x E {1,2,3,4} ^ x is even }
// gives {4,8}
List<Integer> duplicated = new ListComprehension<Integer>()
.giveMeAll((Integer x) -> x * 2)
.suchThat(x -> {
x.belongsTo(Arrays.asList(1, 2, 3, 4));
x.is(even);
});
// duplicated = {4,8}
You can use lambdas for the function, like so:
class Comprehension<T> {
/**
*in: List int
*func: Function to do to each entry
*/
public List<T> comp(List<T> in, Function<T, T> func) {
List<T> out = new ArrayList<T>();
for(T o: in) {
out.add(func.apply(o));
}
return out;
}
}
the usage:
List<String> stuff = new ArrayList<String>();
stuff.add("a");
stuff.add("b");
stuff.add("c");
stuff.add("d");
stuff.add("cheese");
List<String> newStuff = new Comprehension<String>().comp(stuff, (a) -> { //The <String> tells the comprehension to return an ArrayList<String>
a.equals("a")? "1":
(a.equals("b")? "2":
(a.equals("c")? "3":
(a.equals("d")? "4": a
)))
});
will return:
["1", "2", "3", "4", "cheese"]
import java.util.Arrays;
class Soft{
public static void main(String[] args){
int[] nums=range(9, 12);
System.out.println(Arrays.toString(nums));
}
static int[] range(int low, int high){
int[] a=new int[high-low];
for(int i=0,j=low;i<high-low;i++,j++){
a[i]=j;
}
return a;
}
}
Hope, that I help you :)

Categories

Resources