Is [var = modify(var)] a bad practice? - java

As a simplified example, given an array of numbers. I need to apply some value filters.
So what I'm doing currently:
Original given array: List<Integer>originalList;
How I construct my filtering methods:
List<Integer> removeNegativeNumber(final List<Integer> numberList) {
return FluentIterable.from(numberList).filter(rule).toList();
}
How I use:
List<Integer> filteredList = removeNegativeNumber(originalList);
filteredList = removeOddNumber(filteredList);
filteredList = removeZeroNumber(filteredList);
My feeling tells me that it can be done better. But I don't know how. Can someone give me advises, recommendations for improving my code ?

Consider lambdas and Stream#filter() if you're using Java 8:
List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5);
List<Integer> filteredList = list.stream()
.filter(i -> i % 2 == 0)
.filter(i -> i != 0)
.collect(Collectors.toList());
System.out.println(filteredList);
Or using IntStream:
IntStream stream = IntStream.of(0, 1, 2, 3, 4, 5);
IntStream filteredStream = stream.filter(i -> i % 2 == 0)
.filter(i -> i != 0);
filteredStream.forEach(System.out::println);
Javadoc:
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#filter-java.util.function.Predicate-

If you don't need to retain neither the original nor transitional values, you could use methods with side-effects rather than a pure function :
removeNegativeNumber(theOnlyList);
removeOddNumber(theOnlyList);
removeZeroNumber(theOnlyList);
// now the list only contains positive non-zero even numbers
These methods could be implemented by using an Iterator's remove() method when its current element does not satisfy the conditions.
If you need to retain the original values, but not the transitional values, you should use pure functions and link them as described by 4castle. As an addition, a more modern way to do so would be to use the Stream API :
List<Integer> filteredList = originalList.stream()
.filter(i -> i >= 0)
.filter(i -> i % 2 == 0)
.filter(i -> i != 0)
.collect(Collectors.toList());

You could chain the statements together, such as like this:
List<Integer> filteredList = removeZeroNumber(
removeOddNumber(
removeNegativeNumber(originalList)));
But that is wasteful because it maps back and forth from a List to FluentIterable with each method. You're better off chaining the underlying FluentIterable methods:
List<Integer> filteredList = FluentIterable.from(numberList)
.filter(i -> i > 0)
.filter(i -> i % 2 == 0)
.toList();
If you're using Java 8 though, an IntStream should be used to prevent repeated autoboxing:
List<Integer> filteredList = originalList.stream()
.mapToInt(Integer::intValue)
.filter(i -> i > 0)
.filter(i -> i % 2 == 0)
.boxed()
.collect(Collectors.toList());

Related

How to simplify comparing?

I do have list like this:
List<Integer> list = new ArrayList<>();
list.add(24);
list.add(12);
list.add(0);
list.add(36);
list.add(1);
list.add(99);
I want to sort it ascendig, but 0 has to always be on the last one. Is there any way to simplify it?
List<Integer> collect = list.stream()
.sorted((a,b) -> b.compareTo(a))
.sorted((a, b) -> Integer.valueOf(0).equals(a) ? 1 : -1)
.collect(Collectors.toList());
List<Integer> list = List.of(24, 12, 0, 36, 1, 99);
List<Integer> sorted = list.stream()
.sorted(Comparator.comparingInt(a -> a == 0 ? Integer.MAX_VALUE : a))
.toList();
System.out.println(sorted);
Seems to work; prints [1, 12, 24, 36, 99, 0].
The one downside is that it won't do the right thing if literally the maximum integer value (which is 2147483647) is in your list, in which case it'll sort the 0s amongst them instead of after them. If that is a problem, nothing is going to look significantly shorter than what you did.
NB: Your code appears to sort descending. In which case the 0 would already sort at the end unless you have negative numbers. If that's what you want and 'ascending' was a typo, you'd have to use MIN_VALUE instead, and reverse the comparator (tack .reverse() t the end).
You can process in the same sorted:
List<Integer> collect = list.stream()
.sorted((o1, o2) -> o1 == 0 ? 1 : (o2 == 0 ? -1 : o1.compareTo(o2)))
.collect(Collectors.toList());
May be introducing variables for the comparators and comparing the boolean if an int equals to zero might simplify or at least make it readable
Comparator<Integer> natural = Comparator.naturalOrder();
Comparator<Integer> zeroLast = Comparator.comparing(i -> i.equals(0));
List<Integer> collect = list.stream()
.sorted(natural)
.sorted(zeroLast)
.collect(Collectors.toList());

Iterate over two Lists of Lists with IntStream instead of streams

I am trying to use streams in order to iterate over two lists of lists in order to verify if the inner lists sizes are the same for the same index. I have managed to achieve this using streams, but I have to rewrite using an IntStream and mapToObj.
My current approach is:
List<List<String>> a = config.getStrips();
List<List<Integer>> b = slotMachineConfig.getWeights();
a.stream()
.filter(first ->
b.stream()
.allMatch(second -> second.size() == first.size())
)
.findFirst()
.orElseThrow(InvalidConfigException::new);
The problem is that I cannot be sure that the sizes will correspond for the big lists, so I have to rewrite this using IntStream and also using indexes for each list.
What I have so far, but does not work looks like this, I am trying to write a "validate" function in order to verify the inner lists, but it seems like I get an error there saying "no instance of type variable U exist so that void conforms to U".
IntStream.range(0, a.size())
.mapToObj(i -> validate(i, a.get(i), b.get(i)))
.findFirst()
.orElseThrow(SlotMachineInvalidConfigException::new);
public void validate(int index, List<String> firstList, List<Integer> secondList) {
How can I rewrite my method using IntStream and mapToObj, can anyone help me?
You have the right idea but you don't really need a separate validation function if you are just comparing sizes. Here's a working example that supports any list types:
public class ListSizeMatcher {
public <T,S> boolean sizeMatches(List<List<T>> list1, List<List<S>> list2) {
return list1.size() == list2.size()
&& IntStream.range(0, list1.size())
.allMatch(i -> list1.get(i).size() == list2.get(i).size());
}
public static void main(String[] args) {
ListSizeMatcher matcher = new ListSizeMatcher();
System.out.println(matcher.sizeMatches(List.of(List.of(1)), List.of(List.of("a"), List.of("b"))));
System.out.println(matcher.sizeMatches(List.of(List.of(1)), List.of(List.of("a", "b"))));
System.out.println(matcher.sizeMatches(List.of(List.of(1, 2)), List.of(List.of("a", "b"))));
}
}
Note that from a design perspective if each item in the list matches the corresponding item in a separate list you'd be better off creating a single class that contains both items.
If I understand correctly, I think something like this would work:
List<List<String>> a = config.getStrips();
List<List<Integer>> b = slotMachineConfig.getWeights();
if (a.size() != b.size()) throw new InvalidConfigException();
boolean allTheSame = IntStream.range(0, a.size())
.map(i -> a.get(i).size() - b.get(i).size())
.allMatch(diff -> diff == 0);
if (!allTheSame) throw new InvalidConfigException();
For the record, your validate function returns void but I'll assume it was meant to return a boolean
here is a more compact version
List<List<String>> a = new LinkedList<>();
List<List<Integer>> b = new LinkedList<>();
boolean match = IntStream.range(0, a.size())
.mapToObj(i -> a.get(i).size() == b.get(i).size())
.reduce(Boolean::logicalAnd).orElseThrow(InvalidConfigException::new);
if (!match) {
throw new InvalidConfigException();
}
Alternative:
List<List<String>> a = new LinkedList<>();
List<List<Integer>> b = new LinkedList<>();
if (IntStream.range(0, a.size()).filter(i -> a.get(i).size() != b.get(i).size()).count() > 0){
throw new InvalidConfigException();
};
At the end of the day it only takes 1 to be different and fail.
The error means that the validate method cannot be void and it is expected to return some valid value (possibly boolean).
If the inner lists are supposed to have the equal sizes to be valid, the check may look as follows:
// assuming the sizes of outer lists are equal
boolean allSizesEqual = IntStream.range(0, a.size())
.allMatch(i -> a.get(i).size() == b.get(i).size());
if (!allSizesEqual) {
throw new InvalidConfigException("Not all sizes are valid");
}
If there's a need to find specific indexes where a discrepancy is detected:
List<Integer> badIndexes = IntStream.range(0, a.size())
.filter(i -> a.get(i).size() != b.get(i).size()) // IntStream
.boxed() // Stream<Integer>
.collect(Collectors.toList());
if (!badIndexes.isEmpty()) {
throw new InvalidConfigException("Different indexes found: " + badIndexes);
}
Or validate method could be fixed to return appropriate value for the filter:
boolean allItemsValid = IntStream.range(0, a.size())
.allMatch(i -> listsAreValid(a.get(i), b.get(i)));
if (!allItemsValid) {
throw new InvalidConfigException("Not all entries are valid");
}
public boolean listsAreValid(List<String> innerA, List<Integer> innerB) {
// any advanced logic
return innerA.size() == innerB.size();
}

java stream find match or the last one?

How to find the first match or the last element in a list using java stream?
Which means if no element matches the condition,then return the last element.
eg:
OptionalInt i = IntStream.rangeClosed(1,5)
.filter(x-> x == 7)
.findFirst();
System.out.print(i.getAsInt());
What should I do to make it return 5;
Given the list
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
You could just do :
int value = list.stream().filter(x -> x == 2)
.findFirst()
.orElse(list.get(list.size() - 1));
Here if the filter evaluates to true the element is retrieved, else the last element in the last is returned.
If the list is empty you could return a default value, for example -1.
int value = list.stream().filter(x -> x == 2)
.findFirst()
.orElse(list.isEmpty() ? -1 : list.get(list.size() - 1));
You can use reduce() function like that:
OptionalInt i = IntStream.rangeClosed(1, 5)
.reduce((first, second) -> first == 7 ? first : second);
System.out.print(i.getAsInt());
Basically I would use one of the following two methods or deviations thereof:
Stream variant:
<T> T getFirstMatchOrLast(List<T> list, Predicate<T> filter, T defaultValue) {
return list.stream()
.filter(filter)
.findFirst()
.orElse(list.isEmpty() ? defaultValue : list.get(list.size() - 1));
}
non-stream variant:
<T> T getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter, T defaultValue) {
T relevant = defaultValue;
for (T entry : iterable) {
relevant = entry;
if (filter.test(entry))
break;
}
return relevant;
}
Or as also Ilmari Karonen suggested in the comment with Iterable<T> you are then able to even call stream::iterator in case you really deal with a Stream instead of a List. Calling the shown methods would look as follows:
getFirstMatchOrLast(Arrays.asList(1, 20, 3), i -> i == 20, 1); // returns 20
getFirstMatchOrLast(Collections.emptyList(), i -> i == 3, 20); // returns 20
getFirstMatchOrLast(Arrays.asList(1, 2, 20), i -> i == 7, 30); // returns 20
// only non-stream variant: having a Stream<Integer> stream = Stream.of(1, 2, 20)
getFirstMatchOrLast(stream::iterator, i -> i == 7, 30); // returns 20
I wouldn't use reduce here because it sounds wrong to me in the sense, that it also goes through the whole entries even though the first entry could have matched already, i.e. it doesn't short-circuit anymore. Moreover for me it isn't as readable as filter.findFirst.orElse... (but that's probably just my opinion)
I probably would then even end up with something as follows:
<T> Optional<T> getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter) {
T relevant = null;
for (T entry : iterable) {
relevant = entry;
if (filter.test(entry))
break;
}
return Optional.ofNullable(relevant);
}
// or transform the stream variant to somethinng like that... however I think that isn't as readable anymore...
so that calls would rather look like:
getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseThrow(...)
getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElse(0);
getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseGet(() -> /* complex formula */);
getFirstMatchOrLast(stream::iterator, i -> i == 5).ifPresent(...)
if you want to do this in one pipeline then you could do:
int startInc = 1;
int endEx = 5;
OptionalInt first =
IntStream.concat(IntStream.range(startInc, endEx)
.filter(x -> x == 7), endEx > 1 ? IntStream.of(endEx) : IntStream.empty())
.findFirst();
but you're probably better off collecting the generated numbers into a list then operate on it as follows:
// first collect the numbers into a list
List<Integer> result = IntStream.rangeClosed(startInc,endEx)
.boxed()
.collect(toList());
// then operate on it
int value = result.stream()
.filter(x -> x == 7)
.findFirst()
.orElse(result.get(result.size() - 1));
Alternatively, if you want to make the latter return an empty Optional in the case of the source being empty (if that's a possible scenario) instead of an exception then you could do:
List<Integer> result = IntStream.rangeClosed(startInc,endEx)
.boxed()
.collect(toList());
Optional<Integer> first =
Stream.concat(result.stream().filter(x -> x == 7), result.isEmpty() ?
Stream.empty() : Stream.of(result.get(result.size() - 1)))
.findFirst();
I am not sure why you really want to use streams for that, a simple for-loop would be enough:
public static <T> T getFirstMatchingOrLast(List<? extends T> source, Predicate<? super T> predicate){
// handle empty case
if(source.isEmpty()){
return null;
}
for(T t : source){
if(predicate.test(t)){
return t;
}
}
return source.get(source.size() -1);
}
Which then can be called like:
Integer match = getFirstMatchingOrLast(ints, i -> i == 7);

Java 8 Streams difference between map and filter

Result of both approach are same which is best practise to use. I am new bee to java 8. i am little bit confused on stream.map and stream.filter
List<String> alpha =
Arrays.asList("a", "b", "csddddddddddd",
"d", "ssdddddddddd", "sw", "we", "wew");
// Java 8
List<String> collect = alpha.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
List<Integer> collect2 = alpha.stream()
.map(s -> s.length())
.collect(Collectors.toList());
List<Integer> collect3 = collect2.stream()
.filter(s -> s > 10)
.collect(Collectors.toList());
List<Integer> collect4 = collect2.stream()
.map(s -> {
Integer temp = 0;
if (s > 10) {
temp = s;
}
return temp;
})
.filter(s -> s > 10)
.collect(Collectors.toList());
Result of both List are same :
[13, 12]
[13, 12]
what is best approach. and what is best approach in this regards.
If you want to filter the elements of a Stream by some condition (i.e. remove elements that don't satisfy the condition), use filter, not map.
The purpose of map is to convert an element of one type to an element of another type.
The only reason you get the same results in collect3 and collect4 is that after applying map in collect4, you apply a filter, which removes all the 0s produced by map.
The entire .map(s ->{ Integer temp = 0;if(s>10) {temp=s;} return temp;}) call is redundant. That's a very unreadable and inefficient way to write code.

Java predicate - match against first predicate [duplicate]

I've just started playing with Java 8 lambdas and I'm trying to implement some of the things that I'm used to in functional languages.
For example, most functional languages have some kind of find function that operates on sequences, or lists that returns the first element, for which the predicate is true. The only way I can see to achieve this in Java 8 is:
lst.stream()
.filter(x -> x > 5)
.findFirst()
However this seems inefficient to me, as the filter will scan the whole list, at least to my understanding (which could be wrong). Is there a better way?
No, filter does not scan the whole stream. It's an intermediate operation, which returns a lazy stream (actually all intermediate operations return a lazy stream). To convince you, you can simply do the following test:
List<Integer> list = Arrays.asList(1, 10, 3, 7, 5);
int a = list.stream()
.peek(num -> System.out.println("will filter " + num))
.filter(x -> x > 5)
.findFirst()
.get();
System.out.println(a);
Which outputs:
will filter 1
will filter 10
10
You see that only the two first elements of the stream are actually processed.
So you can go with your approach which is perfectly fine.
However this seems inefficient to me, as the filter will scan the whole list
No it won't - it will "break" as soon as the first element satisfying the predicate is found. You can read more about laziness in the stream package javadoc, in particular (emphasis mine):
Many stream operations, such as filtering, mapping, or duplicate removal, can be implemented lazily, exposing opportunities for optimization. For example, "find the first String with three consecutive vowels" need not examine all the input strings. Stream operations are divided into intermediate (Stream-producing) operations and terminal (value- or side-effect-producing) operations. Intermediate operations are always lazy.
return dataSource.getParkingLots()
.stream()
.filter(parkingLot -> Objects.equals(parkingLot.getId(), id))
.findFirst()
.orElse(null);
I had to filter out only one object from a list of objects. So i used this, hope it helps.
In addition to Alexis C's answer, If you are working with an array list, in which you are not sure whether the element you are searching for exists, use this.
Integer a = list.stream()
.peek(num -> System.out.println("will filter " + num))
.filter(x -> x > 5)
.findFirst()
.orElse(null);
Then you could simply check whether a is null.
Already answered by #AjaxLeung, but in comments and hard to find.
For check only
lst.stream()
.filter(x -> x > 5)
.findFirst()
.isPresent()
is simplified to
lst.stream()
.anyMatch(x -> x > 5)
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
// Stream is ~30 times slower for same operation...
public class StreamPerfTest {
int iterations = 100;
List<Integer> list = Arrays.asList(1, 10, 3, 7, 5);
// 55 ms
#Test
public void stream() {
for (int i = 0; i < iterations; i++) {
Optional<Integer> result = list.stream()
.filter(x -> x > 5)
.findFirst();
System.out.println(result.orElse(null));
}
}
// 2 ms
#Test
public void loop() {
for (int i = 0; i < iterations; i++) {
Integer result = null;
for (Integer walk : list) {
if (walk > 5) {
result = walk;
break;
}
}
System.out.println(result);
}
}
}
A generic utility function with looping seems a lot cleaner to me:
static public <T> T find(List<T> elements, Predicate<T> p) {
for (T item : elements) if (p.test(item)) return item;
return null;
}
static public <T> T find(T[] elements, Predicate<T> p) {
for (T item : elements) if (p.test(item)) return item;
return null;
}
In use:
List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5);
Integer[] intArr = new Integer[]{1, 2, 3, 4, 5};
System.out.println(find(intList, i -> i % 2 == 0)); // 2
System.out.println(find(intArr, i -> i % 2 != 0)); // 1
System.out.println(find(intList, i -> i > 5)); // null
Improved One-Liner answer: If you are looking for a boolean return value, we can do it better by adding isPresent:
return dataSource.getParkingLots().stream().filter(parkingLot -> Objects.equals(parkingLot.getId(), id)).findFirst().isPresent();

Categories