create empty stream and add elements to it - java

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();
}

Related

How to compare 'list of string' with 'enum string values' to return maximum match?

The current code send List<String> but returns Enum if all values matches
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
public enum Rara {
MA_ON("ST"),
MA_MAN("ST", "YS"),
MA_IP("ST", "YS", "IC"),
CODE("RC");
Rara(String... codes) {
this.codes = List.of(codes);
}
private List<String> codes;
public static Optional<Rara> getValue(List<String> values){
return Arrays.stream(values())
.filter(rara -> rara.codes.containsAll(values))
.findFirst();
}
}
public class Main {
public static void main(String args[]){
System.out.println(Rara.getValue(Arrays.asList("ST", "YS")));
// Optional[MA_MAN]
System.out.println(Rara.getValue(Arrays.asList("ST")));
// Optional[MA_ON]
}
}
But now requirements are if we get List as then max match enum should be returned
System.out.println(Rara.getValue(Arrays.asList("ST", "YS", "IC", "BLABLA")));
//Should return MA_IP
System.out.println(Rara.getValue(Arrays.asList("ST", "Bla", "Blabla", "BLABLA")));
//Should return MA_ON
Note: IN case of ST & RC or any conflicting We need to pick the first one. (Like ST, RC we can pick MA_ON)
can anyone suggest how to achieve in same enum in getValue? (In place of containsALL if i add contains then it doesn't work. Not sure if it requires some maxmatch etc)
If I understood your problem correctly all you need is to
know how many common strings has codes and values (for each enum)
filter out those with no common elements
pick one with maximum amount of common elements (or in case of many maxes first one from them)
To simplify our job we can create helper method to find only common elements in two lists. It can look like (I am assuming your Lists will have only unique elements)
private static List<String> commonElements(List<String> list1, List<String> list2) {
List<String> common = new ArrayList<>(list1); // to avoid modifying `list1`
common.retainAll(list2); // leaves in `common` only elements from `list2`
return common;
}
With this we can
map each enum to pair containing enum, amountOfCommonElements (we can use Map.entry to create pairs),
filter pairs where amountOfCommonElements is 0,
pick pair with max amountOfCommonElements
get from that pair only enum.
Demo:
enum Rara {
MA_ON("ST"),
MA_MAN("ST", "YS"),
MA_IP("ST", "YS", "IC"),
CODE("RC");
Rara(String... codes) {
this.codes = List.of(codes);
}
private List<String> codes;
private static List<String> commonElements(List<String> list1, List<String> list2) {
List<String> common = new ArrayList<>(list1); // to avoid modifying `list1`
common.retainAll(list2); // leaves in `common` only elements from `list2`
return common;
}
public static Optional<Rara> getValue(List<String> values){
return Arrays.stream(values())
.map(en -> Map.entry(en, commonElements(en.codes, values).size()))
.filter(entry -> entry.getValue()>0)
.max(Comparator.comparing(Map.Entry::getValue))
.map(Map.Entry::getKey);
}
}
class Main {
public static void main(String args[]){
System.out.println(Rara.getValue(Arrays.asList("ST", "YS")));
// Optional[MA_MAN]
System.out.println(Rara.getValue(Arrays.asList("ST")));
// Optional[MA_ON]
System.out.println(Rara.getValue(Arrays.asList("ST", "RC")));
// Optional[MA_ON]
System.out.println(Rara.getValue(Arrays.asList("STB", "RCC")));
//Optional.empty
}
}

How can I retrieve the n size elements of this list?

I have the below code which retrieves the value in the getNames.get(x), getNames() is of type List<String>. The requirement is to create an account, based on the size of the getNames(). This can vary from 1 to x, so rather than write the below, how can I use streams to accommodate this? Failing that, perhaps a loop will suffice. Final result should be based on the size of getNames(), createAccount() is called the same number of times.
public List<Account> buildAccounts(MetaData metaData){
return List.of(
createAccount(metaData.getNames().get(0)),
createAccount(metaData.getNames().get(1)),
createAccount(metaData.getNames().get(2)));
}
public Account createAccount(String name){
....
}
The length can be calculated using metaData.getNames().stream.collect(Collectors.counting());
Looks like a good candidate for map.
public public List<Account> buildAccounts(MetaData metaData){
return metaData.getNames().stream()
.map(n -> createAccount(n))
.collect(Collectors.toList());
}
As an aside
The length can be calculated using
metaData.getNames().stream.collect(Collectors.counting());
That would be weird when you can simply do metaData.getNames().size().
You can use Stream#map.
Returns a stream consisting of the results of applying the given function to the elements of this stream.
return metaData.getNames().stream()
.map(name -> createAccount(name)).collect(Collectors.toList());
In Java 16+, the collect call can be shortened to .toList().
For the size of the list you can use the size() method metaData.getNames().size();
For creating account
public List<Account> buildAccounts(MetaData metaData) {
return metaData.getNames()
.stream()
.map(this::createAccount)
.collect(Collectors.toList());
}
Another approach with for-each loop (enhanced for loop)
public List<Account> buildAccounts(MetaData metaData) {
List<String> names = metaData.getNames();
List<Account> accounts = new ArrayList<>(names.size());
for (String name : names) {
var account = createAccount(name);
accounts.add(account);
}
return accounts;
}

How to get an String and the ArrayList stored in a Arraylist of objects of a class using Java stream

I have modified the code and trying to get an ArrayList and the String stored in an Arraylist of Objects on a specific condition(say 'str' string equal to 2). I'm not able to convert the Stream to ArrayList. Please help me understand what needs to be done to get the ArrayList from this stream.
I have a class 'SampleClass' like below:
import java.util.ArrayList;
public class SampleClass {
String str;
ArrayList<String> al;
String check;
public SampleClass(String str, ArrayList<String> al, String check) {
super();
this.str = str;
this.al = al;
this.check = check;
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public ArrayList<String> getAl() {
return al;
}
public void setAl(ArrayList<String> al) {
this.al = al;
}
public String getCheck() {
return check;
}
public void setCheck(String check) {
this.check = check;
}
}
I have another class 'GetTheArrayListStoredInAnotherArrayList' like below where I'm trying to get the ArrayList stored inside the ArrayList of objects. Please correct me where I'm wrong.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.Collectors;
public class GetTheArrayListStoredInAnotherArrayList{
public static void main(String[] args) {
String test = "qw,rer,try,try,erh5,wertgw45t,45";
ArrayList<String> al = new ArrayList<String>();
al.addAll(new ArrayList<String>(Arrays.asList(test.split(","))));
System.out.println(al);
ArrayList<SampleClass> sca = new ArrayList<SampleClass>();
SampleClass sc1 = new SampleClass("1", al,"ch1");
SampleClass sc2 = new SampleClass("2", al,"cc2");
SampleClass sc3 = new SampleClass("3", al,"fr3");
SampleClass sc4 = new SampleClass("4", al,"fg4");
sca.add(sc1);
sca.add(sc2);
sca.add(sc3);
sca.add(sc4);
ArrayList<String> als1 = null;
ArrayList<String> als = sca.stream().filter( s -> s.getStr().equals("2")).flatMap(sc -> sc.getAl().stream()).collect(Collectors.toCollection(ArrayList::new));
System.out.println(als);
String ch = (String) sca.stream().filter(s -> s.getStr().equals("1")).map(ac -> ac.getCheck());
System.out.println(ch);
}
}
I got the below error when I executed the code :
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Cannot cast from Stream<String> to String
at GetTheArrayListStoredInAnotherArrayList.main(GetTheArrayListStoredInAnotherArrayList.java:24)
Not entirely sure what you are trying to do, but you need to change your code a bit:
List<String> als = sca.stream()
.filter(s -> s.getStr().equals("2"))
.flatMap(sc -> sc.getAl().stream())
.collect(Collectors.toList());
A few things :
flatMap must return a Stream (in your case you are returning a List)
Collectors.toList makes no guarantee of the List in returns, so the assignment is to a List, not an ArrayList.
EDIT
This:
Stream<String> stream = sca.stream().filter(s -> s.getStr().equals("1"))
.map(ac -> ac.getCheck());
Will produce a Stream<String>. You can't simply cast that to a String, you have to collect/reduce that to whatever you want. Like let's say a List:
List<String> list = sca.stream()
.filter(s -> s.getStr().equals("1"))
.map(ac -> ac.getCheck())
.collect(Collectors.toList());
Or a single String for example:
String r = sca.stream()
.filter(s -> s.getStr().equals("1"))
.map(ac -> ac.getCheck())
.collect(Collectors.joining(","));
This is actually basic stuff... you should really study some samples and the documentation.
Change
ArrayList<String> als = sca.stream().filter( s -> s.getStr().equals("2")).flatMap( sc -> sc.getAl());
To
ArrayList<String> als = sca.get(0).getAl();
First you have to use List instead of ArrayList. So with List you code will looks like
List<String> als1 = null;
List<String> als = sca.stream().
filter(s -> s.getStr().equals("2")). //Comparing
map(s -> s.getAl()) // Converting List<SampleClass> to list of all al list inside all SampleClass in format List<List<Straing>>
.flatMap(ArrayList::stream) //Creating a flat list from list of list of List :: List<List<Straing>> --To--> List<String>
.collect(Collectors.toList()); // Collecting as list
I have commented this code with details. But here if there are two SampleCalss objects in the list with str=2 then it will merge the al list of both objects. hope it will help you .
I'm trying to get the ArrayList stored inside the ArrayList of objects.
Well, the basic algorithm is as follows: Filter sca so it only leaves elements where str is "2" -> Get a single element from all the left over elements -> Get the al stored inside of that element.
You have done the first part correctly:
sca.stream().filter( s -> s.getStr().equals("2"))
But now you need to get a single element from the filtered result (filter can result in multiple elements being left over), so you call findFirst:
.findFirst().get()
This get call will throw an exception if there is no element left after the filter. If you don't want it to throw an exception, you can replace it with an orElse call:
.findFirst.orElse(new SampleClass("", null))
If you use orElse, the method chain will evaluate to null if no element with str being "2".
Now you just need to get the array list by calling getAl():
.getAl();
Now we combine all this together:
ArrayList<String> als = sca.stream()
.filter( s -> s.getStr().equals("2"))
.findFirst().orElse(new SampleClass("", null)).getAl();

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))

Possible Java1.8 stream anomaly

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.

Categories

Resources