collecting column of multidimensional array to set - java

I have an attribute this.sudoku which is a int[9][9] array.
I need to get a column of this into a set.
Set<Integer> sudoku_column = IntStream.range(0, 9)
.map(i -> this.sudoku[i][column])
.collect(Collectors.toSet());
I expect a columns values in this set. but it says that Collectors.toSet() cannot be applied to this collect function in the chain. Can someone explain why?

IntStream#map consumes an IntUnaryOperator which represents an operation on a single int-valued operand that produces an int-valued result thus the result is an IntStream, however IntStream does not have the collect overload you're attempt to use, which means you have a couple of options; i.e. either use IntStream#collect:
IntStream.range(0, 9)
.collect(HashSet::new, (c, i) -> c.add(sudoku[i][column]), HashSet::addAll);
or use mapToObj to transform from IntStream to Stream<Integer> which you can then call .collect(Collectors.toSet()) upon.
IntStream.range(0, 9)
.mapToObj(i -> this.sudoku[i][column])
.collect(Collectors.toSet());

IntStream#map takes an IntUnaryOperator which is a function to transform an int to another int.
It's fine if you want to continue with an IntStream. But if you need to collect the stream into a Set<Integer>, you need to turn your IntStream into a stream of boxed ints, a Stream<Integer>, by IntStream#boxed.
.map(i -> this.sudoku[i][column])
.boxed()
.collect(Collectors.toSet());
Collectors.toSet() cannot be applied to this collect function in the chain
Collectors.toSet() return a Collector which doesn't fit the signature of the single collect(Supplier, ObjIntConsumer, BiConsumer) method in IntStream. Though, it's suitable for Stream.collect(Collector).

Related

Java Cannot convert from String to Int [duplicate]

public static int construction(String myString) {
Set<Character> set = new HashSet<>();
int count = myString.chars() // returns IntStream
.mapToObj(c -> (char)c) // Stream<Character> why is this required?
.mapToInt(c -> (set.add(c) == true ? 1 : 0)) // IntStream
.sum();
return count;
}
The above code will not compile without:
.mapObj(c -> (char)c)
// <Character> Stream<Character> java.util.stream.IntStream.mapToObj(IntFunction<? extends Character> mapper)
If i remove it, I get the following error
The method mapToInt((<no type> c) -> {}) is undefined for the type IntStream
Can someone explain this? It seems like I am starting with and IntStream, converting to a Stream of Characters and then back to IntStream.
The method CharSequence::chars returns the IntStream, which of course doesn't provide any method converting to int, such as mapToInt, but mapToObj instead. Therefore the method IntStream::map(IntUnaryOperator mapper) which both takes returns int as well shall be used since IntUnaryOperator does the same like Function<Integer, Integer> or UnaryOperator<Integer>:
int count = myString.chars() // IntStream
.map(c -> (set.add((char) c) ? 1 : 0)) // IntStream
.sum();
long count = myString.chars() // IntStream
.filter(c -> set.add((char) c)) // IntStream
.count();
Also, using Set<Integer> helps you to avoid conversion to a Character:
Set<Integer> set = new HashSet<>();
int count = myString.chars() // IntStream
.map(c -> (set.add(c) ? 1 : 0)) // IntStream
.sum();
long count = myString.chars() // IntStream
.filter(set::add) // IntStream
.count();
However, regardless of what you try to achieve, your code is wrong by principle. See the Stateless behaviors. Consider using the following snippet which lambda expressions' results are not dependent on the result of a non-deterministic operation, such as Set::add.
Stream pipeline results may be nondeterministic or incorrect if the behavioral parameters to the stream operations are stateful.
long count = myString.chars() // IntStream
.distinct() // IntStream
.count();
You can also collect to a set and then take the size without using an explicit map.
It does not require using external state to contain the characters.
long count = str.chars().boxed().collect(Collectors.toSet()).size();
But imho, the more direct approach which was already mentioned is cleaner in appearance and the one I would prefer to use.
long count = str.chars().distinct().count();
Because String.chars() is already returning an IntStream and IntStream does not have mapToInt function
You could use a filter instead then count:
int count = myString.chars()
.filter(c -> set.add(c) == true)
.count();
I admit that I made this so slubby last midnight!
As mentioned by the comments, here is the required fixes.
Thank you for mentioning.
long count = myString.chars()
.filter(c -> set.add((char)c))
.count();

obtaining unique number from a list of duplicate integers using java 8 streams

I’m trying to obtain a only duplicated numbers list from a list of integers:
final Set<Integer> setOfNmums = new HashSet<>();
Arrays.asList(5,6,7,7,7,6,2,4,2,4).stream()
.peek(integer -> System.out.println("XX -> " + integer))
.filter(n -> !setOfNmums.add(n))
.peek(System.out::println)
.map(String::valueOf)
.sorted()
.collect(Collectors.toList());
The output is 2,4,6,7,7
Expected : 2,4,6,7
I don’t understand how that’s happening.. is this running in parallel? how am I getting two '7'?
The hashset should return false if it exists and that used by the filter?
Yes I can use distinct, but I’m curious to know why would the filter fail.. is it being done in parallel?
Your filter rejects the first occurrence of each element and accepts all subsequent occurrences. Therefore, when an element occurs n times, you’ll add it n-1 times.
Since you want to accept all elements which occur more than once, but only accept them a single time, you could use .filter(n -> !setOfNmums.add(n)) .distinct() or you enhance the set to a map, to be able to accept an element only on its second occurrence.
Map<Integer, Integer> occurrences = new HashMap<>();
List<String> result = Stream.of(5,6,7,7,7,6,2,4,2,4)
.filter(n -> occurrences.merge(n, 1, Integer::sum) == 2)
.map(String::valueOf)
.sorted()
.collect(Collectors.toList());
But generally, using stateful filters with streams is discouraged.
A cleaner solution would be
List<String> result = Stream.of(5,6,7,7,7,6,2,4,2,4)
.collect(Collectors.collectingAndThen(
Collectors.toMap(String::valueOf, x -> true, (a,b) -> false, TreeMap::new),
map -> { map.values().removeIf(b -> b); return new ArrayList<>(map.keySet()); }));
Note that this approach doesn’t count the occurrences but only remembers whether an element is unique or has seen at least a second time. This works by mapping each element to true with the second argument to the toMap collector, x -> true, and resolving multiple occurrences with a merge function of (a,b) -> false. The subsequent map.values().removeIf(b -> b) will remove all unique elements, i.e. those mapped to true.
You can use .distinct() function in your stream check this out.
Since Holger already explained why your solution didn't work, I'll just provide an alternative.
Why not use Collections.frequency(collection, element) together with distinct()?
The solution would be quite simple(i apologize for the formatting, i just copied it from my ide and there doesn't seem to be an autoformat feature in SOF):
List<Integer> numbers = List.of(5, 6, 7, 7, 7, 6, 2, 4, 2, 4);
List<String> onlyDuplicates = numbers.stream()
.filter(n -> Collections.frequency(numbers, n) > 1)
.distinct()
.sorted()
.map(String::valueOf)
.toList();
This simply keeps all elements that occur more than once and then filters out the duplicates before sorting, converting each element to a string and collecting to a list since that seems to be what you want.
if you need a mutable list you can use collect(toCollection(ArrayList::new)) instead of toList()

How extract sorted list of distinct integers from a Set of objects using Java streams?

I have a set of value objects:
Set<EntityKey> clientAssignedPlaceholderEntityKeys
Where the EntityKey class has the following properties:
private Integer objectRef;
private String entityType;
What is the most efficient way to extract the distinct objectRef values into a sorted list using streams?
I have the following but the fact that it calls stream() twice seems like a bad smell:
// Extract into a sorted list all the distinct placeholder objectRefs (regardless of type).
List<Integer> sortedPlaceholderObjectRefs = clientAssignedPlaceholderEntityKeys.stream()
.map(entityKey -> entityKey.getObjectRef())
.collect(Collectors.toSet())
.stream() // having to call stream() a 2nd time here feels sub-optimal
.sorted()
.collect(Collectors.toList());
Maybe:
sortedPlaceholderObjectRefs = clientAssignedPlaceholderEntityKeys.stream()
.map(entityKey -> entityKey.getObjectRef())
.sorted()
.distinct()
.collect(Collectors.toList());
EDIT:
calling .distinct() before .sorted() might be more optimal
clientAssignedPlaceholderEntityKeys.stream()
.map(ek -> ek.getObjectRef())
.sorted()
.distinct()
.collect(Collectors.toList());
Regarding your doubt about the order of sorted() and distinct()
Quoting the very first answer from here:
Chaining distinct() operation after sorted(), the implementation will utilize the sorted nature of the data and avoid building an internal HashSet.
List<Integer> sortedRefs = clientAssignedPlaceholderEntityKeys
.stream()
.map(EntityKey::getObjectRef)
.distinct()
.sorted()
.collect(Collectors.toList());

How do I filter a stream of integers into a list?

I am trying to process a stream of Integers and collect the integers that match a predicate (via the compare() function) into a list. Here's a rough outline of the code I've written.
private List<Integer> process() {
Z z = f(-1);
return IntStream.range(0, 10)
.filter(i -> compare(z, f(i)))
.collect(Collectors.toCollection(ArrayList::new)); // Error on this line
}
private boolean compare(Z z1, Z z2) { ... }
private Z f(int i) { ... }
Unfortunately my solution does not compile and I cannot make sense of the compiler error for the line highlighted:
The method collect(Supplier<R>, ObjIntConsumer<R>, BiConsumer<R,R>) in the type IntStream is not applicable for the arguments (Collector<Object,capture#1-of ?,Collection<Object>>)
Any suggestions?
IntStream doesn't contain a collect method that accepts a single argument of type Collector. Stream does. Therefore you have to convert your IntStream to a Stream of objects.
You can either box the IntStream into a Stream<Integer> or use mapToObj to achieve the same.
For example:
return IntStream.range(0, 10)
.filter(i -> compare(z, f(i)))
.boxed()
.collect(Collectors.toCollection(ArrayList::new));
boxed() will return a Stream consisting of the elements of this stream, each boxed to an Integer.
or
return IntStream.range(0, 10)
.filter(i -> compare(z, f(i)))
.mapToObj(Integer::valueOf)
.collect(Collectors.toCollection(ArrayList::new));
Or you can specify the Supplier, Accumulator and Combiner yourself:
IntStream.range(0, 10)
.filter(i -> compare(z, f(i)))
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);

Return empty element from Java 8 map operation

Using Java 8 stream what is the best way to map a List<Integer> when you have no output for the input Integer ?
Simply return null? But now my output list size will be smaller than my input size...
List<Integer> input = Arrays.asList(0,1,2,3);
List<Integer> output = input.stream()
.map(i -> {
Integer out = crazyFunction(i);
if(out == null || out.equals(0))
return null;
return Optional.of(out);
})
.collect(Collectors.toList());
I don’t get why you (and all answers) make it so complicated. You have a mapping operation and a filtering operation. So the easiest way is to just apply these operation one after another. And unless your method already returns an Optional, there is no need to deal with Optional.
input.stream().map(i -> crazyFunction(i))
.filter(out -> out!=null && !out.equals(0))
.collect(Collectors.toList());
It may be simplified to
input.stream().map(context::crazyFunction)
.filter(out -> out!=null && !out.equals(0))
.collect(Collectors.toList());
But you seem to have a more theoretical question about what kind of List to generate, one with placeholders for absent values or one with a different size than the input list.
The simple answer is: don’t generate a list. A List is not an end in itself so you should consider for what kind of operation you need this list (or its contents) and apply the operation right as the terminal operation of the stream. Then you have your answer as the operation dictates whether absent values should be filtered out or represented by a special value (and what value that has to be).
It might be a different answer for different operations…
Replace the map call with flatMap. The map operation produces one output value per input value, whereas the flatMap operation produces any number of output values per input value -- include zero.
The most straightforward way is probably to replace the check like so:
List<Integer> output = input.stream()
.flatMap(i -> {
Integer out = crazyFunction(i);
if (out == null || out.equals(0))
return Stream.empty();
else
return Stream.of(out);
})
.collect(Collectors.toList());
A further refactoring could change crazyFunction to have it return an Optional (probably OptionalInt). If you call it from map, the result is a Stream<OptionalInt>. Then you need to flatMap that stream to remove the empty optionals:
List<Integer> output = input.stream()
.map(this::crazyFunctionReturningOptionalInt)
.flatMap(o -> o.isPresent() ? Stream.of(o.getAsInt()) : Stream.empty())
.collect(toList());
The result of the flatMap is a Stream<Integer> which boxes up the ints, but this is OK since you're going to send them into a List. If you weren't going to box the int values into a List, you could convert the Stream<OptionalInt> to an IntStream using the following:
flatMapToInt(o -> o.isPresent() ? IntStream.of(o.getAsInt()) : IntStream.empty())
For further discussion of dealing with streams of optionals, see this question and its answers.
Simpler variants of #Martin Magakian 's answer:
List<Integer> input = Arrays.asList(0,1,2,3);
List<Optional<Integer>> output =
input.stream()
.map(i -> crazyFunction(i)) // you can also use a method reference here
.map(Optional::ofNullable) // returns empty optional
// if original value is null
.map(optional -> optional.filter(out -> !out.equals(0))) // return empty optional
// if captured value is zero
.collect(Collectors.toList())
;
List<Integer> outputClean =
output.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList())
;
You can wrap the output into an Optional which may or may not contain a non-null value.
With an output: return Optional.of(out);
Without output: return Optional.<Integer>empty();
You have to wrap into an option because an array cannot contain any null value.
List<Integer> input = Arrays.asList(0,1,2,3);
List<Option<Integer>> output = input.stream()
.map(i -> {
Integer out = crazyFunction(i);
if(out == null || out.equals(0))
return Optional.<Integer>empty();
return Optional.of(out);
})
.collect(Collectors.toList());
This will make sure input.size() == output.size().
Later on you can exclude the empty Optional using:
List<Integer> outputClean = output.stream()
.filter(Optional::isPresent)
.map(i -> {
return i.get();
})
.collect(Collectors.toList());

Categories