1

This is how my code looks :

public static String firstContainsChars(Stream<String> items,String charset) {
    char[] inChars = charset.toCharArray();
    for (char ch:inChars) {
        items.filter(s->s.contains(String.valueOf(ch)));
    }
    return items.findFirst().orElse("No Match Found");
}

In fact, I am getting a collection of chars as a String and filter the stream but the problem is I can't use the stream several times in the foreach loop and compiler says :

Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed

Any ideas on how to duplicate the Stream without the need to collect it as a Collection as it will be total waste. (streaming and collecting after the chnage)?

Alexis C.
  • 91,686
  • 21
  • 171
  • 177
  • 2
    You can pass a `Supplier>` to your function. – Flown Jul 27 '16 at 11:59
  • @Flown, Thanks, A very simple code snippet on how to use it will be appreciated as I am a total newbie and don't know what to search in google. –  Jul 27 '16 at 12:01
  • A `Stream` has no memory, the only alternative to using a Collection which does have a memory is to re-evaluate the Stream each time. – Peter Lawrey Jul 27 '16 at 12:01
  • 2
    To make your code work you should change the line in the loop to: `items = items.filter(s -> s.contains(String.valueOf(ch)));` – Flown Jul 27 '16 at 12:05
  • No, finding all strings who contain all of the charsets. For example for filtering out all items who have `a`,`b`,`c`, We should pass "abc" as the second argument. –  Jul 27 '16 at 12:06
  • @Flown, Thanks, It worked altogether! –  Jul 27 '16 at 12:07
  • See also http://stackoverflow.com/questions/23860533/copy-a-stream-to-avoid-stream-has-already-been-operated-upon-or-closed-java-8 – Tunaki Jul 27 '16 at 12:30

3 Answers3

3

As I said in the comment section you have to assign items to the new pipeline in the loop.

public static String firstContainsChars(Stream<String> items, String charset) {
  char[] inChars = charset.toCharArray();
  for (char ch : inChars) {
    items = items.filter(s -> s.contains(String.valueOf(ch)));
  }
  return items.findFirst().orElse("No Match Found");
}

But a much cleaner approach would be to Stream your charset too, like:

public static String firstContainsChars(Stream<String> items, String charset) {
  return items.filter(s -> charset.codePoints().allMatch(cp -> s.indexOf(cp) != -1))
              .findFirst().orElse("No Match Found");
}
Flown
  • 11,480
  • 3
  • 45
  • 62
1

You don't actually need to duplicate the stream, just stream the charset chars inside your filter :

public static String firstContainsChars(Stream<String> items, String charset){

    return items
            .filter(s-> charset
                        .chars() // stream of all the chars in charset
                        .mapToObj(i -> (char)i ) //chars() is actually a stream of ints... convert to chars
                        .allMatch(c -> s.indexOf(c) >= 0) //check that all chars are contained in the string
            )
            .findFirst()
            .orElse("No Match Found");
}

List<String> items = Arrays.asList("foo", "bar", "foobar");

String s1 = firstContainsChars(items.stream(), "of");  // -> "foo"
String s2 = firstContainsChars(items.stream(), "rab"); // -> "bar"
String s3 = firstContainsChars(items.stream(), "fob"); // -> "foobar"
String s4 = firstContainsChars(items.stream(), "xyz"); // -> "No Match Found"
Pierre Henry
  • 16,658
  • 22
  • 85
  • 105
  • good solution but if you can avoid multiple lines inside `{}` in lambda will be much cleaner solution – bananas Jul 27 '16 at 12:25
  • @ehsan : basically the same as the second option from Flown's answer. You will get used to "think in streams" as you start using them more ! – Pierre Henry Jul 27 '16 at 12:27
  • 1
    @AsteriskNinja : You are right, I removed the {} and return statment. However I think it is much more readable if you spread it on several lines – Pierre Henry Jul 27 '16 at 12:31
0

I will not enter in goal of you efforts, but say that you code will doing nothing, because filter is just intermediate operation on stream, it is not terminate operation like findFirst.
Now, you can rewrite you code to see how it works like :

public static String firstContainsChars(Stream<String> items,String charset) {
    String result = "";
    char[] inChars = charset.toCharArray();
    for (char ch:inChars) {
      result = items.filter(s->s.contains(String.valueOf(ch))).findFirst().orElse("No Match Found");
      System.out.println(result);
    }
    return result;
}

But please, read documentation for understanding operations on stream and lifecycle of its https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps

Yan Pak
  • 1,767
  • 2
  • 19
  • 15