How can I convert Stream to IntStream - java-8

I want to implement a static method called youngWinners that given a Stream<Winner> returns a new Stream<Winner> containing the winners that are younger than 35, ordered alphabetically.
Inside my file I have : index, year, age, name, movie.
My problem is that I don't know how I can convert Stream into IntStream to compare the age with 35. I also got a bit confused, do I have to use comparators to do this or not ?
public static Stream<Winner> youngWinners(Stream<Winner> young) {
// Stream<Winner> youngWin = young;
String[] toString = young.toArray(s -> new String[s]);
Arrays.stream(toString).flatMap((<any> f) -> {
try {
return Files.lines(Paths.get(f))
.filter(age -> int (age) <= 35 )
.mapToInt(a -> a.getWinnerage())
.map(WinneropsDB::new);
} catch (Exception e) {
System.out.println("error");
return null;
}
});
return null;
}

you don't need an IntStream at all.
Simply do:
return young.filter(w -> w.getAge() < 35)
.sorted(Comparator.comparing(Winner::getName));

Related

Pagination and Sorting - custom sorting of data

I have a problem with sorting data in my project.
Since I implemented pagination I don't know how solve this issue.
Before pagination I fetched whole list of entities and sort it by this class:
public class EntitySorter {
private static final Logger LOG = Logger.getLogger(EntitySorter.class);
public static int sort(String s1, String s2) {
if (StringUtils.isBlank(s1) || StringUtils.isBlank(s2)) {
return -1;
}
if (!s1.contains("/") || !s2.contains("/")) {
return -1;
}
if (s1.substring(s1.lastIndexOf("/") + 1).length() != 4 ||
s2.substring(s2.lastIndexOf("/") + 1).length() != 4) {
return -1;
}
final String year1 = s1.substring(s1.lastIndexOf("/") + 1);
final String year2 = s2.substring(s2.lastIndexOf("/") + 1);
if (!NumberUtils.isDigits(year1) || !NumberUtils.isDigits(year2)) {
return -1;
}
final int result = NumberUtils.toInt(year1) - NumberUtils.toInt(year2);
if (result != 0) {
return result;
}
final String caseNumber1 = s1.substring(0, s1.indexOf("/"));
final String caseNumber2 = s2.substring(0, s2.indexOf("/"));
if (!NumberUtils.isDigits(caseNumber1) && NumberUtils.isDigits(caseNumber2)) {
try {
final int intCaseNumber1 = Integer.parseInt(caseNumber1.replaceAll("[^0-9]", ""));
return intCaseNumber1 - Integer.parseInt(caseNumber2);
} catch (NumberFormatException e) {
LOG.error(e.getMessage(), e);
}
return -1;
}
if (NumberUtils.isDigits(caseNumber1) && !NumberUtils.isDigits(caseNumber2)) {
try {
final int intCaseNumber2 = Integer.parseInt(caseNumber2.replaceAll("[^0-9]", ""));
return Integer.parseInt(caseNumber1) - intCaseNumber2;
} catch (NumberFormatException e) {
LOG.error(e.getMessage(), e);
}
return -1;
}
if (!NumberUtils.isDigits(caseNumber1) && !NumberUtils.isDigits(caseNumber2)) {
try {
final int intCaseNumber1 = Integer.parseInt(caseNumber1.replaceAll("[^0-9]", ""));
final int intCaseNumber2 = Integer.parseInt(caseNumber2.replaceAll("[^0-9]", ""));
return intCaseNumber1 - intCaseNumber2;
} catch (NumberFormatException e) {
LOG.error(e.getMessage(), e);
}
return -1;
}
return NumberUtils.toInt(caseNumber1) - NumberUtils.toInt(caseNumber2);
}
}
Let's take some example:
We have a list of IDs:
101/2021
102/2021
1/2022
86/2020
Correct sorted list is:
1/2022
102/2021
101/2021
86/2020
In database this ID is one column. It's not split to number and year. I tried to use Sort.by() but I didn't make a success. How can I use pagination and keep correct sorting?
For pagination to work optimally, the data should be indexed correctly..
If there is a different representation of the data you can use with one column then it's the best way.
If not then the easy way would just to decompose one column to multiple columns, create a multi column index and sort by these columns + you need to understand if the natural ordering of the columns fits your logic.
The hard way would be to create user defined function and index on it and other solutions, but I would avoid the unnecessary complexity.
Keep it simple!

adding parallell to a stream causes NullPointerException

I'm trying to get my head around Java streams. It was my understanding that they provide an easy way to parallellize behaviour, and that also not all operations benefit from parallellization, but that you always have the option to do it by just slapping .parallell() on to an existing stream. This might make the stream go slower in some cases, or return the elements in a different order at the end etc, but you always have the option to parallellize a stream. That's why I got confused when I changed this method:
public static List<Integer> primeSequence() {
List<Integer> list = new LinkedList<Integer>();
IntStream.range(1, 10)
.filter(x -> isPrime(x))
.forEach(list::add);
return list;
}
//returns {2,3,5,7}
to this:
public static List<Integer> primeSequence() {
List<Integer> list = new LinkedList<Integer>();
IntStream.range(1, 10).parallel()
.filter(x -> isPrime(x))
.forEach(list::add);
return list;
}
//throws NullPointerException();
I thought all streams were serial unless otherwise stated and parallel() just made then execute in parallel. What am I missing here? Why does it throw an Exception?
There is one significant issue with your initial primeSequence method implementation - you mix stream iteration with outer list modification. You should avoid using streams that way, otherwise you will face a lot of problems. Like the one you have described. If you take a look at how add(E element) method is implemented you will see something like this:
public boolean add(E e) {
this.linkLast(e);
return true;
}
void linkLast(E e) {
LinkedList.Node<E> l = this.last;
LinkedList.Node<E> newNode = new LinkedList.Node(l, e, (LinkedList.Node)null);
this.last = newNode;
if (l == null) {
this.first = newNode;
} else {
l.next = newNode;
}
++this.size;
++this.modCount;
}
If you use CopyOnWriteArrayList instead of a LinkedList in your example, there will be no NullPointerException thrown - only because CopyOnWriteArrayList uses locking for multithread execution synchronization:
public boolean add(E e) {
ReentrantLock lock = this.lock;
lock.lock();
boolean var6;
try {
Object[] elements = this.getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
this.setArray(newElements);
var6 = true;
} finally {
lock.unlock();
}
return var6;
}
But it is still not the best way to utilize parallel stream.
Correct way to use Stream API
Consider following modification to your code:
public static List<Integer> primeSequence() {
return IntStream.range(1, 10)
.parallel()
.filter(x -> isPrime(x))
.boxed()
.collect(Collectors.toList());
}
Instead of modifying some outer list (of any kind) we are collecting the result and return a final list. You can transform any list to a stream using .stream() method and you don't have to worry about initial list - all operation you will apply to that list won't modify the input and the result will be a copy of the input list.
I hope it helps.

String stream joining: stream has already been operated upon or closed

Using java8 to concatenate an object certain field value with a "_". The last line in the code throws an "stream has already been operated upon or closed".
Stream<Field> fields = ...
Stream<String> exclusions = ...
Stream<String> stringStream = fields.filter(f -> exclusions.anyMatch(e -> e.equals(f.getName())))
.map(f -> {
f.setAccessible(true);
Object value = null;
try {
value = f.get(obj);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return value;
})
.filter(v -> v != null)
.map(Object::toString);
String suffix = stringStream.collect(Collectors.joining("_"));
EDIT: I have tried this with:
List<Foo> list = new ArrayList<>();
list.stream().filter(item -> item != null).map(item -> {
String value = null;
return value;
}).filter(item -> item != null).map(item -> {
String value = null;
return value;
}).collect(Collectors.joining(""));
And there is no such exception.
How many times is the first filter called? More then once right? The exclusions that you use in the first call to filter is consumed via anyMatch; thus the second time you try to use it - you get the exception.
The way to solve it, would be to stream on every single filter operation:
filter(f -> sourceOfExclusions.stream().anyMatch...

I have a long time to stay at making some RxJava codes better to average some data

I have a collection of data like dummy below
class Place {
userId,
price
}
That means a collection of some places.
Use-case:
There is a user with userId and login.
How to calc average place-price that equal to userId ?
RxJava is nice and I have tried filter and toList, however it is not so performance nice.
Observable.fromIterable(places)
.subscribeOn(Schedulers.newThread())
.filter(new Predicate<Place>() {
#Override
public boolean test(Place place) throws Exception {
return place.userId == global.login.userId;
}
})
.toList()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<List<Place>>() {
#Override
public void accept(List<Place> filteredPlace) throws Exception {
//Here I have to use loop to do math-average, it is not nice to average.
}
});
If the places is something that is already available in-memory, you can rearrange the evaluation such as this:
Observable.just(places)
.subscribeOn(Schedulers.computation())
.map((Iterable<Place> list) -> {
double sum = 0.0;
int count = 0;
for (Place p : list) {
if (p.userId == global.login.userId) {
sum += p.price;
count++;
}
}
return sum / count;
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(average -> { /* display average */ });
If the sequence of places becomes available over time (through an Observable):
Observable<Place> places = ...
places
.observeOn(Schedulers.computation())
.filter((Place p) -> p.userId == global.login.userId)
.compose(o -> MathObservable.averageDouble(o.map(p -> p.price)))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(average -> { /* display average */ });
MathObservable is part of the RxJava 2 Extensions library.

Java 8 is not maintaining the order while grouping

I m using Java 8 for grouping by data. But results obtained are not in order formed.
Map<GroupingKey, List<Object>> groupedResult = null;
if (!CollectionUtils.isEmpty(groupByColumns)) {
Map<String, Object> mapArr[] = new LinkedHashMap[mapList.size()];
if (!CollectionUtils.isEmpty(mapList)) {
int count = 0;
for (LinkedHashMap<String, Object> map : mapList) {
mapArr[count++] = map;
}
}
Stream<Map<String, Object>> people = Stream.of(mapArr);
groupedResult = people
.collect(Collectors.groupingBy(p -> new GroupingKey(p, groupByColumns), Collectors.mapping((Map<String, Object> p) -> p, toList())));
public static class GroupingKey
public GroupingKey(Map<String, Object> map, List<String> cols) {
keys = new ArrayList<>();
for (String col : cols) {
keys.add(map.get(col));
}
}
// Add appropriate isEqual() ... you IDE should generate this
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final GroupingKey other = (GroupingKey) obj;
if (!Objects.equals(this.keys, other.keys)) {
return false;
}
return true;
}
#Override
public int hashCode() {
int hash = 7;
hash = 37 * hash + Objects.hashCode(this.keys);
return hash;
}
#Override
public String toString() {
return keys + "";
}
public ArrayList<Object> getKeys() {
return keys;
}
public void setKeys(ArrayList<Object> keys) {
this.keys = keys;
}
}
Here i am using my class groupingKey by which i m dynamically passing from ux. How can get this groupByColumns in sorted form?
Not maintaining the order is a property of the Map that stores the result. If you need a specific Map behavior, you need to request a particular Map implementation. E.g. LinkedHashMap maintains the insertion order:
groupedResult = people.collect(Collectors.groupingBy(
p -> new GroupingKey(p, groupByColumns),
LinkedHashMap::new,
Collectors.mapping((Map<String, Object> p) -> p, toList())));
By the way, there is no reason to copy the contents of mapList into an array before creating the Stream. You may simply call mapList.stream() to get an appropriate Stream.
Further, Collectors.mapping((Map<String, Object> p) -> p, toList()) is obsolete. p->p is an identity mapping, so there’s no reason to request mapping at all:
groupedResult = mapList.stream().collect(Collectors.groupingBy(
p -> new GroupingKey(p, groupByColumns), LinkedHashMap::new, toList()));
But even the GroupingKey is obsolete. It basically wraps a List of values, so you could just use a List as key in the first place. Lists implement hashCode and equals appropriately (but you must not modify these key Lists afterwards).
Map<List<Object>, List<Object>> groupedResult=
mapList.stream().collect(Collectors.groupingBy(
p -> groupByColumns.stream().map(p::get).collect(toList()),
LinkedHashMap::new, toList()));
Based on #Holger's great answer. I post this to help those who want to keep the order after grouping as well as changing the mapping.
Let's simplify and suppose we have a list of persons (int age, String name, String adresss...etc) and we want the names grouped by age while keeping ages in order:
final LinkedHashMap<Integer, List<String> map = myList
.stream()
.sorted(Comparator.comparing(p -> p.getAge())) //sort list by ages
.collect(Collectors.groupingBy(p -> p.getAge()),
LinkedHashMap::new, //keeps the order
Collectors.mapping(p -> p.getName(), //map name
Collectors.toList())));

Resources