【问题标题】:Compare two custom objects in java with nullable fields比较java中的两个自定义对象和可为空的字段
【发布时间】:2017-03-16 12:03:53
【问题描述】:

我有一个Collection<A>

class A {
    String name;
    Long id;
}

现在我有了 UI 屏幕,我可以在其中提供名称和 id 值来匹配这个集合并显示结果。

现在我有 3 个标准:

  1. 仅提供 ID:显示所有与 ID 匹配的记录,尽管名称为空
  2. 只有名字
  3. 姓名和身份证。

我可以通过三个 if 检查来完成它,基于 UI 字段的空检查,但我不想让 if-else 阻塞,因为如果我有多个 UI 文件,那么嵌套 if-else 会很忙检查。

请在 java 7 中提出更好的方法。

【问题讨论】:

  • 覆盖等于。
  • 我不能,因为如果我可以为两个人使用相同的名字,并且如果我在 UI 中只提供名字,它应该提供两条记录
  • 我看到解决这个问题的唯一方法是使用基于谓词的代码。但是您仍然需要检查每个提交的 UI 以查看是否应将其包含在 creterias 中。

标签: java performance if-statement collections null


【解决方案1】:

您可以提供一些用于过滤集合的谓词。由于 java.util.function.Predicate 仅在 Java 8 中添加,您需要自行开发或使用 Google Guava 等库。

这样您就可以传递要在过滤器中使用的谓词:

Collection<Predicate<A>> predicates = ...;//you get them from your UI
Collection<A> filtered = Collections2.filter( originalCollection, Preciates.and( predicates ) );

谓词可能如下所示:

class ANamePredicate implements Predicate<A> {
  private String name; 

  public ANamePredicate( String n) { 
    name = n;
  }     

  boolean apply(@Nullable A input) { 
    //if one or both could be null, add handling for that
    return input.name.equals(n);
  }

  //implement equals(...) as well
}

当然您也可以使用匿名类,但您可能希望提供一个抽象基类,以消除始终实现 equals() 的要求:

abstract class AbstractPredicate<T> implements Predicate<T> {
  public boolean equals( Object o) { 
    //simplified, you might have to provide a better implementation, e.g. you want to provide a set of predicates
    return false;
  }
}

然后在需要时创建谓词集合:

Collection<Predicate<A>> predicates = new LinkedList<>();
predicates.add( new AbstractPredicate<A>() {
   boolean apply(A input) { 
    return "Skabdus".equals(input.name);
   }
} );
predicates.add( new AbstractPredicate<A>() {
   boolean apply(A input) { 
    return input.id == 1;
   }
} );

【讨论】:

  • 你能详细说明一下代码sn-p吗。特别是谓词对我来说会更好
  • @Skabdus 我添加了这样一个谓词的简化示例实现。这是您要求的详细说明吗?
【解决方案2】:

您可以将 装饰器模式 与表达式一起使用。原则是每个“标准”都将实现一个接口(使用一种方法:eval),并在 终端操作符非终端操作符之间进行区分。

您将有一个 NameIsSet 类,一个 IdIsSet 类作为终端操作符(或其他类,如果您将来有其他实现),逻辑表达式作为非终端运算符:AndOrNot、...

那么,你应该这样使用模式:

Predicate predicate = new And(new IdIsSet("id"), new Not(new NameIsSet("name")));
if(predicate.eval()){
// Do something
}

如果你想为每种情况设置一个特定的行为,你可以创建一个这些谓词的列表,并设置一个在谓词为真时调用的方法:

for(Predicate predicate: predicateList){
    if(predicate.eval()) {
       predicate.execute();
       //break if only one case is accepted
    }
}

这里是 And 类和 NameIsSet 类的示例:

public class And implements Predicate{
    private Predicate pred1;
    private Predicate pred2;

    public And(Predicate p1, Predicate p2){
        this.pred1 = p1;
        this.pred2 = p2;
    }

    @Override
    public boolean eval(){
        return this.pred1.eval() && this.pred2.eval();
    }

    @Override
    public void execute(){
        // Not sure of you want to do here
    }

}

public class NameIsSet implements Predicate{
    private String name;

    public NameIsSet (String name){
        this.name = name;
    }

    @Override
    public boolean eval(){
        return this.name == null || this.name.length = 0;
    }

    @Override
    public void execute(){
        // Not sure of you want to do here
    }

}

在此实现中,您必须事先创建所有谓词,将它们添加到列表中并为每个谓词设置 execute() 方法。

希望这对你有用。

【讨论】:

    【解决方案3】:

    您的if 语句不需要嵌套。例如,您可以这样做:

    static boolean matches(A candidate, A pattern) {
        if (pattern.name != null && !candidate.name.equals(pattern.name))
            return false;
        if (pattern.id != null && !candidate.id.equals(pattern.id))
            return false;
        return true;
    }
    

    这与任意数量的字段以 1:1 的比例缩放。 (您可以在那里使用candidate.id != pattern.id,但我使用equals 以保持一致性。)

    如果您有几十个字段并且不想单独命名它们,则可以使用反射并使用单个 for 循环处理所有字段:

    static boolean matches(A candidate, A pattern) throws IllegalAccessException {
        for (Field fld : A.class.getDeclaredFields()) {
            Object c = fld.get(candidate);
            Object p = fld.get(pattern);
            if (p != null && !c.equals(p))
                return false;
        }
        return true;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-12
      • 1970-01-01
      • 2023-03-07
      • 2023-02-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多