查找集合中的对象是否具有相同的字段值

Find If Objects in Collection have same field values

我正在尝试创建一个编辑器来编辑在 JTree 中选择的多个设备。

如果集合中的项目都对某个字段具有相同的值,我将在编辑器表单中显示该值。如果它们有不同的值,我将显示 "Multiple Values"

我曾尝试使用类似的东西,但仅限于比较两个项目。我想对集合中的所有项目执行此操作。

 private static List<String> difference(Student s1, Student s2) {
 List<String> values = new ArrayList<>();
 for (Field field : s1.getClass().getDeclaredFields()) {
    // You might want to set modifier to public first (if it is not public yet)
    field.setAccessible(true);
    Object value1 = field.get(s1);
    Object value2 = field.get(s2); 
    if (value != null && value != null) {
        System.out.println(field.getName() + "=" + value1);
        System.out.println(field.getName() + "=" + value2);
        if (!Objects.equals(value1, value2) {
            values.add(value2);
        }
    }
}
return values;

}

有人可以举例说明您如何确定集合中的对象具有相同值的字段吗?

我的哈希和等于代码如下。我假设这可以使用 Collection 的内置方法来完成,但我希望能举个例子。

    @Override
public int hashCode() {
    int hash = 5;
    hash = 47 * hash + (this.isSelected ? 1 : 0);
    hash = 47 * hash + Objects.hashCode(this.user);
    hash = 47 * hash + Objects.hashCode(this.password);
    hash = 47 * hash + Objects.hashCode(this.address);
    hash = 47 * hash + (int) (this.addressAsLong ^ (this.addressAsLong >>> 32));
    hash = 47 * hash + this.port;
    hash = 47 * hash + Objects.hashCode(this.vendor);
    hash = 47 * hash + Objects.hashCode(this.model);
    hash = 47 * hash + Objects.hashCode(this.OS);
    hash = 47 * hash + Objects.hashCode(this.description);
    hash = 47 * hash + Objects.hashCode(this.version);
    hash = 47 * hash + Objects.hashCode(this.hostName);
    hash = 47 * hash + Objects.hashCode(this.domain);
    hash = 47 * hash + Objects.hashCode(this.deviceType);
    hash = 47 * hash + Objects.hashCode(this.Location);
    hash = 47 * hash + Objects.hashCode(this.SerialNumber);
    // hash = 47 * hash + Objects.hashCode(this.parent);
    return hash;
}

@Override
public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }
    if (obj == null) {
        return false;
    }
    if (getClass() != obj.getClass()) {
        return false;
    }
    final DefaultDevice other = (DefaultDevice) obj;
    if (this.isSelected != other.isSelected) {
        System.out.println("isSelected");
        return false;
    }
    if (this.addressAsLong != other.addressAsLong) {
        // System.out.println("long");
        return false;
    }
    if (this.port != other.port) {
        //System.out.println("port");
        return false;
    }
    if (!Objects.equals(this.user, other.user)) {
        // System.out.println("user");
        return false;
    }
    if (!Objects.equals(this.password, other.password)) {
        //System.out.println("pass");
        return false;
    }

    if (!Objects.equals(this.vendor, other.vendor)) {
        //System.out.println("ven");
        return false;
    }
    if (!Objects.equals(this.model, other.model)) {
        //System.out.println("mod");
        return false;
    }
    if (!Objects.equals(this.OS, other.OS)) {
        // System.out.println("os");
        return false;
    }
    if (!Objects.equals(this.description, other.description)) {
        //System.out.println("des");
        return false;
    }
    if (!Objects.equals(this.version, other.version)) {
        //System.out.println("ver");
        return false;
    }
    if (!Objects.equals(this.hostName, other.hostName)) {
        // System.out.println("hostNa");
        return false;
    }
    if (!Objects.equals(this.domain, other.domain)) {
        // System.out.println("dom");
        return false;
    }
    if (!Objects.equals(this.deviceType, other.deviceType)) {
        // System.out.println("dt");
        return false;
    }
    if (!Objects.equals(this.Location, other.Location)) {
        //System.out.println("loc");
        return false;
    }
    if (!Objects.equals(this.SerialNumber, other.SerialNumber)) {
        // System.out.println("sn");
        return false;
    }

    return true;
}

考虑到可访问性、使用 getters/setters 而不是字段访问等的综合解决方案将需要相当多的努力,但在您描述的范围内,算法可能如下所示:

  • 在你的方法中return一个你迭代的类型的对象作为结果,并为不匹配的情况取一个具有默认值的对象(如果你愿意,用Map<String, Object>替换那些)
  • 从非空集合中取出第一项并确定其class
  • 遍历所有声明的字段,每个字段都从第一个项目中获取值,然后遍历项目直到第一个不匹配
  • 如果不匹配,从默认值分配值并中断迭代
  • 将找到的值设置到结果字段并移动到下一个字段

在代码中它看起来像这样:

class ObjectMatcher {

    // NPE on null items
    // assumes public default constructor for T is available
    public <T> T match(Collection<T> items, T defaults) {
        if (items.isEmpty()) {
            return defaults;
        }
        try {
            @SuppressWarnings("unchecked")
            Class<T> clazz = (Class<T>) items.iterator().next().getClass();
            Field[] fields = clazz.getDeclaredFields();

            T res = clazz.newInstance();

            for (Field field : fields) {
                boolean firstItem = true;
                Object match = null;
                for (T item : items) {
                    Object value = field.get(item);
                    if (firstItem) {
                        match = value;
                    }
                    else if (!Objects.equals(value, match)) {
                        match = field.get(defaults);
                        break;
                    } // otherwise keep the match as is
                    firstItem = false;
                }
                field.set(res, match);
            }
            return res;
        }
        catch (IllegalAccessException | InstantiationException e) {
            throw new RuntimeException(e);
        }
    }
}

下面是字段不匹配、匹配和 null/non-null 不匹配情况的简单测试:

@Test
public void match_onMistmatchMatchAndNull_ok() {
    Student s1 = new Student("Andrew", "Physics", null);
    Student s2 = new Student("Joe", "Physics", 3.45);
    Student s3 = new Student("Nicki", "Physics", 2.39);

    Student defaults = new Student("Multiple Names", "Multiple Courses", 1.0);

    ObjectMatcher matcher = new ObjectMatcher();
    Student res = matcher.match(Arrays.asList(s1, s2, s3), defaults);

    assertEquals("Multiple Names", res.name);
    assertEquals("Physics", res.course);
    assertEquals(1.0, res.grade.doubleValue(), 0.001);
}

您将在 dedicated GitHub repo 上找到测试 class 的完整代码。