Infinite sequence of Natural numbers with Java8 generator - java

I defined natural for Infinite sequence (Stream) of Natural numbers with Java8 iterator.
IntStream natural = IntStream.iterate(0, i -> i + 1);
natural
.limit(10)
.forEach(System.out::println);
Now, I want to define it with Java8 generator.
static Stream generate(Supplier s)
What would be the simplest way? Thanks.

With a generator you need to keep track of your current index. One way would be:
IntStream natural = IntStream.generate(new AtomicInteger()::getAndIncrement);
Note: I use AtomicInteger as a mutable integer rather than for its thread safety: if you parallelise the stream the order will not be as expected.

This is built into IntStream:
IntStream.range(0, Integer.MAX_VALUE)
This returns all values up to (but not including) Integer.MAX_VALUE.

Note: #assylias managed to do it with a lambda using AtomicInteger. He should probably have the accepted answer.
I'm not sure you can do that with a lambda (because it is stateful), but with a plain Supplier this would work:
IntSupplier generator = new IntSupplier() {
int current = 0;
public int getAsInt() {
return current++;
}
};
IntStream natural = IntStream.generate(generator);
However, I highly prefer your current solution, because this is the purpose of iterate(int seed, IntUnaryOperator f) IMHO:
IntStream natural = IntStream.iterate(0, i -> i + 1);

Related

Java 8 Streams : Count the occurrence of elements(List<String> list1) from list of text data(List<String> list2)

Input :
List<String> elements= new ArrayList<>();
elements.add("Oranges");
elements.add("Figs");
elements.add("Mangoes");
elements.add("Apple");
List<String> listofComments = new ArrayList<>();
listofComments.add("Apples are better than Oranges");
listofComments.add("I love Mangoes and Oranges");
listofComments.add("I don't know like Figs. Mangoes are my favorites");
listofComments.add("I love Mangoes and Apples");
Output : [Mangoes, Apples, Oranges, Figs] -> Output must be in descending order of the number of occurrences of the elements. If elements appear equal no. of times then they must be arranged alphabetically.
I am new to Java 8 and came across this problem. I tried solving it partially; I couldn't sort it. Can anyone help me with a better code?
My piece of code:
Function<String, Map<String, Long>> function = f -> {
Long count = listofComments.stream()
.filter(e -> e.toLowerCase().contains(f.toLowerCase())).count();
Map<String, Long> map = new HashMap<>(); //creates map for every element. Is it right?
map.put(f, count);
return map;
};
elements.stream().sorted().map(function).forEach(e-> System.out.print(e));
Output: {Apple=2}{Figs=1}{Mangoes=3}{Oranges=2}
In real life scenarios you would have to consider that applying an arbitrary number of match operations to an arbitrary number of comments can become quiet expensive when the numbers grow, so it’s worth doing some preparation:
Map<String,Predicate<String>> filters = elements.stream()
.sorted(String.CASE_INSENSITIVE_ORDER)
.map(s -> Pattern.compile(s, Pattern.LITERAL|Pattern.CASE_INSENSITIVE))
.collect(Collectors.toMap(Pattern::pattern, Pattern::asPredicate,
(a,b) -> { throw new AssertionError("duplicates"); }, LinkedHashMap::new));
The Predicate class is quiet valuable even when not doing regex matching. The combination of the LITERAL and CASE_INSENSITIVE flags enables searches with the intended semantic without the need to convert entire strings to lower case (which, by the way, is not sufficient for all possible scenarios). For this kind of matching, the preparation will include building the necessary data structure for the Boyer–Moore Algorithm for more efficient search, internally.
This map can be reused.
For your specific task, one way to use it would be
filters.entrySet().stream()
.map(e -> Map.entry(e.getKey(), listofComments.stream().filter(e.getValue()).count()))
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.forEachOrdered(e -> System.out.printf("%-7s%3d%n", e.getKey(), e.getValue()));
which will print for your example data:
Mangoes 3
Apple 2
Oranges 2
Figs 1
Note that the filters map is already sorted alphabetically and the sorted of the second stream operation is stable for streams with a defined encounter order, so it only needs to sort by occurrences, the entries with equal elements will keep their relative order, which is the alphabetical order from the source map.
Map.entry(…) requires Java 9 or newer. For Java 8, you’d have to use something like
new AbstractMap.SimpleEntry(…) instead.
You can still modify your function to store Map.Entry instead of a complete Map
Function<String, Map.Entry<String, Long>> function = f -> Map.entry(f, listOfComments.stream()
.filter(e -> e.toLowerCase().contains(f.toLowerCase())).count());
and then sort these entries before performing a terminal operation forEach in your case to print
elements.stream()
.map(function)
.sorted(Comparator.comparing(Map.Entry<String, Long>::getValue)
.reversed().thenComparing(Map.Entry::getKey))
.forEach(System.out::println);
This will then give you as output the following:
Mangoes=3
Apples=2
Oranges=2
Figs=1
First thing is to declare an additional class. It'll hold element and count:
class ElementWithCount {
private final String element;
private final long count;
ElementWithCount(String element, long count) {
this.element = element;
this.count = count;
}
String element() {
return element;
}
long count() {
return count;
}
}
To compute count let's declare an additional function:
static long getElementCount(List<String> listOfComments, String element) {
return listOfComments.stream()
.filter(comment -> comment.contains(element))
.count();
}
So now to find the result we need to transform stream of elements to stream of ElementWithCount objects, then sort that stream by count, then transform it back to stream of elements and collect it into result list.
To make this task easier, let's define comparator as a separate variable:
Comparator<ElementWithCount> comparator = Comparator
.comparing(ElementWithCount::count).reversed()
.thenComparing(ElementWithCount::element);
and now as all parts are ready, final computation is easy:
List<String> result = elements.stream()
.map(element -> new ElementWithCount(element, getElementCount(listOfComments, element)))
.sorted(comparator)
.map(ElementWithCount::element)
.collect(Collectors.toList());
You can use Map.Entry instead of a separate class and inline getElementCount, so it'll be "one-line" solution:
List<String> result = elements.stream()
.map(element ->
new AbstractMap.SimpleImmutableEntry<>(element,
listOfComments.stream()
.filter(comment -> comment.contains(element))
.count()))
.sorted(Map.Entry.<String, Long>comparingByValue().reversed().thenComparing(Map.Entry.comparingByKey()))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
But it's much harder to understand in this form, so I recommend to split it to logical parts.

Lambda expression for supplier to generate IntStream

How do I replace the Supplier code here with lambda expression
IntStream inStream = Stream.generate(new Supplier<Integer>() {
int x= 1;
#Override
public Integer get() {
return x++ ;
}
}).limit(10).mapToInt(t -> t.intValue());
inStream.forEach(System.out::println);
The output of above piece of code is:
1
2
3
4
5
6
7
8
9
10
The Stream::generate is not suitable for this issue. According to the documentation:
This is suitable for generating constant streams, streams of random elements, etc.
You might want to use rather IntStream::range:
IntStream intStream = IntStream.range(1, 11);
intStream.forEach(System.out::println);
Another solution might be using the IntStream.iterate where you can control the increment comfortably using the IntUnaryOperator:
IntStream intStream = IntStream.iterate(1, i -> i+1).limit(10);
intStream.forEach(System.out::println);
As said, the Stream::generate is suitable for the constant streams or random elements. The random element might be obtained using the class Random so here you might want to get an increment using AtomicInteger:
AtomicInteger atomicInteger = new AtomicInteger(1);
IntStream intStream = Stream.generate(atomicInteger::getAndIncrement).limit(10);
intStream.forEach(System.out::println);
Or you can use this
IntStream.iterate(1, i -> i + 1)
.limit(10)
.forEach(System.out::println);
Something like this if you're bound to use Stream.generate specifically :
IntStream inStream = Stream.generate(new AtomicInteger(1)::getAndIncrement)
.limit(10)
.mapToInt(t -> t);
inStream.forEach(System.out::println);
Edit: Using IntStream.generate, you can perform it as
IntStream.generate(new AtomicInteger(1)::getAndIncrement).limit(10);
Note: A better solution in terms of the API design would definitely be to make use of Stream.iterate for such a use case.

Create a object initialization loop using Stream API

I have a set of constant values that are available as a list. Using these values I have to create a key value pair object and this object has to be added to a list. I would like to achieve this using Stream API in JAVA 8. Below is the sample implementation using a for loop
for (int i=0; i<length; i+=2){
list.add(new sampleObject(constant[i],constant[i+1]);
}
Can this be implemented using Stream reduction operations?
Chain IntStream.iterate() that produces a infinite IntStream with IntStream.limit() to make it finite :
List<sampleObject> list =
IntStream.iterate(0, i -> i + 2)
.limit(Math.ceil(length / 2D))
.mapToObj(i -> new sampleObject(constant[i], constant[i+1]))
.collect(Collectors.toList());
Of course it can!
IntStream.iterate(0, i -> i < length, i -> i + 2)
.mapToObj(i -> new sampleObject(constant[i], constant[i+1]))
.collect(Collectors.toList());
I'm not sure off the top of my head, but constant may have to be final or effectively final for this to compile.
Note: I just realized, this overloaded iterate method was added in Java 9. Please see davidxxx's answer for a Java 8 solution!

Java Stream API storing lambda expression as variable

This title sounds stupid even to me, but there must be at least somewhat clever way to achieve such effect and I don't know how else to explain it. I need to sort array using sorted in stream API. Here is my stream so far:
Arrays.stream(sequence.split(" "))
.mapToInt(Integer::parseInt)
.boxed()
.sorted((a, b) -> a.compareTo(b))
.forEach(a -> System.out.print(a + " "));
Now I have two different sorts of course - ascending and descending and the sort I need to use is specified in the user input. So what I want to do is having something like switch with 2 cases: "ascending" and "descending" and a variable to store the lambda expression respectively:
switch(command) {
case "ascending": var = a.compareTo(b);
case "descending": var = b.compareTo(a);
}
Then I my sorted looks like:
.sorted((a, b) -> var)
I got the idea in a python course I attended. There it was available to store an object in variable, thus making the variable "executable". I realize that this lambda is not an object, but an expression, but I'm asking is there any clever way that can achieve such result, or should I just have
if(var)
and two diferent streams for each sort order.
The question is not stupid at all. Answering it in a broader sense: Unfortunately, there is no generic solution for that. This is due to the type inference, which determines one particular type for the lambda expression, based on the target type. (The section about type inference may be helpful here, but does not cover all details regarding lambdas).
Particularly, a lambda like x -> y does not have any type. So there is no way of writing
GenericLambdaTypefunction = x -> y;
and later use function as a drop-in replacement for the actual lambda x -> y.
For example, when you have two functions like
static void useF(Function<Integer, Boolean> f) { ... }
static void useP(Predicate<Integer> p) { ... }
you can call them both with the same lambda
useF(x -> true);
useP(x -> true);
but there is no way of "storing" the x -> true lambda in a way so that it later may be passed to both functions - you can only store it in a reference with the type that it will be needed in later:
Function<Integer, Boolean> f = x -> true;
Predicate<Integer> p = x -> true;
useF(f);
useP(p);
For your particular case, the answer by Konstantin Yovkov already showed the solution: You have to store it as a Comparator<Integer> (ignoring the fact that you wouldn't have needed a lambda here in the first place...)
You can switch between using Comparator.reverseOrder() and Comparator.naturalOrder:
Comparator<Integer> comparator = youWantToHaveItReversed ? Comparator.reverseOrder(): Comparator.naturalOrder();
Arrays.stream(sequence.split(" "))
.map(Integer::valueOf)
.sorted(comparator)
.forEach(a -> System.out.print(a + " "));
In Lambdas you can use a functionblock
(a,b) -> { if(anything) return 0; else return -1;}

How do I convert a Java 8 IntStream to a List?

I'm looking at the docs for the IntStream, and I see an toArray method, but no way to go directly to a List<Integer>
Surely there is a way to convert a Stream to a List?
IntStream::boxed
IntStream::boxed turns an IntStream into a Stream<Integer>, which you can then collect into a List:
theIntStream.boxed().collect(Collectors.toList())
The boxed method converts the int primitive values of an IntStream into a stream of Integer objects. The word "boxing" names the int ⬌ Integer conversion process. See Oracle Tutorial.
Java 16 and later
Java 16 brought the shorter toList method. Produces an unmodifiable list. Discussed here.
theIntStream.boxed().toList()
You could also use mapToObj() on a Stream, which takes an IntFunction and returns an object-valued Stream consisting of the results of applying the given function to the elements of this stream.
List<Integer> intList = myIntStream.mapToObj(i->i).collect(Collectors.toList());
You can use primitive collections available in Eclipse Collections and avoid boxing.
MutableIntList list =
IntStream.range(1, 5)
.collect(IntArrayList::new, MutableIntList::add, MutableIntList::addAll);
Note: I am a contributor to Eclipse Collections.
You can use the collect method:
IntStream.of(1, 2, 3).collect(ArrayList::new, List::add, List::addAll);
In fact, this is almost exactly what Java is doing when you call .collect(Collectors.toList()) on an object stream:
public static <T> Collector<T, ?, List<T>> toList() {
return new Collectors.CollectorImpl(ArrayList::new, List::add, (var0, var1) -> {
var0.addAll(var1);
return var0;
}, CH_ID);
}
Note: The third parameter is only required if you want to run parallel collection; for sequential collection providing just the first two will suffice.
Find the folowing example of finding square of each int element using Java 8 :-
IntStream ints = Arrays.stream(new int[] {1,2,3,4,5});
List<Integer> intsList = ints.map(x-> x*x)
.collect(ArrayList<Integer>::new, ArrayList::add, ArrayList::addAll);

Categories

Resources