軟件構造實驗中遇到的錯誤整理

  這篇文章是在完成軟件構造實驗的過程中,記錄的一些花了一段時間才找到原因的問題的整理。前面幾個是比較簡單的問題,但是確實困擾了剛接觸Java的我不少時間,後面幾個是在實驗功能已經實現,測試的時候暴露出來的問題。


  1. Java字符串的比較

  一個字符串是一個String類的實例,比較兩個字符串是否相等需要使用equals方法。==可以用來比較基本數據類型是否相等,而用在對象上只是比較兩個對象的內存地址是否相等。
  現在看起來,這是一個相當簡單的錯誤,但是當時對對象的理解不夠透徹,對Java將數據類型分爲基本數據類型和對象還不是很理解,所以造成了錯誤。


  1. HashSet的contains方法使用hashcode進行比較

  HashSet底層是一個HashMap,HashMap通過散列表排列數據,散列值是數據的hashcode。所以設計一個類之後,需要重寫equals方法和hashCode方法,如果只重寫了equals方法,將數據加入HashSet中,再使用contains方法查詢集合中是否存在該元素時會出錯。


  1. 列表元素的刪除

  ArrayList是List下最常用的類,在ArrayList中刪除某個元素有幾個需要注意的地方。
  for循環遍歷時刪除元素:在for循環中找到要刪除的元素,假設爲第i個,刪除後,原來第i+1個元素變成第i個,如果直接進入下一輪循環,則跳過了原來的第i+1個元素,可能會出現錯誤。而且由於在循環中刪除元素,所以判斷循環結束的條件不能是提前讀取的列表的大小,否則會造成訪問越界。
  foreach循環遍歷時刪除元素:很容易拋出java.util.ConcurrentModificationException異常,所以不推薦這種方式,雖然有些情況下可以正常刪除,可以參考這篇博客


  1. 安全拷貝耗時問題

  在開始設計程序的時候,比較注重程序的安全性,在類對外提供的觀察類內部內容的方法中都使用了安全拷貝,這本來也沒錯。但是等到測試程序性能時,打開VisualVM查看最耗時的方法,排在前面的都是幾個類的clone方法。當然這並不意味着不要使用安全拷貝,而要根據程序的邏輯,減少安全拷貝方法的使用。例如在實驗中,需要根據一個頂點的label查找ConcreteGraph中的頂點,最初設計時是在ConcreteGraph類外面獲取圖上的所有頂點,然後找到具有該label的頂點,這樣就必然要調用安全拷貝方法,造成耗時。改進措施是在ConcreteGraph中添加一個根據label找頂點的方法,ConcreteGraph直接擁有頂點集,不需要安全拷貝,這樣就避免了安全拷貝的耗時。另外還要注意在提供安全拷貝方法的類中避免使用安全拷貝方法,安全拷貝方法應該是提供給外部類使用,類裏面使用會造成不必要的耗時。


  1. 使用equals方法耗時問題
      在集合或者列表中使用equals方法比較找到某一個元素,時間複雜度是O(n),隨着集合元素的增加,比較會越來越慢。使用哈希表會是個比較好的選擇,例如在HashSet中查找一個元素時間複雜度爲O(1)。當數據量非常大時,兩者的差距非常明顯。
      在實驗測試程序的性能的過程中,發現的第一個問題是上面提到的安全拷貝問題,做了改進以後,clone方法不再是最耗時的方法,取而代之的是equals方法,定位到具體代碼,該處程序是將一條邊添加到ConcreteGraph中,添加之前需要判斷邊集合中是否存在具有相同label的邊,若有,則需要自動修改label。這樣就需要遍歷這個邊的集合,並使用equals方法進行比較,而且隨着加入的邊越來越多,比較的過程會越來越慢。於是想到了使用一個HashSet專門存放邊的label,這樣,添加邊時,只需要在這個集合中查找,時間複雜度爲O(1),不會隨着加入的邊越來越多造成程序運行越來越慢,做了這個改進以後,程序讀取一個有30多萬條邊的文件並建圖的完成時間從一個小時完成不了變成了一分多鐘。後面又做了一些修改,例如加了一個HashMap存放頂點的label和頂點的對應關係,這樣,根據label查找頂點的時間複雜度也降爲O(1),也就是做了空間換時間的改進,最後只需二三十秒便可完成。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章