I have the list of int - 2, 3, 6, 8, 14, 15, 9, 4
.
I want to search such numbers whose square is present in the list, ex: 2
square 4
is present, so the output list should have 2
.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamTest {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(2, 3, 6, 8, 14, 15, 9, 4);
System.out.println(
list.stream()
.map(i -> hasSquare(i, list))
.distinct()
.collect(Collectors.toList()));
}
private static Object hasSquare(Integer i, List<Integer> list) {
if (list.contains(i * i)) {
return i;
} else {
return 0;
}
}
}
This code is giving me the output [2, 3, 0]
, but I would like to get rid of 0
.
Is there a way to do the same without using a third method?
CodePudding user response:
You could filter before collecting:
list.stream().map( i -> hasSquare(i,list)).filter(e->e!=0).distinct().collect(Collectors.toList()))
and also return Integer instead of Object when you are certain that your list contains Integers.
private static Integer hasSquare(Integer i, List<Integer> list) {
if(list.contains(i*i)){
return i;
} else return 0;
}
CodePudding user response:
The problem lies within the stream and your method .
- You can easily use
filter
method in stream instead ofmap
cause all you are doing is contains check in your method. - You are returning the values and in else part returning
0
.That is why you are having 0 in your result . Instead of returning the existing value return onlytrue
orfalse
that way it will be much easier to control and organized.
private static boolean hasSquare(Integer i, List<Integer> list) {
return list.contains(i*i);
}
Stream will be
System.out.println(list.stream().filter(i-> hasSquare(i, list)).collect(Collectors.toList()));
CodePudding user response:
Your method hasSquare()
has a cost O(n) because method contains()
needs to iterate over the given list. And it will give a quadratic overall time complexity. But we can do better.
In order to do this in a linear time, we need to create a HashSet
containing all the elements from the source list. And then check if the squared value of each element in the source list is present in a set.
It can be written in a single stream-statement by using the built-in collector collectingAndThen()
in conjunction with toSet()
. As the second argument collectingAndThen()
expects a function that is meant to produce the final result.
The function creates a stream over the given list, and filter()
operation will take care of keeping only those elements which squared values are present in the set. Operation distinct()
is required to insure uniqueness.
public static List<Integer> getSquares(List<Integer> list) {
return list.stream()
.collect(Collectors.collectingAndThen(
Collectors.toSet(),
set -> list.stream().filter(num -> set.contains(num * num))
.distinct()
.collect(Collectors.toList())));
}
A more simple approach is to create a HashSet
separately (and I would rather prefer this version):
public static List<Integer> getSquares(List<Integer> list) {
Set<Integer> set = new HashSet<>(list);
return list.stream()
.filter(num -> set.contains(num * num))
.distinct()
.collect(Collectors.toList());
}
main()
public static void main(String[] args) {
List<Integer> source = List.of(2, 3, 6, 8, 14, 15, 9, 4);
System.out.println(getSquares(source));
}
Output:
[2, 3]
Sidenote: if you're using Java 9 and above, List.of()
is a preferred way to initialize the List because it doesn't create an intermediate array (if number of elements not greater than 10
).
CodePudding user response:
You want to filter instead of mapping.
import java.util.*;
import java.util.stream.Collectors;
public class StreamTEst {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(2, 3, 6, 8, 14, 15, 9, 4);
List<Integer> result = list.stream()
.filter(i -> list.contains(i*i))
.distinct()
.collect(Collectors.toList());
System.out.println(result);
}
}
If you start working with longer lists then you might want to create a hash set for faster lookup like this:
import java.util.*;
import java.util.stream.Collectors;
public class StreamTEst {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(2, 3, 6, 8, 14, 15, 9, 4);
Set<Integer> lookup = new HashSet<>(list);
List<Integer> result = list.stream()
.filter(i -> lookup.contains(i*i))
.distinct()
.collect(Collectors.toList());
System.out.println(result);
}
}
CodePudding user response:
My Solution: Takes advantage of constant lookup time of Set
import java.util.Set;
import java.util.HashSet;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List <Integer> list = Arrays.asList(2, 3, 6, 8, 14, 15, 9, 4);
Set <Integer> numbers = new HashSet <Integer> (list);
List <Integer> result = list.stream()
.filter((current) - > numbers.contains(current * current))
.collect(Collectors.toList());
System.out.println(result);
}
}
Similar to your approach:
List.contains()
takes n (element count in your list) time to compute. Applying same operation to each element takes n*n time, which scales logarithmically, inefficient and expensive. Java stream executes in parallel, its fast but still your code not scales well.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List <Integer> list = Arrays.asList(2, 3, 6, 8, 14, 15, 9, 4);
System.out.println(
list.stream()
.filter(i -> hasSquare(i, list))
.collect(Collectors.toList()));
}
private static boolean hasSquare(Integer i, List < Integer > list) {
if (list.contains(i * i)) {
return true;
} else {
return false;
}
}
}
CodePudding user response:
Your goal is achievable without using a third method. You could simply stream your list
and use the filter
operation to keep only the numbers whose square is equal to any number of the same list. Also, if you want to keep distinct elements, you could collect your elements in a Set
instead of using an expensive operation like disntict
.
List<Integer> list = Arrays.asList(2, 3, 6, 8, 14, 15, 9, 4);
Set<Integer> setRes = list.stream()
.filter(n -> list.stream().anyMatch(x -> x == n * n))
.collect(Collectors.toSet());
System.out.println(setRes);
Here is a link to test the code:
https://www.jdoodle.com/iembed/v0/rrE
Output
[2, 3]
CodePudding user response:
I think below is one of straight-forward way:
List<Integer> list = Arrays.asList(2, 3, 6, 8, 14, 15, 9, 4);
List<Integer> result = list.stream().collect(ArrayList<Integer>::new, (x, y) -> {
if (list.contains((int) Math.pow(y, 2)))
x.add(y);
}, (x, y) -> y.addAll(x));
System.out.println(result);