Understanding reduction operation of accumulator - java 8 - java

I m trying to understand reduction accumulator operation: In the below example
List<String> letters = Arrays.asList("a","bb","ccc");
String result123 = letters
.stream()
.reduce((partialString, element) ->
partialString.length() < element.length()
? partialString
: element
).get();
System.out.println(result123);
Is partialString initialized to empty string? Since its a fold operation, I assume that operation should result empty string but its printing "a". Can someone please explain how this accumulator works?

The for-loop corresponding code for the reduce operation is like
boolean seen = false;
String acc = null;
for (String letter : letters) {
if (!seen) {
seen = true;
acc = letter;
} else {
acc = acc.length() < letter.length() ? acc : letter;
}
}
The first pair of element to reduce are (firstElt, secondElt) , there is no empty initial element
If you print each step of the reduce
letters.stream()
.reduce((partialString, element) -> {
System.out.println(partialString + " " + element);
return partialString.length() < element.length() ? partialString : element;
}).get();
// output
a bb
a ccc

If you read the documentation, i.e. the javadoc of reduce(), you will learn that partialString is initialized to the first value, and reduce() is only called to combine values, aka to reduce them.
Is partialString initialized to empty string?
No. If you wanted that, you need to use the other reduce() method, so you wouldn't need to call get():
String result123 = letters
.stream()
.reduce("", (partialString, element) ->
partialString.length() < element.length()
? partialString
: element
);
Of course, that doesn't make any sense, because partialString is now always a string with length() = 0, so result of expression is always an empty string. You might as well just write String result123 = ""; and save all the CPU time.

Related

Finding the first Non-repeating Character in the given string, not able to pass a few test cases due to Timeout

I'm working on a Problem from CodeSignal:
Given a String s consisting of the alphabet only, return the first
non-repeated element. Otherwise, return '-'.
Example: input -
s="abacabad", output - 'c'.
I came up with the following the code. It passes only 16/19 test cases. Is there a way to solve this problem in O(n) or O(1)?
My code:
public char solution(String s) {
ArrayList<Character> hs = new ArrayList<>();
for (char c:s.toCharArray()) {
hs.add(c);
}
for (int j=0; j<s.length(); j++) {
if ( 1 == Collections.frequency(hs, s.charAt(j))) {
return s.charAt(j);
}
}
return '_';
}
The minimal possible time complexity for this task is linear O(n), because we need to examine every character in the given string to find out whether a particular character is unique.
Your current solution runs in O(n^2) - Collections.frequency() iterates over all characters in the string and this iteration and this method is called for every character. That's basically a brute-force implementation.
We can generate a map Map<Character,Boolean>, which associates each character with a boolean value denoting whether it's repeated or not.
That would allow to avoid iterating over the given string multiple times.
Then we need to iterate over the key-set to find the first non-repeated character. As the Map implementation LinkedHashMap is used to ensure that returned non-repeated character would be the first encountered in the given string.
To update the Map I've used Java 8 method merge(), which expects three arguments: a key, a value, and a function responsible for merging the old value and the new one.
public char solution(String s) {
Map<Character, Boolean> isNonRepeated = getMap(s);
for (Map.Entry<Character, Boolean> entry: isNonRepeated.entrySet()) {
if (entry.getValue()) {
return entry.getKey();
}
}
return '_';
}
public Map<Character, Boolean> getMap(String s) {
Map<Character, Boolean> isNonRepeated = new LinkedHashMap<>();
for (int i = 0; i < s.length(); i++) {
isNonRepeated.merge(s.charAt(i), true, (v1, v2) -> false);
}
return isNonRepeated;
}
In case if you're comfortable with streams, this problem can be addressed in one statement (the algorithm remains the same and time complexity would be linear as well):
public char solution(String s) {
return s.chars()
.mapToObj(c -> (char) c)
.collect(Collectors.toMap( // creates intermediate Map<Character, Boolean>
Function.identity(), // key
c -> true, // value - first occurrence, character is considered to be non-repeated
(v1, v2) -> false, // resolving values, character is proved to be a duplicate
LinkedHashMap::new
))
.entrySet().stream()
.filter(Map.Entry::getValue)
.findFirst()
.map(Map.Entry::getKey)
.orElse('_');
}
Here is a slightly different approach using both a Set to account for duplicates, and a Queue to hold candidates before a possible duplicate is discovered.
iterate over the list of characters.
try and add the character to the seen set. If not already there,
also add it to the candidates queue.
else if it has been "seen", try and remove it from the candidates queue.
By the time this gets done, the head of the queue should contain the first, non-repeating character. If the queue is empty, return the default value as no unique character was found.
public char solution(String s) {
Queue<Character> candidates = new LinkedList<>();
Set<Character> seen = new HashSet<>();
for (char c : s.toCharArray()) {
if (seen.add(c)) {
candidates.add(c);
} else {
candidates.remove(c);
}
}
return candidates.isEmpty() ? '_' : candidates.peek();
}
I have done pretty extensive testing of this and it has yet to fail. It is also comparatively very efficient. But as can happen, I may have overlooked something.
One technique would be a 2 pass solution using a frequency/count array for each character.
public static char firstNonRepeatingChar(String s) {
int[] frequency = new int[26]; // this is O(1) space complexity because alphabet is finite of 26 letters
/* First Pass - Fill our frequency array */
for(int i = 0; i < s.length(); i++) {
frequency[s.charAt(i) - 'a']++;
}
/* Second Pass - Look up our frequency array */
for(int i = 0; i < s.length(); i++) {
if(frequency[s.charAt(i) - 'a'] == 1) {
return s.charAt(i);
}
}
/* Not Found */
return '_';
}
This solution is O(2n) -> O(n) and a space complexity of O(1) because we are using a finite set of the English alphabet (26 letters). This wouldn't work in other scenarios for non-English alphabets.

Java HashMap performing put() and get() in same line

I am using a HashMap<String, Integer> to keep track of count of an occurrence of a specific string. I am performing this operation in a single-thread manner in the following way:
HashMap<String, Integer> count = new HashMap<>();
// List<Stringā‰„ words = ...;
for (String word : words) {
if (!count.containsKey(word)) {
count.put(word, 0);
}
count.put(word, count.get(word) + 1);
}
Is it possible, for the same word, the count increases by more than 1 because I am performing a put and get on the same key at the same time? i.e. Let's say the word = "hello". Initially, count.get(word) => 1. When I perform count.put(word, count.get(word) + 1), if I do count.get(word), instead of getting 2, I get 3.
To answer your questions directly: no it is not possible for the statement count.put(word, count.get(word) + 1) to increment the value by more than 1. Although the two method calls are in the same statement they are performed sequentially: the get is performed first to find the second argument to pass to the put.
You can combine your missing key test and initialisation into a single statement:
count.putIfAbsent(word, 0);
This conveniently returns the value afterwards, allowing:
count.put(word, 1 + count.putIfAbsent(word, 0));
However there is also a method that already combines those two operations:
count.merge(word, 1, Integer::sum);
Map has methods compute and merge that would allow to implement shorter updates of the values for the keys:
compute
for (String word : words) {
count.compute(word, (w, prev) -> prev == null ? 1 : prev + 1);
}
merge
for (String word : words) {
count.merge(word, 1, (prev, one) -> prev + one);
}
Lambda expression (prev, one) -> prev + one is actually a function of two int arguments returning their sum, therefore it may be replaced with a method reference Integer::sum:
for (String word : words) {
count.merge(word, 1, Integer::sum);
}
It absolutely safe to do it in a single thread.
No, it's not possible that "count increases by more than 1 because I am performing a put and get on the same key at the same time" because two operations never can happen at the same time with single-threaded execution.
Code count.put(word, count.get(word) + 1); will execute commands in following order:
Integer value1 = count.get(word);
int value2 = value1.intValue();
int value3 = value2 + 1;
Integer value4 = new Integer(value3);
count.put(word, value4);
By the way, your code will produce quite a lot of garbage and will be not very effective.
This way is more effective:
private static class CounterHolder{
int value;
}
Map<String, CounterHolder> count = new HashMap<>();
List<String> words = ...
for (String word : words) {
CounterHolder holder;
if (count.containsKey(word)) {
holder = new CounterHolder();
} else {
holder = new CounterHolder();
count.put(word, holder);
}
++holder.value;
}

How to compare and operate two adjacent elements in one list using stream in Java?

The background is I have two String type variables str1 and str2 as inputs. At last I have to return a list that contains the consecutive prefix of str1 that smaller than the related prefix in str2.
I have the Java code like this:
public List<Character> getPrefix(String str1, String str2) {
int index = 0;
List<Character> res = new ArrayList<>();
//str1 = "1243"
//str2 = "2324"
// The answer will be "12".
while (index < str1.length() && index < str2.length() && str1.charAt(index) <= str2.charAt(index)) {
res.add(str1.charAt(index));
index++;
}
return res;
}
//the return type could either be List<String> or List<Character>
I was asked to convert this code in stream without using while or for loop, just in stream method. I plan to convert this code like this
List<String> list = new ArrayList<>();
list.add(str1);
list.add(str2);
List<String> res = list.stream()
.filter()
.reduce();
I found filter() method could select the element that match the given predicate, and reduce() method could use identity and accumulator to get one final result.
But I find I could neither have a way to operate two adjacent elements in one list, or get one pointer to compare and traverse each character in each element in the list(the element is String type).
So is there any ways that I could operate two adjacent elements in one list, so that I can compare their characters which in the same position.
You can:
Generate a stream of indexes
Get characters of both strings using the index
Select chars while valid
// The magic
public static List<Character> getPrefix(String str1, String str2) {
return IntStream
.range(0, Math.min(str1.length(), str2.length()))
.mapToObj(i -> new char[] { str1.charAt(i), str2.charAt(i) })
.takeWhile(a -> a[0] < a[1])
.map(a -> a[0])
.collect(Collectors.toList());
}
Look at the code and maybe that is what you want.
Still it could be enhanced more and do not solve the case when first string start with a value greater than second. This can be implemented also but need additional work.
(could not do in one piece because the supplier consume one element for check needed to chain dropWhile and takeWhile).
Simply, with a supplier you can do comparison between elements from the stream with elements from other data-structure.
import java.util.LinkedList;
import java.util.function.Supplier;
import java.util.stream.Collectors;
public class Pre1 {
public static void main(String[] args) {
System.out.println(new Pre1().getPre("1234", "2315"));
System.out.println(new Pre1().getPre("941234", "712315"));
System.out.println(new Pre1().getPre("2345", "341"));
}
public String getPre(String s1, String s2) {
//second list is used as supplier
LinkedList<Integer> l2 = s2.chars().boxed()
.map(t->Character.getNumericValue(t))
.collect(Collectors.toCollection(LinkedList<Integer>::new));
//l2.forEach(System.out::println);
Supplier<Integer> supplier = () -> {
// System.out.println(l2.peek());
return l2.isEmpty() ? 0 : l2.pollFirst();
};
return s1.chars().boxed()
.map(t->Character.getNumericValue(t))
.takeWhile(t->t<supplier.get())
.map(t->String.valueOf(t))
.collect(Collectors.joining());
}
}
Output
12
nothing
23

Kotlin Destructuring when/if statement

So I have a String i would want to check if I should split into two, or return some default value. Like this:
val myString = "firstPart-secondPart"
val (first, second) = when (myString.contains("-")) {
true -> myString.split('-', limit = 2)
else -> ?? <-- How would i return ("Default1", "Default2") so the destructuring still works?
}
So my question is, how do i return two default strings, so that the deconstructing works? I've used String.split() before in order to deconstruct and it's really nice.
How to return 2 values for destructuring
You need to return a type matching the above type, split returns a list, so you could use this:
listOf("Default1", "Default2")
Full code
val myString = "firstPart-secondPart"
val (first, second) = when (myString.contains("-")) {
true -> myString.split('-', limit = 2)
else -> listOf("Default1", "Default2")
}
Why this works
As both branches return List<String> you can treat the whole when block as a List<String>, so it can be destructured to get the values from it.
Possible cleanup
val myString = "firstPart-secondPart"
val (first, second) = when {
myString.contains("-") -> myString.split('-', limit = 2)
else -> listOf("Default1", "Default2")
}
This may make more sense, assuming you are going to add more conditions, otherwise an if may make more sense.
As an alternative to the good and correct answer of jrtapsell, you could use destructured Pairs:
val (first, second) = when (myString.contains("-")) {
true -> myString.split('-', limit = 2).let { it[0] to it[1] }
else -> "Default1" to "Default2"
}
Note 1: The resulting list with two elements is transformed to a Pair with the help of let.
Note 2: The infix function to is used to create Pairs here.

How should I check whether a Stream<T> is sorted?

With an Iterable<T>, it's easy:
T last = null;
for (T t : iterable) {
if (last != null && last.compareTo(t) > 0) {
return false;
}
last = t;
}
return true;
But I can't think of a clean way to do the same thing for a Stream<T> that avoids consuming all the elements when it doesn't have to.
There are several methods to iterate over the successive pairs of the stream. For example, you can check this question. Of course my favourite method is to use the library I wrote:
boolean unsorted = StreamEx.of(sourceStream)
.pairMap((a, b) -> a.compareTo(b) > 0)
.has(true);
It's short-circuit operation: it will finish as soon as it find the misorder. Also it works fine with parallel streams.
This is a sequential, state holding solution:
IntStream stream = IntStream.of(3, 3, 5, 6, 6, 9, 10);
final AtomicInteger max = new AtomicInteger(Integer.MIN_VALUE);
boolean sorted = stream.allMatch(n -> n >= max.getAndSet(n));
Parallelizing would need to introduce ranges. The state, max might be dealt with otherwise, but the above seems most simple.
You can grab the Stream's underlying spliterator and check it it has the SORTED characteristic. Since it's a terminal operation, you can't use the Stream after (but you can create another one from this spliterator, see also Convert Iterable to Stream using Java 8 JDK).
For example:
Stream<Integer> st = Stream.of(1, 2, 3);
//false
boolean isSorted = st.spliterator().hasCharacteristics(Spliterator.SORTED);
Stream<Integer> st = Stream.of(1, 2, 3).sorted();
//true
boolean isSorted = st.spliterator().hasCharacteristics(Spliterator.SORTED);
My example shows that the SORTED characteristic appears only if you get the Stream from a source's that reports the SORTED characteristic or you call sorted() at a point on the pipeline.
One could argue that Stream.iterate(0, x -> x + 1); creates a SORTED stream, but there is no knowledge about the semantic of the function applied iteratively. The same applies for Stream.of(...).
If the pipeline is infinite then it's the only way to know. If not, and that the spliterator does not report this characteristic, you'd need to go through the elements and see if it does not satisfy the sorted characteristic you are looking for.
This is what you already done with your iterator approach but then you need to consume some elements of the Stream (in the worst case, all elements). You can make the task parallelizable with some extra code, then it's up to you to see if it's worth it or not...
You could hijack a reduction operation to save the last value and compare it to the current value and throw an exception if it isn't sorted:
.stream().reduce((last, curr) -> {
if (((Comparable)curr).compareTo(last) < 0) {
throw new Exception();
}
return curr;
});
EDIT: I forked another answer's example and replaced it with my code to show it only does the requisite number of checks.
http://ideone.com/ZMGnVW
You could use allMatch with a multi-line lambda, checking the current value against the previous one. You'll have to wrap the last value into an array, though, so the lambda can modify it.
// infinite stream with one pair of unsorted numbers
IntStream s = IntStream.iterate(0, x -> x != 1000 ? x + 2 : x - 1);
// terminates as soon as the first unsorted pair is found
int[] last = {Integer.MIN_VALUE};
boolean sorted = s.allMatch(x -> {
boolean b = x >= last[0]; last[0] = x; return b;
});
Alternatively, just get the iterator from the stream and use a simple loop.
A naive solution uses the stream's Iterator:
public static <T extends Comparable<T>> boolean isSorted(Stream<T> stream) {
Iterator<T> i = stream.iterator();
if(!i.hasNext()) return true;
T current = i.next();
while(i.hasNext()) {
T next = i.next();
if(current == null || current.compareTo(next) > 0) return false;
current = next;
}
return true;
}
Edit: It would also be possible to use a spliterator to parallelize the task, but the gains would be questionable and the increase in complexity is probably not worth it.
I don't know how good it is , but i have just got an idea:
Make a list out of your Stream , Integer or Strings or anything.
i have written this for a List<String> listOfStream:
long countSorted = IntStream.range(1, listOfStream.size())
.map(
index -> {
if (listOfStream.get(index).compareTo(listOfStream.get(index-1)) > 0) {
return 0;
}
return index;
})
.sum();

Categories