I am getting unexpected behaviour when using Java Streams and Scanners

Jaquarh :

I recently saw a topic of some Uni coursework which was being conducted by a friend whom was instructed to do it a certain way. I thought I'd take the opportunity to jump in on the task.

I created a Book class like so:

class Book
{
    private String author, title;

    public Book setAuthor(String a)
    {
        author = a;
        return this;
    }

    public Book setTitle(String t)
    {
        title = t;
        return this;
    }

    public String getAuthor() 
    {
        return author;
    }

    public String getTitle()
    {
        return title;
    }
}

The concept is that a user can create multiple books at the start of the program and then search for an author:

private final static int BOOK_NO = 3;
private final static SO instance = new SO(); // This is whatever you called the class

public static void main(String[] args)
{
    Book[] books = new Book[BOOK_NO];
    Scanner kybd = new Scanner(System.in);

    for(int i = 0; i < BOOK_NO; i++)
    {
        books[i] = instance.addBook(kybd, new Book());
    }

    Arrays.stream(instance.findBook(kybd, books)).forEach(o -> {
        System.out.println(o.getTitle() + " by " + o.getAuthor());
    });
}

public Book addBook(Scanner s, Book b)
{

    System.out.println("Enter the Author of this book:");
    b.setAuthor(s.next());

    System.out.println("Enter the Title of this book:");
    b.setTitle(s.next());

    return b;
}

public Book[] findBook(Scanner s, Book[] bs)
{
    System.out.println("Search a book by author:");

    List<Book> finding = Arrays .stream(bs)
            .filter(o -> o.getAuthor().equalsIgnoreCase(s.next()))
            .collect(Collectors.toList());

    System.out.println("Found " + finding.size() + " matches.");

    Book[] output = new Book[finding.size()];
    output = finding.toArray(output);
    return output;
}

Now the whole program works fine, however I am experience unexpected behaviour with the Scanner when it comes to searching for a book. Here is a direct input/output behaviour I am experiencing:

Enter the Author of this book:
Foo
Enter the Title of this book:
Bar
Enter the Author of this book:
Foo
Enter the Title of this book:
FooBar
Enter the Author of this book:
Bar
Enter the Title of this book:
Foo
Search a book by author:
Foo
Foo
Foo
Found 2 matches.
Bar by Foo
FooBar by Foo

As you can see, I am having to type the author of the book into the scanner 3 times before getting any result. How can I mitigate this? What is causing this to happen?

GBlodgett :

This is because in your Stream you call next(), so for every Book object in the Stream, the Predicate in the call to filter is applied to it, and next() will be called. Resolve it to a variable so it isn't called more than once:

String book = s.next();
List<Book> finding = Arrays.stream(bs)
                           .filter(o -> o.getAuthor().equalsIgnoreCase(book))
                           .collect(Collectors.toList());

filter() accepts a Predicate, which in this case will be something like:

Predicate<String> pred = str -> str.equalsIgnoreCase(s.next());

So every time it is applied, next() will be called

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=80806&siteId=1