什麼應該考慮使可變類不可變?例如:我們仍然可以在不可變的堆棧類中使用push和pop方法嗎?或者我們應該簡單地刪除任何改變實例化對象狀態的方法?Mutable vs. Immutable
回答
的底線是:你最好能夠去除法修改從類實例化對象的狀態。但是如果你想保留它,那麼它應該創建一個與原始狀態不同的新對象並返回新對象。
這裏有一個更明確的答案:
不應該有任何的不可變類改變對象狀態的方法。
有很多void方法在可變類中改變這個對象的狀態。所以我們應該以返回新對象的方式改變它們的簽名,而不是改變「this」對象的狀態。
還有很多非void方法改變「this」對象的狀態並返回它們在「this」中改變的值。這些方法的簽名也應該以它們返回新對象的方式進行更改,而不是更改「this」的狀態。談到列表,通常還需要另一種方法(如「偷看」)才能獲得一定的價值。檢查樣品波紋管得到這是什麼意思:
查閱這些「推」和一個可變的堆棧類「流行」的方法:
public class Stack <E> {
…
public void push (E e) {
ensureCapacity(); // This method checks for capacity
elements[size++] = e;
}
這種方法在的頂部增加了一個新元素堆棧並以這種方式改變「this」對象的狀態。
public E pop() {
if (size == 0) throw new IllegalStateException("Stack.pop");
E result = elements[--size];
elements[size] = null;
return result;
}
…
}
該方法刪除堆棧頂部的元素並將其返回並通過刪除元素來更改「this」對象的狀態。
現在,假設我們需要改變這些方法來使這個堆棧不可變。我們首先處理「推」的方法:
「推」是一種無效的方法,通過添加一個新元素來改變「this」對象的狀態。爲了使堆棧不變,我們將創建一個類似於「本」和新元素添加到這個新的堆棧,然後返回一個新的堆棧:
public class ImmStack <E> {
...
/**
* this method pushes the item to a new object and keeps the original object unchanged
* @param e The item to be pushed
* @return A new list
* @PRE An original list object is required as well as an item to be pushed
* @POST A new list would be returned
*/
@SuppressWarnings({ "unchecked", "rawtypes" }) // All items in this.elements[] are of type E
public ImmStack<E> push (E e) {
ImmStack<E> stc = new ImmStack(getNewElements());
stc.elements=ensureCapacity(stc.elements);
stc.elements[size] = e;
stc.size = size +1;
return stc;
}
「流行」方法,通過改變「這個」對象的狀態刪除一個元素。爲了使類不可變的,我們將reate類似「這個」,從這個新的堆棧中刪除的元素,然後返回一個新的堆棧:
/**
* This pop method returns a new stack without the element at the top of the original list
* @return The new stack
* @POST The new stack would be returned
*/
@SuppressWarnings({ "unchecked", "rawtypes" }) // All items in this.elements[] are of type E
public ImmStack<E> pop() {
if (size == 0) throw new IllegalStateException("Stack.pop");
ImmStack<E> stc = new ImmStack(getNewElements());
stc.elements=ensureCapacity(stc.elements);
stc.elements[size-1] = null;
stc.size=size-1;
return stc;
}
舊的「流行」方法是在頂部返回的元素。我們還需要一種返回頂部元素以覆蓋此功能的新方法:
/**
* Returns item at front of queue without removing.
* @return item at front
* @throws java.util.NoSuchElementException if empty
*/
public E top()
{
if (this.isEmpty())
{
throw new NoSuchElementException("Queue underflow");
}
return elements[size-1];
}
這只是一個示例。你可能有更多的方法來改變你的類,使其不可變。
如果你的堆棧是不可變的,那麼根據定義它是不能改變的。 push()
和pop()
方法無法完成。
當某個方法無法成功完成時,可以拋出異常。當一個方法可以永不成功完成,拋出的標準異常是UnsupportedOperationException
。
例如:
public E[] push (E e) {
throw new UnsupportedOperationException();
}
編輯:
您注意到在評論你的push()方法只是返回堆棧的深層副本與新的元素。它看起來像是將不可變堆棧表示爲一個類的實例,並將所推入的堆棧表示爲一個數組。
您可以使用newElements.length
獲得newElements
引用的兩個數組之一的大小。所以,你可以寫這樣的代碼:
public E[] push (E e) {
E[] newElements=getNewElements();
int oldLength = newElements.length;
newElements=ensureCapacity(newElements);
int lastIndexInNewArray = oldLength;
newElements[ lastIndexInNewArray ] = e;
return newElements;
}
下面是C#中不可變堆棧的實現。
推動和彈出讓你回到一個全新的堆棧,並且Peek可以讓你在沒有彈出的情況下查看堆棧的頂部。
請注意,不需要複製整個堆棧。
這就是不可變結構在任何非平凡情況下的實現方式。在某些情況下,非平凡的不可變結構非常有用。海報說,這是不可能做到很多誤解。
原代碼和詳細信息可以在這裏找到:
https://blogs.msdn.microsoft.com/ericlippert/2007/12/04/immutability-in-c-part-two-a-simple-immutable-stack/
public interface IStack<T> : IEnumerable<T>
{
IStack<T> Push(T value);
IStack<T> Pop();
T Peek();
bool IsEmpty { get; }
}
public sealed class Stack<T> : IStack<T>
{
private sealed class EmptyStack : IStack<T>
{
public bool IsEmpty { get { return true; } }
public T Peek() { throw new Exception("Empty stack"); }
public IStack<T> Push(T value) { return new Stack<T>(value, this); }
public IStack<T> Pop() { throw new Exception("Empty stack"); }
public IEnumerator<T> GetEnumerator() { yield break; }
IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
}
private static readonly EmptyStack empty = new EmptyStack();
public static IStack<T> Empty { get { return empty; } }
private readonly T head;
private readonly IStack<T> tail;
private Stack(T head, IStack<T> tail)
{
this.head = head;
this.tail = tail;
}
public bool IsEmpty { get { return false; } }
public T Peek() { return head; }
public IStack<T> Pop() { return tail; }
public IStack<T> Push(T value) { return new Stack<T>(value, this); }
public IEnumerator<T> GetEnumerator()
{
for(IStack<T> stack = this; !stack.IsEmpty ; stack = stack.Pop())
yield return stack.Peek();
}
IEnumerator IEnumerable.GetEnumerator() {return this.GetEnumerator();}
}
- 1. mutable和immutable之間有什麼區別?
- 2. 返回R中Mutable或Immutable變量的函數
- 3. ember-data中的mutable array&immutable數組有什麼區別?
- 4. F#ref-mutable vars vs對象字段
- 5. 在Java中實現Immutable類
- 6. Mapbox iOS Mutable RMPolylineAnnotation
- 7. Scala Mutable Option?
- 8. Python mutable NamedTuple
- 9. Mutable NSHTTPURLResponse或NSURLResponse
- 10. Redux with Immutable JS
- 11. 什麼是語義含義:volatile-mutable與:unynchronized-mutable?
- 12. impl convert ::從for(mutable)reference
- 13. python:「mutable vectors are unshashable」error
- 14. Elementary Mutable Haskell哈希表
- 15. SWIG在課堂上忽略%mutable;如何解決?
- 16. Spying on immutable native方法
- 17. Immutable js地圖人口
- 18. 爲什麼java將字符串創建爲IMMUTABLE CLASS?
- 19. Rust中的immutable和const變量有什麼區別?
- 20. 字符串生成器VS串
- 21. 爲什麼[] mutable {}不能編譯?
- 22. 被迫使用mutable來讀取Excel ThisWorkbook.Names?
- 23. 爲什麼FSharp交互允許mutable讓?
- 24. Spark accumulableCollection不能與mutable配合使用
- 25. 轉換mutable爲不可變的地圖
- 26. Django mutable POST不會出現在cleared_data
- 27. 要添加的Python,爲什麼immutable變量更快?
- 28. immutable js修改所有嵌套記錄
- 29. @Immutable在groovy中如何工作?
- 30. seamless-immutable不是一直更新狀態
如果你可以把一個新元素進棧,它不是一成不變的。 –
如果你只修改'newElement'數組,爲什麼'this'的'size'會增長? –
你想改變一個_immutable_對象的狀態嗎?如何和爲什麼? – Tom