Getting intermediate results from stream to be used later in stream - java

I was trying to write some functional programming code (using lambdas and streams from Java 8) to test if a string has unique characters in it (if it does, return true, if it does not, return false). A common way to do this using vanilla Java is with a data structure like a set, i.e.:
public static boolean oldSchoolMethod(String str) {
Set<String> set = new HashSet<>();
for(int i=0; i<str.length(); i++) {
if(!set.add(str.charAt(i) + "")) return false;
}
return true;
}
The set returns true if the character/object can be added to the set (because it did not exist there previously). It returns false if it cannot (it exists in the set already, duplicated value, and cannot be added). This makes it easy to break out the loop and detect if you have a duplicate, without needing to iterate through all length N characters of the string.
I know in Java 8 streams you cannot break out a stream. Is there anyway way to capture the return value of an intermediate stream operation, like adding to the set's return value (true or false) and send that value to the next stage of the pipeline (another intermediate operation or terminal stream operation)? i.e.
Arrays.stream(myInputString.split(""))
.forEach( i -> {
set.add(i) // need to capture whether this returns "true" or "false" and use that value later in
// the pipeline or is this bad/not possible?
});
One of the other ways I thought of solving this problem, is to just use distinct() and collect the results into a new string and if it is the same length as the original string, than you know it is unique, else if there are different lengths, some characters got filtered out for not being distinct, thus you know it is not unique when comparing lengths. The only issue I see here is that you have to iterate through all length N chars of the string, where the "old school" method best-case scenario could be done in almost constant time O(1), since it is breaking out the loop and returning as soon as it finds 1 duplicated character:
public static boolean java8StreamMethod(String str) {
String result = Arrays.stream(str.split(""))
.distinct()
.collect(Collectors.joining());
return result.length() == str.length();
}

Your solutions are all performing unnecessary string operations.
E.g. instead of using a Set<String>, you can use a Set<Character>:
public static boolean betterOldSchoolMethod(String str) {
Set<Character> set = new HashSet<>();
for(int i=0; i<str.length(); i++) {
if(!set.add(str.charAt(i))) return false;
}
return true;
}
But even the boxing from char to Character is avoidable.
public static boolean evenBetterOldSchoolMethod(String str) {
BitSet set = new BitSet();
for(int i=0; i<str.length(); i++) {
if(set.get(str.charAt(i))) return false;
set.set(str.charAt(i));
}
return true;
}
Likewise, for the Stream variant, you can use str.chars() instead of Arrays.stream(str.split("")). Further, you can use count() instead of collecting all elements to a string via collect(Collectors.joining()), just to call length() on it.
Fixing both issues yields the solution:
public static boolean newMethod(String str) {
return str.chars().distinct().count() == str.length();
}
This is simple, but lacks short-circuiting. Further, the performance characteristics of distinct() are implementation-dependent. In OpenJDK, it uses an ordinary HashSet under the hood, rather than BitSet or such alike.

This code might work for you:
public class Test {
public static void main(String[] args) {
String myInputString = "hellowrd";
HashSet<String> set = new HashSet<>();
Optional<String> duplicateChar =Arrays.stream(myInputString.split("")).
filter(num-> !set.add(num)).findFirst();
if(duplicateChar.isPresent()){
System.out.println("Not unique");
}else{
System.out.println("Unique");
}
}
}
Here using findFirst() I am able to find the first duplicate element. So that we don't need to continue on iterating rest of the characters.

What about just mapping to a boolean?
Arrays.stream(myInputString.split(""))
.map(set::add)
.<...>
That would solve your concrete issue, I guess, but it's not a very nice solution because the closures in stream chains should not have side-effects (that is exactly the point of functional programming...).
Sometimes the classic for-loop is still the better choice for certain problems ;-)

Related

Java 8 flatmap unwrap multiple objects from collection

Looking to use some streams in a project and replace some of the logic currently implemented. Not sure if this type of case is a good use case for java stream api. Lets say we have a collection and we want to iterate through it and check values from two objects within the collection, and only if both of them are true will we return a populated optional.
return Arrays.stream(someMultiDimensionalArray).flatMap(objectArray ->
.filter(MyClass.class::isInstance)
.filter(v -> v.value().equals(true))
//need to do something here to do, && (if another value in the collection is true also)
.findFirst();
/
boolean one = false;
for(int i=0; i<objectArray.length; i++){
if(!one && objectArray[i].hidden==true)
one = true;
if(objectArray[i].hidden == true && one)
return objectArray[i];
}
So, what you want is, if there are more than 1 objecst in the array that is having hidden == true, you want the 2nd one? (From what your code means).
So it is simply something like stream.filter(o -> o.hidden).skip(1).findFirst()
as you mentioned in comment above:
If there are two objects which have a true field in the collection then the condition is met.
your if statements code snippet in loop can be merged as:
boolean one = false;
for(int i=0; i<objectArray.length; i++){
if(!one && objectArray[i].hidden) {
one = true;
return objectArray[i];
}
}
Indeed, It can be simplified as further:
for(int i=0; i<objectArray.length; i++){
if(objectArray[i].hidden) {
return objectArray[i];
}
}
Then using for-each loop like this:
for(??? it: objectArray){
if(it.hidden) {
return it;
}
}
AND then using stream-api like this:
return Arrays.stream(objectArray).filter(it->it.hidden).findFirst();
maybe you need cast instance & return a stream in flatMap in the first approach:
return Arrays.stream(someMultiDimensionalArray).flatMap(objectArray ->
Arrays.stream(objectArray).filter(it->it.hidden).findFirst()
.map(Stream::of).orElse(Stream.empty())
)
.filter(MyClass.class::isInstance)
.map(MyClass.class::cast)
.filter(MyClass::value)
.findFirst();

Need help executing a true/false boolean which includes a for-loop and a conditional

Right now I have my boolean setup like this:
public boolean deleteItem(String p) {
for(int i = this.myList.size() - 1; i > -1; i--) {
if(this.myList.get(i) == p) {
this.myList.remove(i);
return true;
} else {
return false;
}
}
}
I'm trying to go through an arraylist and delete string p from the array list.However if string p does exist within the arraylist I need to delete the string and return true. If it does not exist I simply must return false. I'm coding in eclipse right now and it says that the return statements I have right now do not count as the "required" return statement that I need. How can I fix my code so that it has the return statement(s) in the right place?
Why reinvent the wheel?
public boolean deleteItem(String p) {
return this.list.remove(p);
}
You have a pathway through your code that does not return anything. If your list is empty then the loop will not execute and it will fall through to nothing. Also your loop will return as soon as you process your first item with either true or false.
The comment by John is correct.
"Remove the else block and move return false; outside the for loop.
Your function has a number of flaws.
You are doing a reference equality check and not the value equality check. For doing a value equality check, always use the "equals" method. You won't get any compiler errors for this kind of flaws. You will get undesired output.
Not all the control flows in your function has a return statement even though your method signature suggests a non-void return statement. This will produce a compiler error, the one that you are seeing in your code.
You are having multiple return statements. This is neither a compiler error nor a runtime error. However from good programming practice perspective, it's not the best idea to have multiple return statements. You can do a flag based return statement at the very end.
You are removing elements from a collection object while iterating through it. Depending on the type of the collection object you are using, it may throw a ConcurrentModificationException exception at run time. You need to use a fail-safe iterator instead if available.
I have tried to correct your program. See if this makes better sense:
public boolean deleteItem(String p) {
boolean itemFound = false;
//Assuming your myList object returns a fail safe iterator.
//If it returns a fail fast iterator instead, see the next option.
Iterator<String> iter = this.myList.iterator();
while(iter.hasNext()){
if(iter.next().equals(p)) {
iter.remove();
itemFound=true;
}
}
return itemFound;
}
The above program will work if the iterator is fail-safe. E.g. if your myList object is of type CopyOnWriteArrayList, its iterator will be fail safe. But if your myList object is of a type such a plain ArrayList, that returns a fail fast iterator, the above method will give you a CME.
If your myList collection object is of type List, you can try something as easy as:
public boolean deleteItem(String p) {
//removeAll will return true if at least 1 element is removed
return this.myList.removeAll(Collections.singletonList(p));
}
Alternately, if you are using Java 8, you can do something like following:
public boolean deleteItem(String p) {
//removeIf will return true if at least 1 element is removed
return this.myList.removeIf(item -> item != null && item.equals(p));
}
Hope this helps you a bit.

Finite generated Stream in Java - how to create one?

In Java, one can easily generate an infinite stream with Stream.generate(supplier). However, I would need to generate a stream that will eventually finish.
Imagine, for example, I want a stream of all files in a directory. The number of files can be huge, therefore I can not gather all the data upfront and create a stream from them (via collection.stream()). I need to generate the sequence piece by piece. But the stream will obviously finish at some point, and terminal operators like (collect() or findAny()) need to work on it, so Stream.generate(supplier) is not suitable here.
Is there any reasonable easy way to do this in Java, without implementing the entire Stream interface on my own?
I can think of a simple hack - doing it with infinite Stream.generate(supplier), and providing null or throwing an exception when all the actual values are taken. But it would break the standard stream operators, I could use it only with my own operators that are aware of this behaviour.
CLARIFICATION
People in the comments are proposing me takeWhile() operator. This is not what I meant. How to phrase the question better... I am not asking how to filter (or limit) an existing stream, I am asking how to create (generate) the stream - dynamically, without loading all the elements upfront, but the stream would have a finite size (unknown in advance).
SOLUTION
The code I was looking for is
Iterator it = myCustomIteratorThatGeneratesTheSequence();
StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, Spliterator.DISTINCT), false);
I just looked into java.nio.file.Files, how the list(path) method is implemented.
Is there any reasonable easy way to do this in Java, without implementing the entire Stream interface on my own?
A simple .limit() guarantees that it will terminate. But that's not always powerful enough.
After the Stream factory methods the simplest approach for creating customs stream sources without reimplementing the stream processing pipeline is subclassing java.util.Spliterators.AbstractSpliterator<T> and passing it to java.util.stream.StreamSupport.stream(Supplier<? extends Spliterator<T>>, int, boolean)
If you're intending to use parallel streams note that AbstractSpliterator only yields suboptimal splitting. If you have more control over your source fully implementing the Spliterator interface can better.
For example, the following snippet would create a Stream providing an infinite sequence 1,2,3...
in that particular example you could use IntStream.range()
But the stream will obviously finish at some point, and terminal operators like (collect() or findAny()) need to work on it.
short-circuiting operations like findAny() can actually finish on an infinite stream, as long as there is any element that matches.
Java 9 introduces Stream.iterate to generate finite streams for some simple cases.
Kotlin code to create Stream of JsonNode from InputStream
private fun InputStream.toJsonNodeStream(): Stream<JsonNode> {
return StreamSupport.stream(
Spliterators.spliteratorUnknownSize(this.toJsonNodeIterator(), Spliterator.ORDERED),
false
)
}
private fun InputStream.toJsonNodeIterator(): Iterator<JsonNode> {
val jsonParser = objectMapper.factory.createParser(this)
return object: Iterator<JsonNode> {
override fun hasNext(): Boolean {
var token = jsonParser.nextToken()
while (token != null) {
if (token == JsonToken.START_OBJECT) {
return true
}
token = jsonParser.nextToken()
}
return false
}
override fun next(): JsonNode {
return jsonParser.readValueAsTree()
}
}
}
Here is a stream which is custom and finite :
package org.tom.stream;
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
public class GoldenStreams {
private static final String IDENTITY = "";
public static void main(String[] args) {
Stream<String> stream = java.util.stream.StreamSupport.stream(new Spliterator<String>() {
private static final int LIMIT = 25;
private int integer = Integer.MAX_VALUE;
{
integer = 0;
}
#Override
public int characteristics() {
return Spliterator.DISTINCT;
}
#Override
public long estimateSize() {
return LIMIT-integer;
}
#Override
public boolean tryAdvance(Consumer<? super String> arg0) {
arg0.accept(IDENTITY+integer++);
return integer < 25;
}
#Override
public Spliterator<String> trySplit() {
System.out.println("trySplit");
return null;
}}, false);
List<String> peeks = new ArrayList<String>();
List<String> reds = new ArrayList<String>();
stream.peek(data->{
peeks.add(data);
}).filter(data-> {
return Integer.parseInt(data)%2>0;
}).peek(data ->{
System.out.println("peekDeux:"+data);
}).reduce(IDENTITY,(accumulation,input)->{
reds.add(input);
String concat = accumulation + ( accumulation.isEmpty() ? IDENTITY : ":") + input;
System.out.println("reduce:"+concat);
return concat;
});
System.out.println("Peeks:"+peeks.toString());
System.out.println("Reduction:"+reds.toString());
}
}
While the author has discarded the takeWhile option, I find it adequate for certain use cases and worth an explanation.
The method takeWhile can be used on any stream and will terminate the stream when the predicate provided to the method returns false. The object which results in a false is not appended to the stream; only the objects which resulted in true are passed downstream.
So one method for generating a finite stream could be to use the Stream.generate method and return a value which signals the end of the stream by being evaluated to false by the predicate provided to takeWhile.
Here's an example, generating all the permutations of an array :
public static Stream<int[]> permutations(int[] original) {
int dim = original.length;
var permutation = original.clone();
int[] controller = new int[dim];
var low = new AtomicInteger(0);
var up = new AtomicInteger(1);
var permutationsStream = Stream.generate(() -> {
while (up.get() < dim) {
if (controller[up.get()] < up.get()) {
low.set(up.get() % 2 * controller[up.get()]);
var tmp = permutation[low.get()];
permutation[low.get()] = permutation[up.get()];
permutation[up.get()] = tmp;
controller[up.get()]++;
up.set(1);
return permutation.clone();
} else {
controller[up.get()] = 0;
up.incrementAndGet();
}
}
return null;
}).takeWhile(Objects::nonNull);
return Stream.concat(
Stream.ofNullable(original.clone()),
permutationsStream
);
}
In this example, I used the null value to signal the end of the stream.
The caller of the method won't receive the null value !
OP could use a similar strategy, and combine it with a visitor pattern.
If it's a flat directory, OP would be better off using Stream.iterate with the seed being the index of the file to yield and Stream.limit on the number of files (which can be known without browsing the directory).

java startsWith() method with custom rules

I implement typing trainer and would like to create my special String startsWith() method with specific rules.
For example: '-' char should be equal to any long hyphen ('‒', etc). Also I'll add other rules for special accent characters (e equals é, but not é equals e).
public class TestCustomStartsWith {
private static Map<Character, List<Character>> identityMap = new HashMap<>();
static { // different hyphens: ‒, –, —, ―
List<Character> list = new LinkedList<>();
list.add('‒');
list.add('–'); // etc
identityMap.put('-', list);
}
public static void main(String[] args) {
System.out.println(startsWith("‒d--", "-"));
}
public static boolean startsWith(String s, String prefix) {
if (s.startsWith(prefix)) return true;
if (prefix.length() > s.length()) return false;
int i = prefix.length();
while (--i >= 0) {
if (prefix.charAt(i) != s.charAt(i)) {
List<Character> list = identityMap.get(prefix.charAt(i));
if ((list == null) || (!list.contains(s.charAt(i)))) return false;
}
}
return true;
}
}
I could just replace all kinds of long hyphens with '-' char, but if there will be more rules, I'm afraid replacing will be too slow.
How can I improve this algorithm?
I don't know all of your custom rules, but would a regular expression work?
The user is passing in a String. Create a method to convert that String to a regex, e.g.
replace a short hyphen with short or long ([-‒]),
same for your accents, e becomes [eé]
Prepend with the start of word dohicky (\b),
Then convert this to a regex and give it a go.
Note that the list of replacements could be kept in a Map as suggested by Tobbias. Your code could be something like
public boolean myStartsWith(String testString, String startsWith) {
for (Map.Entry<String,String> me : fancyTransformMap) {
startsWith = startsWith.replaceAll(me.getKey(), me.getValue());
}
return testString.matches('\b' + startsWith);
}
p.s. I'm not a regex super-guru so if there may be possible improvements.
I'd think something like a HashMap that maps the undesirable characters to what you want them to be interpreted as might be the way to go if you are worried about performance;
HashMap<Character, Character> fastMap = new Map<Character, Character>();
// read it as '<long hyphen> can be interpreted as <regular-hyphen>
fastMap.add('–', '-');
fastMap.add('é', 'e');
fastMap.add('è', 'e');
fastMap.add('?', '?');
...
// and so on
That way you could ask for the value of the key: value = map.get(key).
However, this will only work as long as you have unique key-values. The caveat is that é can't be interpreted as è with this method - all the keys must be unique. However, if you are worried about performance, this is an exceedingly fast way of doing it, since the lookup time for a HashMap is pretty close to being O(1). But as others on this page has written, premature optimization is often a bad idea - try implementing something that works first, and if at the end of it you find it is too slow, then optimize.

ArrayItem equals atleast one term in another array

I want to check to see if two arrays share at least one term in common for my program.
I'm not quite sure what the code is to compare two arrays, but here is what I have so far;
if ((modWikiKeyArray).equals(inputArray[0]))
{
StringBuilder hyperlinkBuilder = new StringBuilder();
for(int i = 0; i < modWikiKeyArray.length; i++)
{
hyperlinkBuilder.append(modWikiKeyArray[i]);
}
}
How would I compare the array modWikiKeyArray to inputArray just to check and see if inputArray[0] is equal to any term inside of modWikiKeyArray?
Arrays.asList lets you build a list backed by an arbitrary array and use convenient Java Collections Framework features like the contains method:
Arrays.asList(oneArray).contains(elementFromAnotherArray)
If you want to see if the arrays have at least one element in common, you could build a HashSet out of one and loop over the other to try to find a common element:
boolean arraysIntersect(Object[] array1, Object[] array2) {
Set array1AsSet = HashSet(Arrays.asList(array1));
for (Object o : array2) {
if (array1AsSet.contains(o)) {
return true;
}
}
return false;
}
You can do the following
for(int i=0;i<modWikiKeyArray.length;i++) {
if(modWikiKeyArray[i].equals(inputArray[0])) {
System.out.println("Match found");
}
}
Note you need to override the equals() method of whatever array you are creating(Class of which array you are creating) .
Going by your code snippet, it looks like you need to check the presence of inputArray[0] only, in which case the following is sufficient:
boolean exists = java.util.Arrays.asList(modWikiKeyArray).contains(inputArray[0]);
Alternatively, you might also want to use ArrayUtils from Apache commons-lang:
boolean exists = ArrayUtils.contains(modWikiKeyArray, inputArray[0]);
However, if I read the text of your question, it seems you want to find if modWikiKeyArray contains at least one item from inputArray. For this you may also use retainAll from the Collections API to perform a list intersecion and see if the intersection list is non-empty.
However, the most primitive is still Aniket's method. However, I will modify it to reduce unnecessary operations:
int i = modWikiKeyArray.length - 1;
MyObject inputElement = inputArray[0];
boolean found = false;
for(; i != 0; i--) {
if(modWikiKeyArray[i].equals(inputElement)) {
found = true;
break;
}
}

Categories

Resources