1. 程式人生 > >20172305 2018-2019-1 《Java軟件結構與數據結構》第四周學習總結

20172305 2018-2019-1 《Java軟件結構與數據結構》第四周學習總結

con 一道 exc nts 對象 owin bee 題目 創建

20172305 2018-2019-1 《Java軟件結構與數據結構》第四周學習總結

教材學習內容總結

本周內容主要為書第六章內容:

  • 列表
    • 有序列表(元素按照元素內在特性進行排序)
    • 無序列表(元素間不具有內在順序,元素按照他們在列表中的位置進行排序)
    • 索引列表(元素可以用數字索引來引用)
  • 列表集合比棧和隊列更一般化,可以在列表的中間和末端添加和刪除元素。

  • 有序列表是基於列表中元素的某種特性的。對於任何已添加到有序列表中的元素,只要給定了元素的關鍵值,同時列表已定義了元素的所有關鍵值,那麽它在列表中就會有一個固定的位置。

  • 無序列表中各元素的位置並不基於元素的任何內在特性。列表中的元素是按照特殊順序放置的,只不過這種順序與元素本身無關。

  • 索引列表的各元素間不存在能夠決定他們在列表中的順序的內在關系。每個元素都能夠從一個數字索引值得到引用,該索引值從列表頭開始從0連續增加直到列表末端。

  • 索引值與數組的根本區別:索引列表的索引值總是連續的?

  • Java集合API提供的列表類主要是支持索引列表。Java API沒有任何直接實現的有序列表。

  • 有序列表與無序列表之間的差別主要體現在往列表添加元素上
    • 有序列表中只需制定要添加的新元素,該元素在列表中的位置取決於其鍵值。
    • 無序列表中可以把元素添加到列表的前端、末端或是列表中某個已有的元素後面。
  • Serializable接口的作用是實現串行化,為了使某個對象能使用串行化進行存儲。

  • Java 序列化技術可以將一個對象的狀態寫入一個Byte 流裏(串行化),並且可以從其它地方把該Byte 流裏的數據讀出來(反串行化)。

  • 只有Comparable對象才能存儲在有序列表。

  • 接口可以用來派生其它接口,子接口包含父接口的所有的抽象方法

  • 接口名可以用來聲明一個對象引用變量。一個接口引用可以用來引用實現了該接口的任意類的任意對象。

  • 接口允許創建多態引用,其中被調用的方法是基於被引用時的特定對象的。

教材學習中的問題和解決過程

  • 問題1:數組與索引列表的索引值是否連續的問題?
  • 問題1解決方案:書上說的索引列表與數組的根本區別是索引列表的索引值是連續的。那麽,數組的索引值就不連續麽?可以從0開始,下一個直接到5麽?仔細想想是不很可能的,那麽書上的話是什麽意思呢?網上並沒有相關的答案,所以我想了一下覺得指的是數組可以在容量允許的前提下跳躍式的往數組內添加元素,可以在0索引值處添加元素,然後在5索引值處添加元素,中間的索引值內都為零。而所以索引列表不同,只能連續的往裏面添加,不能中間出現斷檔。

    • 索引列表為它的元素維護了一段連續的數字索引值
  • 問題2:列表用普通數組而不是環形數組的原因?
  • 問題2解決方案:隊列用環形數組的原因是只在一頭出另一頭進,而列表的添加方法比較多,其中就要求在中間進行添加,此外刪除的時候也要求既可以刪頭又可以刪尾的。所以,使用環形數組並不方便,頭部變量和尾部變量都在動,插中間會更麻煩。

代碼學習中的問題和解決過程

  • 問題1:有序列表的add與無序列表的addafter、addToRear、addToFront的區別
  • 問題1的解決方案:有序列表與無序列表在添加的方法下有很大的不同,有序列表的添加直接按元素的內在順序進行排列,而無序列表的就比較任意可以根據用戶的想法進行。此外,有序列表要求的是只有Comparable對象才可以存儲在有序列表中。

      數組表示有序列表的添加
      public void add(T element) {
        if (!(element instanceof Comparable)) {
            throw new NonComparableElementException("OrderList");
          }
    
        Comparable<T> comparableElement = (Comparable<T>) element;
        if (size() == list.length)
            expandCapacity();
        int scan = 0;
        while (scan < rear && comparableElement.compareTo(list[scan]) > 0)
            scan++;
        for (int shift = rear; shift > scan; shift--)
            list[shift] = list[shift - 1];
        list[scan] = element;
        rear++;
        modCount++;
      }
    鏈表表示有序列表的添加
    public void add(T element) {
      LinearNode<T> node = new LinearNode(element); 
    
      LinearNode<T> temp1 = null,temp2 = head;
      while(temp2 != null && node.compareTo(temp2.getElement())> 0){
          temp1 = temp2;
          temp2 = temp2.getNext();
        }
      if(temp1 == null){
          head = tail =  node;
        }
      else{
          temp1.setNext(node);
        }
      node.setNext(temp2);
      if(node.getNext() == null)
          tail = node;
    
      count++;
      modCount++;
      }

    通過compareTo方法來進行比較,通過循環遍歷隊列元素來判斷大小,然後數組把添加的索引值後面的元素進行後移,鏈表進行插入操作就行。但是,數組需要判斷容量大小,以及循環次數不是數組的容量而是元素的總量;鏈表需要確定添加的元素之前鏈表是否有內容,沒有內容的話直接頭結點等於尾節點等於添加元素,有內容的話需要把後面的內容接上。

    數組表示無序列表的添加--頭插
    public void addToFront(T element) {
          if (size() == list.length)
              expandCapacity();
    
          for (int shift = rear; shift > 0; shift--)
              list[shift] = list[shift - 1];
          list[0] = element;
          rear++;
          modCount++;
    
      }
    
      數組表示無序列表的添加--尾插
      public void addToRear(T element) {
          if (size() == list.length)
              expandCapacity();
          list[rear] = element;
          rear++;
          modCount++;
    
      }
    
      數組表示無序列表的添加--目標元素後面
      public void addAfter(T element, T target) {
    
          if (size() == list.length)
              expandCapacity();
    
          int scan = 0;
    
          while (scan < rear && !target.equals(list[scan]))
              scan++;
    
          if (scan == rear)
              throw new ElementNotFoundException("UnorderedList");
    
          scan++;
    
          for (int shift = rear; shift > scan; shift--)
              list[shift] = list[shift - 1];
    
          list[scan] = element;
          rear++;
          modCount++;
      }
    鏈表表示無序列表的添加--頭插
    public void addToFront(T element) {
          LinearNode<T> node = new LinearNode<T>(element);
          LinearNode<T> temp = head;
          if(isEmpty())
              head = node;
          else {
              node.setNext(head);
              while(temp.getNext() != null){
                  temp = temp.getNext();
              }
              tail = temp;
              head = node;
          }
          count++;
          modCount++;
      }
      鏈表表示無序列表的添加--尾插
      public void addToRear(T element){
          LinearNode<T> node = new LinearNode<T>(element);
          if(isEmpty())
              head = node;
          else
              tail.setNext(node);
          tail = node;
          count++;
      }
      鏈表表示無序列表的添加--目標元素後面
      public void addAfter(T element, T target) {
          LinearNode<T> node = new LinearNode<T>(element);
          LinearNode<T> temp = head;
          if(temp == null){
              head = tail = node;
          }
          while((temp != null)&&(temp.getElement() == target)){
              temp = temp.getNext();
          }
          if(temp.getNext() == null){
              temp.setNext(node);
              tail = node;
          }else{
              node.setNext(temp.getNext());
              temp.setNext(node);
          }
          count++;
          modCount++;
      }
    無序列表的添加看似多實則比有序列表的簡單多了就是進行尾插、頭插、中間插三種方法。其中,尾插法相對簡單,頭插法需要把數組內的所有元素整體後移,遍歷一遍,中間插的話要考慮插後的元素來確定後移的循環次數。
  • 問題2:列表的常見操作removeFirst、removeLast、remove、first、last、contains、isEmpty、size方法
  • 問題2的解決方法:無序列表和有序列表的共有方法

    • removeFirst:從列表中刪除第一個元素
    • 數組:需要先判斷數組是否為空,不為空的話索引值為0的內容直接等於索引值為1的內容,以此循環前移。
    • 鏈表:先判斷鏈表內的元素的個數,如果為1直接頭結點等於尾節點等於空,不為1的話直接使鏈表的頭等於頭的下一個。
    • removeLast:從列表中刪除最後一個元素
    • 數組:在數組不為空的前提下,直接使數組索引值最大的位置內為空,計數變量減一。
    • 鏈表:確定鏈表內元素的個數,如果為1直接頭結點等於尾節點等於空,不為1的話就通過遍歷鏈表,從頭找到尾,確定尾部為空,倒第二為尾部。
    • remove:從列表中刪除某個元素
    • 數組:通過find方法的輔助可以確定刪除元素是否在隊列中以及索引值的位置,然後在此索引值後的元素依次前移,此前數組索引值最大的位置為空。
    • 鏈表:確定列表是否為空或僅有一個元素、元素是否在列表中、並處理刪除元素是列表的末尾元素、首元素、還是中間位置。如果是刪除僅有的元素就直接令頭結點等於尾結點等於空,刪除頭元素直接令頭結點等於頭結點的下一個,刪除尾元素就遍歷到倒數第二個為尾結點,刪除的是中間的元素就直接把前一個元素與後一個元素連接。
    • first:查看位於列表前端的元素
    • 數組:先判斷數組是否有元素,再直接輸出索引值為0的內容。
    • 鏈表:先判斷鏈表內是否有元素,再直接調出頭結點的內容。
    • last:查看位於列表末端的元素
    • 數組:先判斷數組是否有元素,再直接輸出索引值最大的內容。
    • 鏈表:先判斷鏈表內是否有元素,再直接調出尾結點的內容。
    • contains:確定列表是否含有某個元素
    • 數組:通過find方法輔助就可以(在一定程度上find方法和contains方法作用相同)。
    • 鏈表:在不為空的情況下,遍歷一遍鏈表找尋就可以。
    • isEmpty:判定列表是否為空
    • 數組:判斷計數變量是否為零,為零即列表為空。
    • 鏈表:頭結點等於尾結點等於空或是計數變量是否為零。
    • toString:
    • 數組:以計數變量的值為循環次數進行循環輸出。
    • 鏈表:從頭節點開始遍歷一遍到尾結點。
    • 關於數組列表的find方法:通過元素與數組內每一個索引下的內容的對比,來判斷索引值的方法。
    • 優點:可以使其他方法變得簡單易懂,也可以利用find方法來輔助其他一堆方法。
    • 數組實現的無序列表
      技術分享圖片
    • 鏈表實現的無序列表
      技術分享圖片
  • 問題3:i++i+1的區別
  • 問題3解決方案:在用數組實現remove方法的時候在移動過程中發現i++i+1的不同意義,雖然之前老師說過兩者意義不同,一直沒有在意,直到兩者在循環的時候才發現,如果用i++的話並不會刪除目標元素,相反會把數組的末尾元素刪掉;如果用i+1的話就會把刪除目標元素,數組的末尾元素不會被刪掉。我覺得是在第一次循環的時候,i++並沒有起到自增的作用,而是把i索引值內的元素又放到i索引值內,導致最後一次循環的時候尾部元素的索引值是最大的,被後面的賦空語句直接刪掉了。而i+1就在循環的第一次實現了自增,使得目標元素被刪掉,最大索引值和最大索引值減1存儲的都是一個元素,這樣在通過賦空語句就可以了。
    • 刪除“網絡空間安全系”的i++版:
      技術分享圖片
    • 刪除“網絡空間安全系”的i+1版:
      技術分享圖片
  • 問題4:PP6.17修改Course進行按系排序在按課程號排序
  • 問題4解決方案:相同類型的編程項目在上學期第七章就實現過,實現Comparable接口,很多同學因為沒有寫implements Comparable<>直接寫個方法完成而扣分,印象十分深刻。有了之前的經驗,在練習這次的就沒那麽難,之前的是進行一重比較,題目要求的是兩重比較,在系相同的情況下對課程號進行排序。直接把比較後的數值輸出,然後在(有序列表)添加的方法裏面融入比較的方法。但是,在上傳到碼雲的時候看了一下別人的代碼,覺得直接確定大小後輸出-1、1和0三個固定的數沒有差別,就沒有進行修改。在用數組和鏈表去實現的時候,發現用數組的方法就會報錯NullPointerException,找了半天也沒有發現錯誤,後來使用鏈表的方法就很成功的實現了,會繼續找數組的添加方法的bug的。
    技術分享圖片

上周考試錯題總結

  • 第三章和第四章

  • 錯題1:In an array implementation of a Stack, the array is ___________ causing the implementation to have to create a new larger array and copy the contents of the stack into the new array in order to expand the capacity of the stack.()
    • A .Dynamic
    • B .Static
    • C .Flexible
    • D .Polymorphic
  • 錯誤解析:擴容的方法是在原數組已滿的前提下更換新容量更大的數組,但是在未更換之前數組的容量一直保持不變。

  • 錯題2:A reference variable can refer to any object created from any class related to it by inheritance.
    • A .true
    • B .false
  • 錯誤解析:引用變量可以引用任何通過繼承過來與之相關的類創建的對象,就是子類與父類的關系即子類可以用父類的任何方法對象,但是父類不能用子類的方法對象。

  • 錯題3:By using the interface name as a return type, the interface doesn’t commit the method to the use of any particular class that implements a stack.
    • A .true
    • B .false
  • 錯誤解析:使用接口名作為返回類型,接口將不會把方法提交給任何實現堆棧的特定類。

  • 錯題4:The implementation of the collection operations should affect the way users interact with the collection.
    • A .true
    • B .false
  • 錯誤解析:集合的實現和用戶與集合的交互沒有影響。第三章和第四章無論使用數組還是鏈表進行維護集合以及維護集合相關的方法都對集合本身毫無影響。

  • 錯題5:Inherited variables and methods can be used in the derived class as if they had been declared locally.
    • A .true
    • B .false
  • 錯誤解析:繼承的變量和方法都可以在子類中應用,通過super、extends等保留字就可以。

  • 錯題6:A linked implementation of a stack adds and removes elements from the _______ of the linked list.
    • A .Front
    • B .Rear
    • C .Middle
    • D .None of the above
  • 錯題解析:堆棧的鏈表用頭和尾都可以當棧頂,但是用頭來充當的話會避免每次循環找到尾,再在尾部進行添加的麻煩。

  • 錯題7:Objects that are stored in a collection should contain implementation details of the underlying data structure.
    • A .true
    • B .false
  • 錯誤解析:存儲在集合中的對象應該包含底層數據結構的實現細節,集合內可以存儲任何內容。

  • 第五章

  • 錯題1:Which of the following operations of a queue examines the element at the front of the queue?
    • A .Enqueue
    • B .isEmpty
    • C .size
    • D .first
  • 錯誤解析:檢查隊列前面的元素用的是first方法,size是用來判斷隊列裏元素的個數。

  • 第三章和第四章的內容有一部分是上學期的內容,很多都不敢確定選項。第五章錯的錯的題目太可惜了,為了趕時間沒有再細細看一遍就提前交卷,結果就錯了一道特別特別簡單的題目。其實,錯題再看一遍就發現自己的選項錯哪了,都不知道自己當時為什麽選別的。

感悟

第六章的內容是用數組和鏈表表示列表的,在一定還曾獨上列表和隊列、棧都有一定程度上的相似,部分代碼完全可以之前編寫的,唯一覺得比較惡心的就是在添加的過程中就直接被排序。通過這幾章學的內容,對鏈表和數組有了更多的認識,應用起來也比較順手。勤能補拙,多聯系多嘗試總沒有錯的。

參考資料

  • Serializable接口

20172305 2018-2019-1 《Java軟件結構與數據結構》第四周學習總結