1. 程式人生 > 實用技巧 >資料結構與演算法-優先順序佇列

資料結構與演算法-優先順序佇列

優先順序佇列

概念

搜尋樹結構和詞典結構,都支援覆蓋資料全集的訪問和操作。也就是說,其中儲存的每一資料物件都可作為查詢和訪問目標
優先順序佇列,較之此前的資料結構反而有所削弱。具體地,這類結構將操作物件限定於當前的全域性極值者。而維護一個偏序關係

  • 介面
template <typename T> struct PQ {
	//優先順序佇列PQ模板類
	virtual void insert ( T ) = 0;
	//按照比較器確定的優先順序次序插入詞條
	virtual T getMax() = 0;
	//叏出優先順序最高的詞條
	virtual T delMax() = 0;
	//刪除除優先順序最高的詞條

};
  • 應用
/******************************************************************************************
2 * Huffman樹極造演算法:對傳入癿Huffman森枃forest逐步合幵,直刡成為一棵樹
3 ******************************************************************************************
4 * forest基亍優先順序佇列實現,此演算法適用亍符合PQ介面癿仸何實現斱式
5 * 為Huffman_PQ_List、Huffman_PQ_ComplHeap和Huffman_PQ_LeftHeap共用
6 * 編譯前對應工程叧需謳置相應標誌:DSA_PQ_List、DSA_PQ_ComplHeap戒DSA_PQ_LeftHeap
7 ******************************************************************************************/
HuffTree* generateTree ( HuffForest* forest ) {
	while ( 1 < forest->size() ) {
		HuffTree* s1 = forest->delMax();
		HuffTree* s2 = forest->delMax();
		HuffTree* s = new HuffTree();
		s->insertAsRoot ( Huffchar ( '^', s1->root()->data.weight + s2->root()->data.weight ) );
		s->attachAsLC ( s->root(), s1 );
		s->attachAsRC ( s->root(), s2 );
		forest->insert ( s );
		//將合幵後癿Huffman樹揑回Huffman森枃
	}
	HuffTree* tree = forest->delMax();
	//至此,森枃中癿最後一棵樹
	return tree;
	//即全尿Huffman編碼樹
}

完全二叉堆

  1. 什麼是堆
  • 它是一個完全二叉樹,即除了最後一層節點不是滿的,其他層節點都是滿的,即左右節點都有。

  • 它不是二叉搜尋樹,即左節點的值都比父節點值小,右節點的值都不比父節點值小,這樣查詢的時候,就可以通過二分的方式,效率是(log N)。

  • 它是特殊的二叉樹,它要求父節點的值不能小於子節點的值。這樣保證大的值在上面,小的值在下面。所以堆遍歷和查詢都是低效的,因為我們只知道從根節點到子葉節點的每條路徑都是降序的,但是各個路徑之間都是沒有聯絡的,查詢一個值時,你不知道應該從左節點查詢還是從右節點開始查詢。

  • 它可以實現快速的插入和刪除,效率都在(log N)左右。所以它可以實現優先順序佇列。

  1. 大頂堆與小頂堆
    堆序性也可對稱地約定為“堆頂以外的每個節點都不低(小)於其父節點”,此時同理,優先順序最低的詞條,必然始終處於堆頂位置。為以示區別,通常稱前(後)者為大(小)頂堆

  2. 模板類與一些常用方法

//必要的方法
int Parent(int i){
	//PQ[i]的父節點(floor((i-1)/2),i無論正負)
	return ((i - 1) >> 1);
}
int LChild(int i){
	//PQ[i]的左孩子
	return (1 + (i << 1));
}
int RChild(int i){
	//PQ[i]的右孩子
	return ((i + 1) << 1);
}
Boolean InHeap(int n,int i){
	//判斷PQ[i]是否合法
	return ((-1 < i) && (i < n));
}
Boolean ParentValid(int i){
	return (Parent(i) >= 0);
}
Boolean LChildValid(int n,int i){
	//判斷PQ[i]是否有一個(左)孩子
	return InHeap(n,LChild(i));
}
Boolean RChildValid(int n,int i){
	//判斷PQ[i]是否有兩個孩子
	return InHeap(n,RChild(i));
}
int Bigger(Vector<T> PQ,int i,int j){
	//取大者(等時前者優先)
	return (lt(PQ.elementAt(i),PQ.elementAt(j)) ? j : i);
}
int ProperParent(Vector<T> PQ,int n, int i){
	/*父子(至多)三者中的大者*/
	return (RChildValid(n,i) ? Bigger(PQ,Bigger(PQ,i,LChild(i)),RChild(i)) :
	                (LChildValid(n,i) ? Bigger(PQ,i,LChild(i)) : i));
}
//相等時父節點優先,如此可避免不必要的交換
Boolean lt(T a,T b){
	return ((int)a > (int)b);
}
void swap(T a,T b){
	T c = a;
	a = b;
	b = c;
}
void copyFrom(T[] A,int lo,int hi){
	int i = 0;
	while (lo < hi){
		this.set(i++, A[lo++]);
	}
}
int LastInternal(int n){
	return ((n + 1) / 2 - 1);
}

元素插入

  • 原理

  • 實現

@Override
    public void insert(T e) {
	//插入詞條
	this.addElement(e);
	percolateUp(elementCount - 1);
}
protected int percolateUp(int i){
	//上濾
	while (ParentValid(i)){
		int j = Parent(i);
		if (lt(elementAt(i),elementAt(j))) break;
		swap(elementAt(i),elementAt(j));
		i = j;
	}
	return i;
}
  • 例項

元素刪除

  • 原理

  • 實現

@Override
    public T delMax() {
	//刪除詞條
	T maxElem = elementAt(0);
	this.setElementAt(elementAt(elementCount - 1),0);
	this.remove(--elementCount);
	percolateDown(elementCount,0);
	return maxElem;
}
protected int percolateDown(int n,int i){
	//下濾
	int j = 0;
	while (i != (j = ProperParent(this,n,j))){
		swap(elementAt(i),elementAt(j));
		i = j;
	}
	return i;
}
  • 例項

批量建堆

  • 原理

  • 實現

public  PQ_CompHeap(T[] A, int n){
	//批量構造
	copyFrom(A,0,n);
	heapify(n);
}
protected void heapify(int n){
	//Floyd建堆演算法
	for (int i = LastInternal(n);i >= 0;i--){
		//自下而上,依次
		percolateDown(n,i);
		//下濾各內部節點
	}
}
protected int percolateDown(int n,int i){
	//下濾
	int j = 0;
	while (i != (j = ProperParent(this,n,j))){
		swap(elementAt(i),elementAt(j));
		i = j;
	}
	return i;
}
  • 例項

就地堆排序

  • 原理

  • 實現

//堆排序
public void heapSort(T[] A,int lo,int hi){
	copyFrom(A,lo,hi);
	int n = hi - lo;
	heapify(n);
	while (0 < --n){
		swap(A[0],A[n]);
		percolateDown(n,0);
	}
}
  • 例項

左視堆

概念

對於堆的合併來說,可以想到的有兩種方法:

  • 簡單的取出並插入

  • 將兩個堆中詞條視為無順序的,用堆合併

  • 堆不需要與二叉樹一樣保持平衡
    左視堆:左式堆是優先順序佇列的另一實現方式,可高效地支援堆合併操作。其基本思路是:在保持堆序性的前提下附加新的條件,使得在堆的合併過程中,只需調整很少量的節點。具體地,需參與調整的節點不超過O(logn)個,故可達到極高的效率。

模板類

使用的是二叉樹結構

空節點路徑長度

左傾性

最右側通路

package com.atguigu.domin;
import com.atguigu.self.BinNode;
import com.atguigu.self.BinTree;
/**
 * @anthor shkstart
 * @create 2020-08-13 15:07
 */
public class PQ_LeftHeap<T> extends BinTree<T> implements PQ<T> {
	@Override
	    public void insert(T e) {
		//插入元素
		BinNode<T> v = new BinNode<T>(e);
		_root = merge(_root,v);
		_root.parent = null;
		_size++;
	}
	@Override
	    public T getMax() {
		//取出優先順序最高的元素
		return _root.data;
	}
	@Override
	    public T delMax() {
		//刪除優先順序最高的元素
		BinNode<T> lHeap = _root.lc;
		//左子堆
		BinNode<T> rHeap = _root.rc;
		//右子堆
		T e = _root.data;
		//備份堆頂處的最大元素
		_size--;
		//刪除一個節點
		_root = merge(lHeap,rHeap);
		//原左右堆合併
		if (_root != null){
			//更新父子連結
			_root.parent = null;
		}
		return e;
		//返回源節點處資料
	}
	public BinNode<T> merge(BinNode<T> a, BinNode<T> b){
		if (a ==null) return b;
		if (b == null) return a;
		if (lt(a.data,b.data)) swap(b,a);
		a.rc = merge(a.rc,b);
		a.rc.parent = a;
		if (a.rc == null || a.rc.npl < a.rc.npl){
			swap(a.lc,a.rc);
		}
		a.npl = (a.rc != null) ? a.rc.npl + 1:1;
		return a;
	}
	//一些必要的方法
	void swap(BinNode<T> a,BinNode<T> b){
		BinNode<T> c = a;
		a = b;
		b = c;
	}
	Boolean lt(T a,T b){
		return ((int)a < (int)b);
	}
}

合併

  • 原理

  • 實現
public BinNode<T> merge(BinNode<T> a, BinNode<T> b){
	if (a ==null) return b;
	if (b == null) return a;
	if (lt(a.data,b.data)) swap(b,a);
	a.rc = merge(a.rc,b);
	a.rc.parent = a;
	if (a.rc == null || a.rc.npl < a.rc.npl){
		swap(a.lc,a.rc);
	}
	a.npl = (a.rc != null) ? a.rc.npl + 1:1;
	return a;
}
//一些必要的方法
void swap(BinNode<T> a,BinNode<T> b){
	BinNode<T> c = a;
	a = b;
	b = c;
}
Boolean lt(T a,T b){
	return ((int)a < (int)b);
}
  • 例項

基於合併的插入和刪除

  • 原理

  • 實現
@Override
    public void insert(T e) {
	//插入元素
	BinNode<T> v = new BinNode<T>(e);
	_root = merge(_root,v);
	_root.parent = null;
	_size++;
}
@Override
    public T getMax() {
	//取出優先順序最高的元素
	return _root.data;
}
@Override
    public T delMax() {
	//刪除優先順序最高的元素
	BinNode<T> lHeap = _root.lc;
	//左子堆
	BinNode<T> rHeap = _root.rc;
	//右子堆
	T e = _root.data;
	//備份堆頂處的最大元素
	_size--;
	//刪除一個節點
	_root = merge(lHeap,rHeap);
	//原左右堆合併
	if (_root != null){
		//更新父子連結
		_root.parent = null;
	}
	return e;
	//返回源節點處資料
}
  • 例項

錦標賽排序

原始演算法

  • 原理

  • 實現

相關連結

https://www.cnblogs.com/binarylei/p/10115921.html#24-堆排序-vs-快速排序

package com.atguigu.domin;
/**
 * @anthor shkstart
 * @create 2020-08-13 16:31
 */
public class TourSort extends PQ_CompHeap{
	public  static final int MAX = 10000;
	private class Node//用node來儲存競賽排序過程中的節點,包括裡面的資料和資料在陣列中的ID
	{
		public int data;
		public int id;
		public Node()
		        {
		}
		public Node(int data, int id) {
			this.data = data;
			this.id = id;
		}
	}
	public void Adjust(Node[] B, int idx){
		//當去除最小元素以後,我們要調整陣列
		while (idx != 0){
			if (idx / 2 == 0){
				if (B[idx].id == -1){
					B[Parent(idx)] = B[idx + 1];
				} else {
					B[Parent(idx)] = B[idx].data > B[idx+1].data ? B[idx+1] : B[idx];
				}
			} else {
				if (B[idx].id == -1){
					B[Parent(idx)] = B[idx - 1];
				} else {
					B[Parent(idx)] = B[idx].data > B[idx-1].data ? B[idx-1] : B[idx];
				}
			}
		}
	}
	public void creat(int[] A) {
		int n = 1;
		while (n < A.length){
			n *= 2;
		}
		int s = n - 1;
		Node[] B = new Node[2*n - 1];
		for (int i = 0;i < n;i++){
			if (i < A.length){
				B[n - 1 + i].data = A[i];
				B[n - 1 + i].id =n - 1 + i;
			} else {
				B[n - 1 + i].data = MAX;
				B[n - 1 + i].id = -1;
			}
		}
		while (--s >= 0){
			if (B[LChild(s)].id == (-1)) {
				B[s] = B[RChild(s)];
			} else if (B[RChild(s)].id == (-1)) {
				B[s] = B[LChild(s)];
			} else {
				B[s] = (B[LChild(s)].data < B[RChild(s)].data) ? B[LChild(s)] : B[RChild(s)];
			}
		}
		for ( int i = 0; i < A.length; i++)//實際排序的過程
		{
			A[i] = B[0].data;
			//取出最小的
			B[B[0].id].id = -1;
			Adjust(B, B[0].id);
		}
	}
}

改進演算法

  • 原理

  • 實現(還未寫)

多叉堆

  • 原理

  • 效率

  • 實現(還未寫)

相關連結

HashTable解析(先寫到這裡,等hashmap看了整理到一起)


package java.util;

import java.io.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.BiFunction;

/*

和HashMap一樣,Hashtable 也是一個散列表,它儲存的內容是鍵值對(key-value)對映。
Hashtable 繼承於Dictionary,實現了Map、Cloneable、java.io.Serializable介面。
Hashtable 的函式都是同步的,這意味著它是執行緒安全的。它的key、value都不可以為null。
此外,Hashtable中的對映不是有序的。
*/

public class Hashtable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable {

    /**
     * hashtable中的資料
     */
    private transient Entry<?,?>[] table;

    /**
     * 表中的元素的實際數量
     */
    private transient int count;

    /**
     * 閾值,判斷是否需要調整容量
     */
    private int threshold;

    /**
     * 載入因子,一般0.75
	 */
    private float loadFactor;

    /**
     *對於表的操作次數,可以判斷
	 *是否有併發執行緒對其進行改動,並返回錯誤
     */
    private transient int modCount = 0;

    /**
	*版本號
	*/
    private static final long serialVersionUID = 1421746759512286392L;

    /**
     * 構造器:有初始容量和載入因子
     */
    public Hashtable(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal Load: "+loadFactor);

        if (initialCapacity==0)
            initialCapacity = 1;//以上是對極端情況的調整判斷
        this.loadFactor = loadFactor;
        table = new Entry<?,?>[initialCapacity];
        threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);//閾值大小
    }

    /**
     * 構造器:載入因子為0.75預設,有初始容量
     */
    public Hashtable(int initialCapacity) {
        this(initialCapacity, 0.75f);
    }

    /**
     * 構造器:載入因子0.75,初始容量11
     */
    public Hashtable() {
        this(11, 0.75f);
    }

    /**
     * 構造器:有Map對映,構造器初始容量大於對映的兩倍
     */
    public Hashtable(Map<? extends K, ? extends V> t) {
        this(Math.max(2*t.size(), 11), 0.75f);
        putAll(t);
    }

    /**
     * 最大容量為int最大值-8
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * 動態改變容量大小
     */
    @SuppressWarnings("unchecked")
    protected void rehash() {
        int oldCapacity = table.length;//記錄表的老容量
        Entry<?,?>[] oldMap = table;//記錄表的資料

        // overflow-conscious code
        int newCapacity = (oldCapacity << 1) + 1;//判斷新的容量與最大值的關係
        if (newCapacity - MAX_ARRAY_SIZE > 0) {
            if (oldCapacity == MAX_ARRAY_SIZE)//判斷老的容量與最大值的關係
                return;//相等就結束
            newCapacity = MAX_ARRAY_SIZE;
        }
        Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];

        modCount++;//操作次數增加
        threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);//設定閾值
        table = newMap;

        for (int i = oldCapacity ; i-- > 0 ;) {//每一個位置的每一條鏈
            for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
                Entry<K,V> e = old;
                old = old.next;//一條鏈的下一個連結

                int index = (e.hash & 0x7FFFFFFF) % newCapacity;//從新計算引用位置
                e.next = (Entry<K,V>)newMap[index];//將這個與上一個連結
                newMap[index] = e;//賦值
            }
        }
    }

    /**
     * 刪除某個詞條
     */
    public synchronized V remove(Object key) {
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>)tab[index];//通過key計算hash值並進一步確定位置
        for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                modCount++;
                if (prev != null) {
                    prev.next = e.next;
                } else {
                    tab[index] = e.next;//將key對應的詞條的上和下連結起來
                }
                count--;//數量減1
                V oldValue = e.value;
                e.value = null;//對應處的數值改為Null
                return oldValue;
            }
        }
        return null;
    }

    /**
     * 複製某一Map的所有詞條至表中
     */
    public synchronized void putAll(Map<? extends K, ? extends V> t) {
        for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
            put(e.getKey(), e.getValue());
    }


    // Views

    /**
     * 所有的key,entry,values都要加一個鎖,保證不發生併發改變的問題
     */
    private transient volatile Set<K> keySet;
    private transient volatile Set<Map.Entry<K,V>> entrySet;
    private transient volatile Collection<V> values;

    /**
     * 
     */
    public Set<K> keySet() {
        if (keySet == null)
            keySet = Collections.synchronizedSet(new KeySet(), this);
        return keySet;
    }

    private class KeySet extends AbstractSet<K> {
        public Iterator<K> iterator() {
            return getIterator(KEYS);
        }
        public int size() {
            return count;
        }
        public boolean contains(Object o) {
            return containsKey(o);
        }
        public boolean remove(Object o) {
            return Hashtable.this.remove(o) != null;
        }
        public void clear() {
            Hashtable.this.clear();
        }
    }

    /**
     * 
     */
    public Set<Map.Entry<K,V>> entrySet() {
        if (entrySet==null)
            entrySet = Collections.synchronizedSet(new EntrySet(), this);
        return entrySet;
    }

    private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
        public Iterator<Map.Entry<K,V>> iterator() {
            return getIterator(ENTRIES);
        }

        public boolean add(Map.Entry<K,V> o) {
            return super.add(o);
        }

        public boolean contains(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> entry = (Map.Entry<?,?>)o;
            Object key = entry.getKey();
            Entry<?,?>[] tab = table;
            int hash = key.hashCode();
            int index = (hash & 0x7FFFFFFF) % tab.length;

            for (Entry<?,?> e = tab[index]; e != null; e = e.next)
                if (e.hash==hash && e.equals(entry))
                    return true;
            return false;
        }

        public boolean remove(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> entry = (Map.Entry<?,?>) o;
            Object key = entry.getKey();
            Entry<?,?>[] tab = table;
            int hash = key.hashCode();
            int index = (hash & 0x7FFFFFFF) % tab.length;

            @SuppressWarnings("unchecked")
            Entry<K,V> e = (Entry<K,V>)tab[index];
            for(Entry<K,V> prev = null; e != null; prev = e, e = e.next) {
                if (e.hash==hash && e.equals(entry)) {
                    modCount++;
                    if (prev != null)
                        prev.next = e.next;
                    else
                        tab[index] = e.next;

                    count--;
                    e.value = null;
                    return true;
                }
            }
            return false;
        }

        public int size() {
            return count;
        }

        public void clear() {
            Hashtable.this.clear();
        }
    }

    /**
     * 
     */
    public Collection<V> values() {
        if (values==null)
            values = Collections.synchronizedCollection(new ValueCollection(),
                                                        this);
        return values;
    }

    private class ValueCollection extends AbstractCollection<V> {
        public Iterator<V> iterator() {
            return getIterator(VALUES);
        }
        public int size() {
            return count;
        }
        public boolean contains(Object o) {
            return containsValue(o);
        }
        public void clear() {
            Hashtable.this.clear();
        }
    }




    /**
     * 判等
     */
    public synchronized boolean equals(Object o) {
        if (o == this)//正好相等
            return true;

        if (!(o instanceof Map))//型別不一致
            return false;
        Map<?,?> t = (Map<?,?>) o;
        if (t.size() != size())
            return false;

        try {//判斷不相等
            Iterator<Map.Entry<K,V>> i = entrySet().iterator();
            while (i.hasNext()) {
                Map.Entry<K,V> e = i.next();
                K key = e.getKey();
                V value = e.getValue();
                if (value == null) {
                    if (!(t.get(key)==null && t.containsKey(key)))//不存在相同key值
                        return false;
                } else {
                    if (!value.equals(t.get(key)))//key對應的位置存在但值不相等
                        return false;
                }
            }
        } catch (ClassCastException unused)   {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }

        return true;
    }

    /**
     * 返回hashcode值
     */
    public synchronized int hashCode() {
        
        int h = 0;
        if (count == 0 || loadFactor < 0)
            return h;  // 空

        loadFactor = -loadFactor;  // 載入因子改為負,作標記,不能被其他程序更改
        Entry<?,?>[] tab = table;
        for (Entry<?,?> entry : tab) {
            while (entry != null) {
                h += entry.hashCode();//hashcode的計算方式:相加該位置所有連結的hash值
                entry = entry.next;
            }
        }

        loadFactor = -loadFactor;  

        return h;
    }

   

   

	/*
	*按照key,value刪除
	*/
    @Override
    public synchronized boolean remove(Object key, Object value) {
        Objects.requireNonNull(value);

        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>)tab[index];
        for (Entry<K,V> prev = null; e != null; prev = e, e = e.next) {
            if ((e.hash == hash) && e.key.equals(key) && e.value.equals(value)) {
                modCount++;
                if (prev != null) {
                    prev.next = e.next;
                } else {
                    tab[index] = e.next;
                }
                count--;
                e.value = null;
                return true;
            }
        }
        return false;
    }



   

  

    /**
     * 通過流儲存檔案
     */
    private void writeObject(java.io.ObjectOutputStream s)
            throws IOException {
        Entry<Object, Object> entryStack = null;

        synchronized (this) {
            
            s.defaultWriteObject();

            s.writeInt(table.length);
            s.writeInt(count);

            for (int index = 0; index < table.length; index++) {
                Entry<?,?> entry = table[index];

                while (entry != null) {
                    entryStack =
                        new Entry<>(0, entry.key, entry.value, entryStack);
                    entry = entry.next;
                }
            }
        }

        while (entryStack != null) {
            s.writeObject(entryStack.key);
            s.writeObject(entryStack.value);
            entryStack = entryStack.next;
        }
    }


    /**
     * 詞條
     */
    private static class Entry<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Entry<K,V> next;

        protected Entry(int hash, K key, V value, Entry<K,V> next) {
            this.hash = hash;
            this.key =  key;
            this.value = value;
            this.next = next;
        }

        @SuppressWarnings("unchecked")
        protected Object clone() {
            return new Entry<>(hash, key, value,
                                  (next==null ? null : (Entry<K,V>) next.clone()));
        }

        // Map.Entry Ops

        public K getKey() {
            return key;
        }

        public V getValue() {
            return value;
        }

        public V setValue(V value) {
            if (value == null)
                throw new NullPointerException();

            V oldValue = this.value;
            this.value = value;
            return oldValue;
        }

        public boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;

            return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
               (value==null ? e.getValue()==null : value.equals(e.getValue()));
        }

        public int hashCode() {
            return hash ^ Objects.hashCode(value);
        }

        public String toString() {
            return key.toString()+"="+value.toString();
        }
    }
}