1

I have a Book class and then I make a List<Book>. I have a problem with checking all attributes. The following is an example of program code for a class Book :

class Book {  
    int id;  
    String name,author;
    public Book(int id, String name, String author) {  
        this.id = id;  
        this.name = name;  
        this.author = author;  
    }   
}

Here's the code snippet on the List<Book> :

 Book b = new Book(1, "", "Ok");
 Book c = new Book(2, "Z", "");
 Book d = new Book(0, "C", "Ok");
 List<Book> x = new ArrayList<>();
 x.add(b);
 x.add(c);
 x.add(d);

How to check if a value is string empty or null in a List<Book> and then return Boolean & return message example id 2 has empty value?

Dada
  • 6,313
  • 7
  • 24
  • 43
senaa
  • 63
  • 3
  • 8

4 Answers4

3

You can use the StringUtils.isEmpty(...) method from Apache Commons, or if you don't want the dependency you can write it your on like this:

public static boolean isEmpty(String s) {
  return s == null || s.isEmpty();
}

To check all authors in the List use Streams:

public boolean anyAuthorEmpty(List<Book> books) {
  return books.stream().anyMatch(b -> isEmpty(b.getAuthor());
}

To find the exact Book you can use .findFirst() like this:

public Book findBookWithEmptyAuthor(List<Book> books) {
  return books.stream().filter(b -> isEmpty(b.getAuthor())
                .findFirst().orElse(null);
}

This will return the Book or null if none found.

If you need all with no author you can use Collectors.toList():

public List<Book> findBooksWithNoAuthor(List<Book> books) {
  return books.stream().filter(b -> isEmpty(b.getAuthor())
                .collect(Collectors.toList());
csalmhof
  • 1,820
  • 2
  • 15
  • 24
  • 1
    The requirement is to return exactly which objects have empty `author`. You will need to add that piece too. – Sree Kumar Nov 26 '21 at 10:56
  • is there any other way without calling the attributes one by one? @csalmhof – senaa Nov 26 '21 at 11:03
  • yes it's true what you said @SreeKumar – senaa Nov 26 '21 at 11:07
  • 1
    @senaa You can use multiple filters or filter for more values in e.g. a method `findBooksWithEmptyValue(List books)`. Then the filter would look like `.filter(b -> (isEmpty(b.getAuthor() || isEmpty(b.getName()))`. But then you loose the information, which field was empty. If you want to general check if any String-Field is null or empty you should go with reflection as @alexey-r mentiononed in [this answer](https://stackoverflow.com/a/70123755/9901515). – csalmhof Nov 26 '21 at 12:04
2

Or you can use Java Stream perhaps

public class Test1 {
    private Test1()
    {
        Book b = new Book(1, "", "Ok");
        Book c = new Book(2, "Z", "");
        Book d = new Book(3, "C", "Ok");
        List<Book> x = new ArrayList<>();
        x.add(b);
        x.add(c);
        x.add(d);
        
        boolean empty = x.stream()
                .filter(book -> book.name == null || book.name.isEmpty() || book.author == null || book.author.isEmpty())
                .count() > 0;
    }

    class Book
    {
        int id;
        String name, author;

        public Book(int id, String name, String author)
        {
            this.id = id;
            this.name = name;
            this.author = author;
        }
    }
    
    public static void main(String[] args)
    {
        new Test1();
    }
}
Dada
  • 6,313
  • 7
  • 24
  • 43
Mr Eddy
  • 64
  • 2
  • Hello Mr Eddy, is there any other way without calling the attributes one by one? – senaa Nov 26 '21 at 11:00
  • 1
    Yes with Java reflection or with a new method in Book class which can test all the string of the current book, then you will juste have to call it for each book object to check if there is a Book with an empty String – Mr Eddy Nov 26 '21 at 11:37
2

You can use reflection so that you do not need to manually test each filed separately:

static void testAllFieldsForNull(List<Book> bookList) throws IllegalAccessException {
    for (int i = 0; i < bookList.size(); i++){
        Book book = bookList.get(i);
        Field[] fields = book.getClass().getDeclaredFields();
        for(Field field: fields){
            Class<?> fieldType = field.getType();
            if(!fieldType.isPrimitive()){
                if (field.get(book) == null){
                    System.out.println("Field [" + field.getName() + "] has null value for book at position " + i);
                    continue;
                }
                if(fieldType.isAssignableFrom(String.class) && ((String)field.get(book)).isEmpty()){
                    System.out.println("Field [" + field.getName() + "] is empty String for book at position " + i);
                }
            }
        }
    }
}

Test:

public static void main(String[] args) throws IllegalAccessException {
    Book b = new Book(1, "", "Ok");
    Book c = new Book(2, "Z", "");
    Book d = new Book(0, "C", null);
    List<Book> x = new ArrayList<>();
    x.add(b);
    x.add(c);
    x.add(d);
    testAllFieldsForNull(x);
}

Output:

Field [name] is empty String for book at position 0
Field [author] is empty String for book at position 1
Field [author] has null value for book at position 2

OR if you need to just collect "good" books (actually any sort of oject) you can use:

public static boolean testObject(Object obj){
    Field[] fields = obj.getClass().getDeclaredFields();
    boolean okay = true;
    for(Field field: fields){
        Class<?> fieldType = field.getType();
        try{
            if(!fieldType.isPrimitive()){
                if (field.get(obj) == null){
                    okay = false;
                    continue;
                }
                if(fieldType.isAssignableFrom(String.class) && ((String)field.get(obj)).isEmpty()){
                    okay = false;
                }
            }
        }catch (IllegalAccessException e){
            e.printStackTrace();
            return false;
        }
    }
    return okay;
}

and then use that for filtering:

public static void main(String[] args) throws IllegalAccessException {
    Book b = new Book(1, "", "Ok");
    Book c = new Book(2, "Z", "");
    Book d = new Book(0, "C", null);
    Book a = new Book(3, "C", "D");
    List<Book> x = new ArrayList<>();
    x.add(b);
    x.add(c);
    x.add(d);
    x.add(a);
    System.out.println(
            x
            .stream()
            .filter(FieldTest::testObject)
            .collect(Collectors.toList()).get(0).id
    );
}
Alexey R.
  • 8,057
  • 2
  • 11
  • 27
2

To check multiple attributes of the class Book the following solution ,may be offered (providing that there is implementation of the helper method isNullOrEmpty):

class SONullEmpty {
    static boolean isNullOrEmpty(String str) {
        return str == null || str.isEmpty();
    }

    static List<Book> booksWithNullOrEmpty(List<Book> books) {
        return books
            .stream()
            .filter(book -> Stream.of(
                    book.getName(), book.getAuthor()
                ).anyMatch(SONullEmpty::isNullOrEmpty)
            )
            .collect(Collectors.toList());
    }
}

Similarly, a method accepting multiple getters of the Book attributes may be implemented and then called:

// in the same SONullEmpty class
static List<Book> withNullOrEmptyAttribs(List<Book> books, Function<Book, String> ... getters) {
    return books
        .stream()
        .filter(book -> Arrays.stream(getters).anyMatch(g -> isNullOrEmpty(g.apply(book))))
        .collect(Collectors.toList());
}

Test:

Book b = new Book(1, "", "Ok");
Book c = new Book(2, "Z", "");
Book d = new Book(3, null, "Ok");
List<Book> x = Arrays.asList(b, c, d);

withNullOrEmptyAttribs(x, Book::getName)
    .forEach(book -> System.out.printf("Book with id=%d has null or empty name%n", book.getId()));

withNullOrEmptyAttribs(x, Book::getAuthor)
    .forEach(book -> System.out.printf("Book with id=%d has null or empty author%n", book.getId()));

Output:

Book with id=1 has null or empty name
Book with id=3 has null or empty name
Book with id=2 has null or empty author
Nowhere Man
  • 19,170
  • 9
  • 17
  • 42