2017-12-03 6 views
3

オブジェクトにClientとOrderが必要です。これらのオブジェクトは双方向関係にあり、それらをファイルに書き込もうとしますが、StackOverflowErrorが発生します。私のequalsメソッドがループされているので、私はこのエラーが発生します。StackOverflowError in双方向オブジェクトの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()メソッドを実装している必要があります。

+0

あなたの質問に完全に無関係です:あなたはそれらの1行if文を必要としませんスタイル賢明な場合は、単にブール式を返すことができます。 – Meepo

+0

https://stackoverflow.com/questions/7602089/examining-associated-objects-in-equals – Raedwald

答えて

2

あなたが座って、2人のクライアントまたは注文が最初に等しいことを意味する必要があるものを真剣に検討する必要があるようです。 Long id;は、最初にオブジェクトグラフを本当に比較するべきかどうか疑問に思います。たとえばクライアントに一意のIDがある場合、クライアントが一意のオブジェクトインスタンスであることを確認してから、問題を完全に解決することができます。

実際にオブジェクトグラフを比較する必要がある場合は、次のようなものを使用できます。私たちは、見たすべてのオブジェクトを記録するためにIdentityHashMapを使用します。サイクルを検出すると、2つのグラフが同じサイクルであるかどうかを示す以前に格納されたカウンタ値を比較します。

ClientOrderあなただけreturn ClientOrderEquality.equals(this, that)の両方でequalsをオーバーライドするので、(そのマップは周りに渡すことができる)コードを共有する必要があります。

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); 
    } 
} 

私は、あなたが実際にそのコードを使用したい場合は、あなたが使っているものは何でもゲッター命名形式を使用してhashCode実装を作成し、それを変更する必要がありますと仮定します。 ClientOrderを拡張する場合は、サブタイプも正しく考慮する必要があります。