Is it useful to create reusable IntStreams? If yes, how? - java

I am wondering, whether it makes sense to create a reusable IntStream for the following problem and how to do so.
Basically, I need to loop over an int[] of numbers to be detected and and count the frequency of these numbers in another int[].
If I had an IntStream-Supplier, it was reusable in some loop. So, I am looking for something like
int[] intsToBeDectected = new int[]{1,2,3}
int[] numbers = new int[]{1,1,1,2,3,3,3,3,3}
Supplier<IntStream> supplier = IntStream.of(numbers); // ERROR
for (int i : intsToBeDetected){
int freq = (int) supplier.get().filter(n -> n=i).count();
}
From object streams in java, I know something like this:
Supplier<Stream<Object>> supplier = () -> Arrays.stream(objects)
This stream of objects is reusable like:
supplier.get().filter()...
However, I somehow fail to transfer this idea to IntStreams - It's not compiling in the line commented with ERROR.
Does it make sense to transfer this concept? If yes, how to do so?

Replace
Supplier<IntStream> supplier = IntStream.of(numbers); // ERROR
with
Supplier<IntStream> supplier = () -> IntStream.of(numbers);

Related

Java Stream HOW TO .stream()

I am trying and googling and can not get it right. How to write this with stream?
private final ConcurrentMap<UUID, Expression> alleMap = new ConcurrentHashMap<>(1000);
private String[] getAllDatabases()
{
Set<String> allDatabases = new HashSet<>();
for (Entry<UUID, Expression> entry : alleMap.entrySet())
{
allDatabases.add(entry.getValue().getChapter().getDatabaseName());
}
List<String> allDatabasesList = new ArrayList<>(allDatabases);
String[] result = new String[allDatabases.size()];
for (int i=0; i < result.length; i++)
{
result[i] = allDatabasesList.get(i);
}
return result;
}
alleMap.values().stream().???
The reason I need an Array is that I am writing a Swing Application and need the String[] for a JOptionPane question.
Breaking this down for you:
Set<String> allDatabases = new HashSet<>();
for (Entry<UUID, Expression> entry : alleMap.entrySet()) {
allDatabases.add(entry.getValue().getChapter().getDatabaseName());
}
This is getting all unique database names for all chapters for expressions that are values in your map.
List<String> allDatabasesList = new ArrayList<>(allDatabases);
String[] result = new String[allDatabases.size()];
for (int i=0; i < result.length; i++) {
result[i] = allDatabasesList.get(i);
}
As far as I can tell this is all just to convert the set of database names to an array. If so, you could have just used allDatabases.toArray(new String[allDatabases.size()]. Streams have a similar method (though they take an IntFunction to create an array rather than the new array):
This translates to:
alleMap.values().stream()
.map(Expression::getChapter)
.map(Chapter::getDatabaseName)
.distinct()
.toArray(String[]::new);
Note the distinct to replicate the uniqueness of sets.
Starts with not wanting arrays. They are low-level, inflexible constructs that don't play nice with generics and in general shouldn't be used unless you really know what you are doing and are willing to accept a little pain to accomodate it.
Streams are a tool. Not a universal improvement. If there's a pressing need to go with streams, by all means. But don't replace everything with streams because you think that's somehow 'better'. Just do what you're comfy with :)
If you must:
You aren't using the keys at all, just the values. So why bother with entrySet? values() will get what you want with far less effort.
List<String> alleMap.values().stream()
.map(e -> e.getChapter().getDataBaseName())
.collect(Collectors.toList());
This is pretty basic; maybe drop the specific case you're working on, and follow some tutorials first, then pick this back up, you'll get the hang of it in no time.

How save a native variable with streams

Hi im trying to understand java 8 api streams, i dont know if this can be done, but im trying to do a filter to a list a save in a new variable like this and the compiler say to me that he cant convert from Stream to String and it has sense but how can i do the operation?
List<String> lista = new ArrayList<String>();
lista.add("1");
lista.add("2");
String num = lista.stream().filter(x -> x.equals("1"));
And if i want to convert that string to int i try to do this but also the compiler say to me, wrong types
List<String> lista = new ArrayList<String>();
lista.add("1");
lista.add("2");
int num = lista.stream().filter(x -> x.equals("1")).map(x -> Integer.parseInt(x));
Streams don't start processing until a terminal method is given to start it. So try this:
List<String> lista = new ArrayList<String>();
lista.add("1");
lista.add("2");
int num = lista.stream().filter(x -> x.equals("1"))
.mapToInt(Integer::parseInt) // use method reference here.
.findFirst().orElse(0);
findFirst returns an OptionalInt so you need to have a default value to return if nothing is found. orElse does that. Also use mapToInt since Integer::parseInt returns an int. Avoids unnecessary boxing. You can apply a similar technique to just get the String when you don't need to parse an int.
You are missing just one call to findFirst:
String num = lista.stream().filter(x -> x.equals("1")).findFirst().get();
And also for the Map:
int num = lista.stream().filter(x -> x.equals("1")).map(x -> Integer.parseInt(x)).findFirst().get();
Be careful when using get method of Optional, if the filter returns nothing it will throw NoSuchElementException.

How to use filter in Java 8 to ignore values from an int array and to collection

I have a list of errorCodes which I want to check whether or not they contain error codes in a separate array. If the error codes exist in list errorCode then I want to filter those out.
This is what I have so far
int[] ignoredErrorCodes = {400, 500};
List<Error> errorCodes = errorsList.stream()
.filter(error -> error.getErrorCode() != ignoredErrorCodes[0])
.collect(Collectors.toList());
how can I check against all values in the array ignoredErrorCodes instead of just one, using streams?
It would be better to store the ignoreds codes in a Set for faster lookup:
Set<Integer> ignored = Set.of(400,500);
List<Error> errorCodes = errorsList.stream()
.filter(error -> !ignored.contains(error.getErrorCode()))
.collect(Collectors.toList());
#Eran is probably right, a Set is likely to best communicate your intent. If you really want to work with an array, though, consider:
import java.util.Arrays
errorsList.stream()
.filter(error -> Arrays.binarySearch(ignoredErrorCodes, error.getCode()) < 0)
.collect(Collectors.toList());

Java8 Associate random point from stream with player object from other stream

So this is one that's really left me puzzled. Lets say I have a Player object, with Point p containing an x and y value:
class Player {
void movePlayer(Point p) {
...
}
}
If I have a bunch of static points (certainly more than players) that I need to randomly, yet uniquely, map to each player's movePlayer function, how would I do so? This process does not need to be done quickly, but often and randomly each time. To add a layer of complication, my points are generated by both varying x and y values. As of now I am doing the following (which crashed my JVM):
public List<Stream<Point>> generatePointStream() {
Random random = new Random();
List<Stream<Point>> points = new ArrayList<Stream<Point>>();
points.add(random.ints(2384, 2413).distinct().mapToObj(x -> new Point(x, 3072)));
points.add(random.ints(3072, 3084).distinct().mapToObj(y -> new Point(2413, y)));
....
points.add(random.ints(2386, 2415).distinct().mapToObj(x -> new Point(x, 3135)));
Collections.shuffle(points);
return points;
}
Note that before I used only one stream with the Stream.concat method, but that threw errors and looked pretty ugly, leading me to my current predicament. And to assign them to all Player objects in the List<Player> players:
players.stream().forEach(p->p.movePlayer(generatePointStream().stream().flatMap(t->t).
findAny().orElse(new Point(2376, 9487))));
Now this almost worked when I used some ridiculous abstraction Stream<Stream<Point>> , except it only used points from the first Stream<Point>.
Am I completely missing the point of streams here? I just liked the idea of not creating explicit Point objects I wouldn't use anyways.
Well, you can define a method returning a Stream of Points like
public Stream<Point> allValues() {
return Stream.of(
IntStream.range(2384, 2413).mapToObj(x -> new Point(x, 3072)),
IntStream.range(3072, 3084).mapToObj(y -> new Point(2413, y)),
//...
IntStream.range(2386, 2415).mapToObj(x -> new Point(x, 3135))
).flatMap(Function.identity());
}
which contains all valid points, though not materialized, due to the lazy nature of the Stream. Then, create a method to pick random elements like:
public List<Point> getRandomPoints(int num) {
long count=allValues().count();
assert count > num;
return new Random().longs(0, count)
.distinct()
.limit(num)
.mapToObj(i -> allValues().skip(i).findFirst().get())
.collect(Collectors.toList());
}
In a perfect world, this would already have all the laziness you wish, including creating only the desired number of Point instances.
However, there are several implementation details which might make this even worse than just collecting into a list.
One is special to the flatMap operation, see “Why filter() after flatMap() is “not completely” lazy in Java streams?”. Not only are substreams processed eagerly, also Stream properties that could allow internal optimizations are not evaluated. In this regard, a concat based Stream is more efficient.
public Stream<Point> allValues() {
return Stream.concat(
Stream.concat(
IntStream.range(2384, 2413).mapToObj(x -> new Point(x, 3072)),
IntStream.range(3072, 3084).mapToObj(y -> new Point(2413, y))
),
//...
IntStream.range(2386, 2415).mapToObj(x -> new Point(x, 3135))
);
}
There is a warning regarding creating too deep concatenated streams, but if you are in control of the creation like here, you can care to create a balanced tree, like
Stream.concat(
Stream.concat(
Stream.concat(a, b),
Stream.concat(c, d)
),
Stream.concat(
Stream.concat(a, b),
Stream.concat(c, d)
)
)
However, even though such a Stream allows to calculate the size without processing elements, this won’t happen before Java 9. In Java 8, count() will always iterate over all elements, which implies having already instantiated as much Point instances as when collecting all elements into a List after the count() operation.
Even worse, skip is not propagated to the Stream’s source, so when saying stream.map(…).skip(n).findFirst(), the mapping function is evaluated up to n+1 times instead of only once. Of course, this renders the entire idea of the getRandomPoints method using this as lazy construct useless. Due to the encapsulation and the nested streams we have here, we can’t even move the skip operation before the map.
Note that temporary instances still might be handled more efficient than collecting into a list, where all instance of the exist at the same time, but it’s hard to predict due to the much larger number we have here. So if the instance creation really is a concern, we can solve this specific case due to the fact that the two int values making up a point can be encapsulated in a primitive long value:
public LongStream allValuesAsLong() {
return LongStream.concat(LongStream.concat(
LongStream.range(2384, 2413).map(x -> x <<32 | 3072),
LongStream.range(3072, 3084).map(y -> 2413L <<32 | y)
),
//...
LongStream.range(2386, 2415).map(x -> x <<32 | 3135)
);
}
public List<Point> getRandomPoints(int num) {
long count=allValuesAsLong().count();
assert count > num;
return new Random().longs(0, count)
.distinct()
.limit(num)
.mapToObj(i -> allValuesAsLong().skip(i)
.mapToObj(l -> new Point((int)(l>>>32), (int)(l&(1L<<32)-1)))
.findFirst().get())
.collect(Collectors.toList());
}
This will indeed only create num instances of Point.
You should do something like:
final int PLAYERS_COUNT = 6;
List<Point> points = generatePointStream()
.stream()
.limit(PLAYERS_COUNT)
.map(s -> s.findAny().get())
.collect(Collectors.toList());
This outputs
2403, 3135
2413, 3076
2393, 3072
2431, 3118
2386, 3134
2368, 3113

Implement counting variable with lambda expression java

I have a question about lambda expressions. I have a class Pair which should hold a String and an int.
Pair gets the String out of a file.
and the int is representiv for the line number.
So far I have this:
Stream<String> lineNumbers = Files.lines(Paths.get(fileName));
List<Integer> posStream = Stream.iterate(0, x -> x + 1).limit(lineNumbers.count()).collect(Collectors.toList());
lineNumbers.close();
Stream<String> line = Files.lines(Paths.get(fileName));
List<Pair> pairs = line.map((f) -> new Pair<>(f,1))
.collect(Collectors.toList());
pairs.forEach(f -> System.out.println(f.toString()));
line.close();
How can I now input the file numbers to the pairs?
Is there a lambda expression which can perform this? Or do I need something else?
There are a few ways to do this. The counter technique suggested by Saloparenator's answer could be implemented as follows, using an AtomicInteger as the mutable counter object and assuming the obvious Pair class:
List<Pair> getPairs1() throws IOException {
AtomicInteger counter = new AtomicInteger(0);
try (Stream<String> lines = Files.lines(Paths.get(FILENAME))) {
return lines.parallel()
.map(line -> new Pair(line, counter.incrementAndGet()))
.collect(toList());
}
}
The problem is that if the stream is run in parallel, the counter won't be incremented in the same order as the lines are read! This will occur if your file has several thousand lines. The Files.lines stream source will batch up bunches of lines and dispatch them to several threads, which will then number their batches in parallel, interleaving their calls to incrementAndGet(). Thus, the lines won't be numbered sequentially. It will work if you can guarantee that your stream will never run in parallel, but it's often a bad idea to write streams that are likely to return different results sequentially vs. in parallel.
Here's another approach. Since you're reading all the lines into memory no matter what, just read them all into a list. Then use a stream to number them:
static List<Pair> getPairs2() throws IOException {
List<String> lines = Files.readAllLines(Paths.get(FILENAME));
return IntStream.range(0, lines.size())
.parallel()
.mapToObj(i -> new Pair(lines.get(i), i+1))
.collect(toList());
}
Another functional way would be to ZIP your list of stream with a integer generator
(I can see java8 dont have it yet but it is essentially merging every cell of two list in a list of pair, so it is easy to implement)
You can see example of generator in java8 here
int cnt = 1;
List<Pair> pairs = line.map((f) -> new Pair<>(f,cnt++))
.collect(Collectors.toList());
I have not tried it yet but may work.

Categories

Resources