简体   繁体   中英

how to write one java generic method to check value in generic list?

Let say I have the Student class:

public class Student {
    private String id;
    private String firstName;
    private String lastName;
    //gettter and setter
}

I will have list of Student :

List<Student> list; //this includes all students

Now, when i retrieve Student details object, I will just have the id and need to check whether this id exists in list provided above or not. I can easily achieve this with simple java like this: creating one method which will take the list and student id as parameters and checking whether it is there in the list or not:

public static boolean valueExistInList(List<Student> list, String studentId) {
    if (StringUtils.isNotBlank(studentId)) {
        for (Student stu : list) {
            if (studentId.equals(stu.getId())) {
                return true;
            }
        }
    }
    return false;
}

But my question is: can I also achieve same kind of functionality with Java generics method that will take list of any type of object and single value parameter(String or Integer or BigDecimal or BigInteger ) and checks whether single parameter exists in list or not and return boolean value?

Any help will be appreciated. Thanks

I am thinking if i can have method like this:

public static <T, U> boolean valueExistInList(List<T> list, U studentId) {
    boolean exists = false;
    //looping and checing whether studentId exists in list or not and 
    //then returning boolean value
    for(T t: list){
        //check studentId exists in list or not
        //if exists then return exists = true
        return exists;
    }
    return exists;
}

Your main problem with this generic method approach is that you do not know which method to call on each list element for checking. Additionally, you easily can imagine other boolean tests, not only equals (and not only on strings).

So you could go another step back: Let the caller of your method decide what boolean test should be made in the loop. You could express this as an interface:

interface BooleanExpression<T> { boolean test(T arg); }

Your generic method now changes to:

public static <T, U> boolean valueExistInList(List<T> list, BooleanExpression<T> tester) {
    for(T t: list) {
        if (tester.test(t)) return true;
    }
    return false;
}

You now can call this method as such:

List<Student> list = ...
final String studentId = ...
boolean result = valueExistsInList(list, new BooleanExpression<Student>() {
    @Override public boolean test(Student arg) {
         return arg.getId().equals(studentId);
    }
});

Having said this ... Java 8 provides such an interface. It is called a Predicate . And it nicely fits into the new Stream API . So you now can write

List<Student> list = ...
String studentId = ...
boolean result = list.stream().anyMatch(s -> s.getId().equals(studentId));

You can in combination with functions.

public static <T> boolean valueExistInList(List<T> list, Predicate<T> predicate) {
    return list.stream().anyMatch(predicate);   
}

You can call it like this:

valueExistsInList(students, s -> s.getId().equals(studentId));

Or:

valueExistsInList(cars, c -> c.getBrand().equals("Tesla"));

In fact, you can do this even without a function, more directly like this:

boolean exists = students.stream().anyMatch(s -> s.getId().equals(studentId));

Java 7

public interface Predicate<T> {
    boolean test(T t);
}

Then call your function like this:

valueExistInList(students, new Predicate<Student>() {
    boolean test(Student s) {
        return s.getId().equals(studentId);
    }
});

And the function valueExistsInList then becomes:

public static <T> boolean valueExistInList(List<T> list, Predicate<T> predicate) {
    for (T t : list) {
        if (predicate.test(t)) {
            return true;
        }
    }
    return false;   
}

List is an extension from the java Collection interface so you can do a lot of customizing by overriding the equals method so your contains method will function as you'd like. You would end up doing something like this:

@Override
public boolean equals(Object o){
    if(o instanceof String){
        String toCompare = (String) o;
        return id.equals(toCompare);
    }
    return false;
}

Please check the (working) example below. By letting Student override from ObjectWithId<U> where U is the type of the id, you can have generic methods to check for the ids (in the Util class)

public class Test
{
  public static void main(String[] args){
     Student s = new Student();
     s.setId("studentId1");

     System.out.println(Util.hasCorrectId(s, "studentId1"));
  }
}

public class ObjectWithId<U>
{
   private U id;

  public U getId(){
    return id;
  }

  public void setId(U id){
    this.id = id; 
  }
}

public class Student extends ObjectWithId<String>
{

}

public class Util {

  public static <U> boolean valueExistInList(List<ObjectWithId<U>> list, U id) {

        for (ObjectWithId<U> obj : list) {
            if(hasCorrectId(obj, id)) return true;
        }

        return false;

  }

  public static <U> boolean hasCorrectId(ObjectWithId<U> obj, U id){
    return id.equals(obj.getId()); 
  }


}

You can have something like this

public static <T, U> boolean valueExistInList(List<T> list, U input) {
for(T t: list)
{
    if (input.equals(t.getU)) // getU method should be there in T 
    return true;
}
return false;
}

But you have to be careful while passing custom types. You have to override equals method.

Although this will work fine for types like String , Integer etc where equals is already overriden.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM