StackOverflowError in equals双向对象的方法

时间:2017-12-03 02:37:22

标签: java

我必须对象客户端和订单,这些对象生活在双向关系中,我尝试将它们写入文件,但我得到了StackOverflowError。我得到了这个错误,因为我的equals方法是循环的。

我尝试序列化的类:

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Client implements Serializable {

    private Long id;

    private String name;

    private List<Order> orders = new ArrayList<>();

    public void addOrder(Order order) {
        order.setClient(this);
        orders.add(order);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Client client = (Client) o;

        if (id != null ? !id.equals(client.id) : client.id != null) return false;
        if (name != null ? !name.equals(client.name) : client.name != null) return false;
        return orders != null ? orders.equals(client.orders) : client.orders == null;
    }

    @Override
    public int hashCode() {
        int result = id != null ? id.hashCode() : 0;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        result = 31 * result + (orders != null ? orders.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        return "Client{" +
                "id=" + id +
                ", name='" + name + '\'' +
//                ", orders=" + orders.size() +
                '}';
    }
}

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Order implements Serializable {

    private Long id;

    private String name;

    private Client client;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Order order = (Order) o;

        if (id != null ? !id.equals(order.id) : order.id != null) return false;
        if (name != null ? !name.equals(order.name) : order.name != null) return false;
        return client != null ? client.equals(order.client) : order.client == null;
    }

    @Override
    public int hashCode() {
        int result = id != null ? id.hashCode() : 0;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        result = 31 * result + (client != null ? client.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        return "Order{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

@Data
@AllArgsConstructor
public class MapDataSource implements Serializable {

    private final Map<Date, List<Client>> clients = new HashMap<>();
    private final Map<Date, List<Order>> orders = new HashMap<>();
}

@Slf4j
public class ObjectWriter {
    private final String fileName = "data.obj";

    public void write(String fileName, MapDataSource mapDataSource) {
        try (
                FileOutputStream fs = new FileOutputStream(fileName);
                ObjectOutputStream oos = new ObjectOutputStream(fs)
        ) {
            oos.writeObject(mapDataSource);
            log.info("Object has been written.");
        } catch (IOException ioe) {}
    }
}

@Slf4j
public class ObjectReader {
    private static final String fileName = "data.obj";

    public MapDataSource readObj(String fileName) {
        MapDataSource mapDataSource = null;
        try (
                FileInputStream fis = new FileInputStream(fileName);
                ObjectInputStream ois = new ObjectInputStream(fis)
        ) {
            mapDataSource = ((MapDataSource) ois.readObject());
//            log.info("Read object: {}", mapDataSource);
        } catch (IOException ioe) {

        } catch (ClassNotFoundException classEx) {
            System.out.println();
        }
        return mapDataSource;
    }
}

当我尝试运行下面的代码时,我得到StackOVerflowError:

String testFile = "testFile.obj";
        final DateTime time = new DateTime(2017, 12, 1, 10, 0);
        final Client client1 = new Client(1L, "Client1", new ArrayList<>());
        final Order order1 = new Order(1L, "Order1", null);
        final MapDataSource mapDataSource = new MapDataSource();
        mapDataSource.getClients().put(time.toDate(), new ArrayList<>());
        mapDataSource.getClients().get(time.toDate()).add(client1);
        mapDataSource.getOrders().put(time.toDate(), new ArrayList<>());
        mapDataSource.getOrders().get(time.toDate()).add(order1);

        new ObjectWriter().write(testFile, mapDataSource);
        final MapDataSource found = new ObjectReader().readObj(testFile);
        System.out.println(found);

解决方案: MapDataSource需要实现equals()hashcode()方法。

1 个答案:

答案 0 :(得分:2)

看起来你需要坐下来认真考虑它应该对两个客户或者首先要求相同的客户意味着什么。 Long id;让我想知道你是否应该首先比较对象图。如果是客户端具有唯一的ID,那么只需确保客户端是唯一的对象实例就可以了,然后完全解决问题。

如果您确实需要比较对象图,可以使用以下内容。我们使用IdentityHashMap来记录我们已经看到的所有对象,然后如果我们检测到一个循环,我们只需比较先前存储的计数器值,它们告诉我们这两个图是否具有相同的周期。 / p>

ClientOrder需要共享代码(因此可以传递地图),因此您只需将equals覆盖到return ClientOrderEquality.equals(this, that)

import java.util.*;

public final class ClientOrderEquality {
    private ClientOrderEquality() {}

    private static final class Counter { long value; }

    public static boolean equals(Client lhs, Client rhs) {
        return equals(lhs, new IdentityHashMap<>(),
                      rhs, new IdentityHashMap<>(),
                      new Counter());
    }

    public static boolean equals(Order lhs, Order rhs) {
        return equals(lhs, new IdentityHashMap<>(),
                      rhs, new IdentityHashMap<>(),
                      new Counter());
    }

    private static boolean equals(Client            lhs,
                                  Map<Object, Long> seenL,
                                  Client            rhs,
                                  Map<Object, Long> seenR,
                                  Counter           counter) {
        if (lhs == null || rhs == null)
            return lhs == rhs;
        Long countL = seenL.putIfAbsent(lhs, counter.value);
        Long countR = seenR.putIfAbsent(rhs, counter.value);
        if (countL != null || countR != null)
            return Objects.equals(countL, countR);
        counter.value++;
        if (lhs == rhs)
            return true;
        if (!Objects.equals(lhs.id, rhs.id))
            return false;
        if (!Objects.equals(lhs.name, rhs.name))
            return false;
        if (lhs.orders.size() != rhs.orders.size())
            return false;
        Iterator<Order> itL = lhs.orders.iterator();
        Iterator<Order> itR = rhs.orders.iterator();
        while (itL.hasNext() && itR.hasNext())
            if (!equals(itL.next(), seenL, itR.next(), seenR, counter))
                return false;
        return true;
    }

    private static boolean equals(Order             lhs,
                                  Map<Object, Long> seenL,
                                  Order             rhs,
                                  Map<Object, Long> seenR,
                                  Counter           counter) {
        if (lhs == null || rhs == null)
            return lhs == rhs;
        Long countL = seenL.putIfAbsent(lhs, counter.value);
        Long countR = seenR.putIfAbsent(rhs, counter.value);
        if (countL != null || countR != null)
            return Objects.equals(countL, countR);
        counter.value++;
        if (lhs == rhs)
            return true;
        if (!Objects.equals(lhs.id, rhs.id))
            return false;
        if (!Objects.equals(lhs.name, rhs.name))
            return false;
        return equals(lhs.client, seenL, rhs.client, seenR, counter);
    }
}

我假设您想要实际使用该代码,您需要更改它以使用您正在使用的任何getter命名格式并编写hashCode实现。如果您正在扩展ClientOrder,您还需要正确考虑子类型。