2017-12-03 16 views
3

我必須對象客戶端和順序和這些對象都生活在雙向關係,我嘗試將它們寫入文件,但我得到StackOverflowError。我得到這個錯誤,因爲我的equals方法循環。StackOverflowError在等於雙向對象的方法

我的班,我試圖序列:

@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

完全與你的問題無關:風格明智,你不需要那些一行if語句,你可以只返回布爾表達式。 – Meepo

+0

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

回答

2

看起來你需要坐下來認真考慮它應該甚至對於兩個客戶或訂單是否平等首先是什麼意思。 Long id;讓我懷疑你是否應該首先比較對象圖。如果例如客戶端具有唯一的ID,那麼確保客戶端是唯一的對象實例,然後完全消除該問題是有意義的。

如果你真的需要比較對象圖,你可以使用類似下面的東西。我們使用一個IdentityHashMap來記錄我們所看到的所有對象,然後如果我們檢測到一個循環,我們只比較先前存儲的計數器值,告訴我們這兩個圖形是否具有相同的循環。

ClientOrder需要共享代碼(這樣的地圖,可以通過左右),所以你只覆蓋兩個equalsreturn 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,您還需要正確考慮子類型。