Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function interface as a lambda to sort a collection?

I am studying lambda expressions and I am struggling with on how to use the java.util.function.Function to sort a collection. Could someone help me or at give me some pointers on how to achieve this?

I have a book POJO and a class that stores books in a collection. I am trying to use a lambda expression of the Function interface to return the same collection but sorted. I could use Collections.sort() and return it that way but I was thinking there was a way to do it with the Function interface.

public class BookTable {

  private Map<Integer, Book> bookMap;

  public BookTable() {
      this.bookMap = new HashMap<>();
  }

  public void addBook(Book book) {
     bookMap.put(size(), book);
  }

  public int size() {
      return bookMap.size();
  }

  public List<Book> getRecord(int key) {
      return Collections.singletonList(bookMap.get(key));
  }

  public List<Book> getRecordsWhere(Predicate<Book> predicate) {
      return bookMap.values()
              .stream()
              .filter(predicate)
              .collect(Collectors.toList());
  }

  public List<Book> getSortedRecords(Function<Book, Comparable> fieldExtractor) {
      // Return sorted list....
  }
}

Book POJO

public class Book {

  private String title;
  private String author;

  public String getTitle() {
      return title;
  }

  public void setTitle(String title) {
      this.title = title;
  }

  public String getAuthor() {
      return author;
  }

  public void setAuthor(String author) {
     this.author = author;
  }
}

Just a quick test...

public class BookTableTest {

  public static void main(String[] args) {

      File file = new File("booklist.csv");
      BookTable table = new BookTable();

      Book book1 = new Book();
      book1.setAuthor("Author 1");
      book1.setTitle("Title 1");

      Book book2 = new Book();
      book2.setAuthor("Book 2 Author 1");
      book2.setTitle("Book 2 Title 1");

      Book book3 = new Book();
      book3.setAuthor("The best author");
      book3.setTitle("The best title");        

      table.addBook(book3);
      table.addBook(book1);
      table.addBook(book2);

      System.out.println("## Sorted by Title");
      System.out.println(table.printRecords(table.getSortedRecords(Book::getTitle)));

      System.out.println();
      System.out.println("## Sorted by Author");
      System.out.println(table.printRecords(table.getSortedRecords(Book::getAuthor)));

  }
}
like image 998
Grim Avatar asked Sep 01 '16 02:09

Grim


People also ask

Why lambda is used in functional interface?

The Lambda expression is used to provide the implementation of an interface which has functional interface. It saves a lot of code. In case of lambda expression, we don't need to define the method again for providing the implementation. Here, we just write the implementation code.

Which functional interface is used for lambda expression?

Any interface with a SAM(Single Abstract Method) is a functional interface, and its implementation may be treated as lambda expressions.

Can lambda be used only with functional interface?

No, all the lambda expressions in this code implement the BiFunction<Integer, Integer, Integer> function interface. The body of the lambda expressions is allowed to call methods of the MathOperation class. It doesn't have to refer only to methods of a functional interface.

Is lambda a function or interface?

A lambda expression (lambda) is a short-form replacement for an anonymous class. Lambdas simplify the use of interfaces that declare single abstract methods. Such interfaces are known as functional interfaces. A functional interface can define as many default and static methods as it requires.


2 Answers

Part of the value of streams is not having to reinvent all of the sorting, filtering, and collection methods. Instead of having filter method, a sort method, a print method, etc., I would simply have one method that returns a Collection<Book>. Let the caller do whatever they want with that collection.

public Collection<Book> getRecords() {
    return bookMap.values();
}

...

table.getRecords().stream()
   .filter(book -> book.getAuthor().equals("Charles Dickens"))
   .sorted(Comparator.comparing(Book::getTitle))
   .forEach(System.out::println);

The advantage of this approach is that it allows the user to chain together the different operations however they like. They can filter and sort and print if that's what they want to do.

like image 140
John Kugelman Avatar answered Nov 05 '22 23:11

John Kugelman


Here is an implementation

List<Book> getSortedRecords(Predicate<Book> predicate, Comparator<Book> comparator) {
    return this.bookMap.values().stream().filter(predicate).sorted(comparator).collect(Collectors.toList());
}

where the Predicate and Comparator can be passed as a arguments for getSortedRecords like below

getSortedRecords(b -> b != null, Comparator.comparing(Book::getTitle))
like image 27
Saravana Avatar answered Nov 06 '22 01:11

Saravana