Hashtable源码分析

1.概述

      Hashtable实现了一个Hash表,并由此将键(Key)映射为与之相对应的值(Value)。任何非null的对象都可以作为该Hash表的键或者值。为了能够实现在Hash表中存储和获取对象,用作键的对象必须实现hashCode方法和equals方法。Hashtable的抽象父类Dictionary定义了对Hash表操作的基本方法:

    abstract public int size();
    abstract public boolean isEmpty();
    abstract public Enumeration<K> keys();
    abstract public Enumeration<V> elements();
    abstract public V get(Object key);
    abstract public V put(K key, V value);
    abstract public V remove(Object key);

但在JDK1.2之后Dictionary就再见了,如果需要实现Hash表则不再使用继承Dictionary的方式,转而采用实现Map接口的方式。Hashtable也被改进以实现Map接口,使之和Java Collections Framework的整体设计相同,但与新的实现不同之处在于, Hashtable是同步的,感觉这好像也是Hashtable仍没有被彻底丢弃的唯一能说的过去的理由了(虽然几乎没有怎么见到过用Hashtable),因为貌似其他的区别之处大部分都是因为历史遗留原因了。

2.快速失败

      在JDK的文档中很多与Collection迭代相关的部分都提到了快速失败(fail-fast),其主要是针对非并发集合(HashMap,Hashtable),在对非并发集合进行迭代时,collection的iterator返回的迭代器是快速失败的,即在创建了Iterator之后,如果从结构上对collection进行修改(增减),除非通过Iterator自身的remove方法,否则在任何时间任何方式对其进行修改都会抛出ConcurrentModificationException。就是说面对并发的修改Iterator很快就会完全失败或者说是立即抛出异常,而不是等到迭代完成或在某个不确定的时间点出现错误时才让你知道,就是说不冒任何风险。

      Hashtable 的键和元素方法返回的 Enumeration 不是快速失败的

3.拉链法

         拉链法解决冲突的做法是:将所有关键字为同义词的结点链接在同一个单链表中。若选定的散列表长度为m,则可将散列表定义为一个由m个头指针组成的指针数组T[0..m-1]。凡是散列地址为i的结点,均插入到以T[i]为头指针的单链表中。T中各分量的初值均应为空指针。在拉链法中,装填因子α可以大于1,但一般均取α≤1。

4.源码注释

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

	/*
	 * Hashtable保存的是key-value的数组Hashtbale是采用拉链法实现的,每一个Entry本质上是一个单向链表
	 * 拉链法的本质:当散列表冲突时 ,我们重开一条链表。transient 序列化时忽略该字段。
	 * 数组存放的是实体的引用,序列化时必须遍历该字段逐个实体序列化。
	 */
	private transient Entry[] table;

	//Hashtable的容量大小
	private transient int count;

	//Hashtable的门限值,用于判断是否对Hashtable扩容
	private int threshold;

	//加载因子=容量*加载因子
	private float loadFactor;

	//Hashtable被改变次数的计数器
	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);
		//如果容量==1,将其置为1
		if (initialCapacity == 0)
			initialCapacity = 1;
		//初始化加载因子、存储的数组、门限值
		this.loadFactor = loadFactor;
		table = new Entry[initialCapacity];
		threshold = (int) (initialCapacity * loadFactor);
	}

	//带有默认加载因子的构造函数
	public Hashtable(int initialCapacity) {
		this(initialCapacity, 0.75f);
	}

	//带有默认容量与加载因子的构造函数,容量默认11,加载因子0.75
	public Hashtable() {
		this(11, 0.75f);
	}

	//带有初始map的构造函数
	public Hashtable(Map<? extends K, ? extends V> t) {
		this(Math.max(2 * t.size(), 11), 0.75f);
		//将map全部存入Hashtable中
		putAll(t);
	}

	//返回Hashtable的大小
	public synchronized int size() {
		return count;
	}

	//判断Hashtable是否为空
	public synchronized boolean isEmpty() {
		return count == 0;
	}

	//返回Hashtable中所有键key的枚举对象
	public synchronized Enumeration<K> keys() {
		return this.<K> getEnumeration(KEYS);
	}

	//返回Hashtable中所有值value的枚举对象
	public synchronized Enumeration<V> elements() {
		return this.<V> getEnumeration(VALUES);
	}

	/*
	 * 判断某个值是否存在于该Hashtable中,其中table = new
	 * Entry[initialCapacity],是Hashtable的存储数组,当出现碰撞时则以链表的形式进行存储,
	 * 所以在测试此映射表中是否存在与指定值关联的键时需要对整个Hashtable进行遍历,直到找到为止。
	 */
	public synchronized boolean contains(Object value) {
		if (value == null) {
			throw new NullPointerException();
		}

		Entry tab[] = table;
		for (int i = tab.length; i-- > 0;) {
			for (Entry<K, V> e = tab[i]; e != null; e = e.next) {
				if (e.value.equals(value)) {
					return true;
				}
			}
		}
		return false;
	}

	//判断某个值是否存在于该Hashtable中,实现Map接口
	public boolean containsValue(Object value) {
		return contains(value);
	}

	/*
	 * 测试指定对象是否为此哈希表中的键,可以看到在Hashtable中使用的hashcode为该对象原有的hashcode的值, hash &
	 * 0x7FFFFFFF是一个比较常见的将hashcode限制到31位的方法,对该对象是该Hashtable的键时返回true
	 */
	public synchronized boolean containsKey(Object key) {
		Entry tab[] = table;
		int hash = key.hashCode();
		// 取得该hashcode对应的index值
		int index = (hash & 0x7FFFFFFF) % tab.length;
		for (Entry<K, V> e = tab[index]; e != null; e = e.next) {
			if ((e.hash == hash) && e.key.equals(key)) {
				return true;
			}
		}
		return false;
	}

	/*
	 * 返回指定键所映射到的值,如果此映射不包含此键的映射,则返回 null,实现方法与containsKey相同,
	 * 只是在返回时返回该键对应的值或者返回null。
	 */
	public synchronized V get(Object key) {
		Entry tab[] = table;
		int hash = key.hashCode();
		int index = (hash & 0x7FFFFFFF) % tab.length;
		for (Entry<K, V> e = tab[index]; e != null; e = e.next) {
			if ((e.hash == hash) && e.key.equals(key)) {
				return e.value;
			}
		}
		return null;
	}
	
	/*
	 * 当Hashtable中的键的数量超出哈希表的容量和加载因子时,自动调用该方法,增加Hashtable的容量, 并对该Hashtable进行重组。
	 */
	protected void rehash() {
		int oldCapacity = table.length;
		Entry[] oldMap = table;
		// 新容量是旧容量的大小*2+1,默认初始容量大小是11。
		int newCapacity = oldCapacity * 2 + 1;
		Entry[] newMap = new Entry[newCapacity];
		// 用来记录Hashtable在结构上被修改的次数
		modCount++;
		// 设定新的门限值容量*0.75(默认)
		threshold = (int) (newCapacity * loadFactor);
		table = newMap;
		// 对整个Hashtable进行重组,即对Hashtable中的每个桶(单链表)重新计算其新的存储位置
		for (int i = oldCapacity; i-- > 0;) {
			for (Entry<K, V> old = oldMap[i]; old != null;) {
				Entry<K, V> e = old;
				old = old.next;

				int index = (e.hash & 0x7FFFFFFF) % newCapacity;
				e.next = newMap[index];
				newMap[index] = e;
			}
		}
	}

	/*
	 * 根据所给定的键key,将其对应的值映射到Hashtable中的对应位置。
	 */
	public synchronized V put(K key, V value) {
		// 如果值为空则抛出NullPointerException
		if (value == null) {
			throw new NullPointerException();
		}
		// 根据key计算其对应的映射位置,并判断在Hashtable中是否存在该键key,如果存在则更新相应的值value,并返回原有的值value
		Entry tab[] = table;
		int hash = key.hashCode();
		int index = (hash & 0x7FFFFFFF) % tab.length;
		for (Entry<K, V> e = tab[index]; e != null; e = e.next) {
			if ((e.hash == hash) && e.key.equals(key)) {
				V old = e.value;
				e.value = value;
				return old;
			}
		}
		// Hashtable的修改记录+1
		modCount++;
		// 如果该Hashtable的存储容量已经大于或者等于门限值,则对其进行rehash操作,并重新计算本次需要put的键key的index
		if (count >= threshold) {
			// Rehash the table if the threshold is exceeded
			rehash();
			tab = table;
			index = (hash & 0x7FFFFFFF) % tab.length;
		}
		// 将Hashtable中index位置的Entry(链表)保存到e中
		Entry<K, V> e = tab[index];
		// 将新的Entry放在链表最前并存在index位置
		tab[index] = new Entry<K, V>(hash, key, value, e);
		// 容量+1
		count++;
		return null;
	}

	//删除给定键key对象对应的元素(键与值)
	public synchronized V remove(Object key) {
		Entry tab[] = table;
		int hash = key.hashCode();
		//计算index
		int index = (hash & 0x7FFFFFFF) % tab.length;
		//遍历查找对应的链表
		for (Entry<K, V> e = tab[index], 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;
				}
				count--;
				V oldValue = e.value;
				e.value = null;
				return oldValue;
			}
		}
		return null;
	}

	//将map里的键与值全部存入Hashtable中
	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());
	}

	//清空Hashtable
	public synchronized void clear() {
		Entry tab[] = table;
		modCount++;
		for (int index = tab.length; --index >= 0;)
			tab[index] = null;
		count = 0;
	}

	//clone该Hashtable,以Object形式返回。
	public synchronized Object clone() {
		try {
			Hashtable<K, V> t = (Hashtable<K, V>) super.clone();
			t.table = new Entry[table.length];
			for (int i = table.length; i-- > 0;) {
				t.table[i] = (table[i] != null) ? (Entry<K, V>) table[i]
						.clone() : null;
			}
			t.keySet = null;
			t.entrySet = null;
			t.values = null;
			t.modCount = 0;
			return t;
		} catch (CloneNotSupportedException e) {
			// this shouldn't happen, since we are Cloneable
			throw new InternalError();
		}
	}

	//重写toString方法
	public synchronized String toString() {
		int max = size() - 1;
		if (max == -1)
			return "{}";

		StringBuilder sb = new StringBuilder();
		Iterator<Map.Entry<K, V>> it = entrySet().iterator();

		sb.append('{');
		for (int i = 0;; i++) {
			Map.Entry<K, V> e = it.next();
			K key = e.getKey();
			V value = e.getValue();
			sb.append(key == this ? "(this Map)" : key.toString());
			sb.append('=');
			sb.append(value == this ? "(this Map)" : value.toString());

			if (i == max)
				return sb.append('}').toString();
			sb.append(", ");
		}
	}

	//获取Hashtable的枚举对象,如果Hashtable为空则返回空枚举对象
	private <T> Enumeration<T> getEnumeration(int type) {
		if (count == 0) {
			return (Enumeration<T>) emptyEnumerator;
		} else {
			return new Enumerator<T>(type, false);
		}
	}

	//获取Hashtable的Iterator对象,如果Hashtable为空则返回空Iterator对象
	private <T> Iterator<T> getIterator(int type) {
		if (count == 0) {
			return (Iterator<T>) emptyIterator;
		} else {
			return new Enumerator<T>(type, true);
		}
	}

	//Hashtable的key集合,使用set存储,不存在重复key
	private transient volatile Set<K> keySet = null;
	//Hashtable的key-value键值对集合,使用set存储,不存在重复元素
	private transient volatile Set<Map.Entry<K, V>> entrySet = null;
	//Hashtable的value集合,使用Collection存储,可以重复
	private transient volatile Collection<V> values = null;

	/*
	 * 使用Collections.synchronizedSet返回一个synchronized的keySet对象,以实现对线程同步,
	 * synchronizedSet对keySet的所有方法都添加synchronized
	 */
	public Set<K> keySet() {
		if (keySet == null)
			keySet = Collections.synchronizedSet(new KeySet(), this);
		return keySet;
	}

	//Hashtable的key集合,继承自AbstractSet
	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();
		}
	}

	/*
	 * 使用Collections.synchronizedSet返回一个synchronized的entrySet对象,以实现对线程同步,
	 * synchronizedSet对entrySet的所有方法都添加synchronized
	 */
	public Set<Map.Entry<K, V>> entrySet() {
		if (entrySet == null)
			entrySet = Collections.synchronizedSet(new EntrySet(), this);
		return entrySet;
	}

	//Hashtable的key-value键值对集合,继承自AbstractSet,其中键值对形式的元素不会重复
	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<K, V> entry = (Map.Entry<K, V>) o;
			K key = entry.getKey();
			Entry[] tab = table;
			int hash = key.hashCode();
			int index = (hash & 0x7FFFFFFF) % tab.length;

			for (Entry<K, V> e = tab[index], 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();
		}
	}

	/*
	 * 使用Collections.synchronizedSet返回一个synchronized的values对象,以实现对线程同步,
	 * synchronizedSet对values的所有方法都添加synchronized
	 */
	public Collection<V> values() {
		if (values == null)
			values = Collections.synchronizedCollection(new ValueCollection(),
					this);
		return values;
	}

	//Hashtable的values集合,继承自AbstractCollection
	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();
		}
	}

	//重写equals方法,如果两个Hashtable的所有key-value元素都相等则认为两个Hashtable相等
	public synchronized boolean equals(Object o) {
		if (o == this)
			return true;

		if (!(o instanceof Map))
			return false;
		Map<K, V> t = (Map<K, V>) o;
		if (t.size() != size())
			return false;
		//从Hashtable中遍历,判断每一个key是否在与之比较的Hashtable中存在,并且是否value也相等
		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)))
						return false;
				} else {
					if (!value.equals(t.get(key)))
						return false;
				}
			}
		} catch (ClassCastException unused) {
			return false;
		} catch (NullPointerException unused) {
			return false;
		}

		return true;
	}

	//重写hashCode方法
	public synchronized int hashCode() {
		int h = 0;
		//如果Hashtable大小为0,或者加载因子小于0,返回0
		if (count == 0 || loadFactor < 0)
			return h; // Returns zero

		loadFactor = -loadFactor; // Mark hashCode computation in progress
		Entry[] tab = table;
		//返回Hashtable中的每个Entry的key和value的异或值 的总和
		for (int i = 0; i < tab.length; i++)
			for (Entry e = tab[i]; e != null; e = e.next)
				h += e.key.hashCode() ^ e.value.hashCode();
		loadFactor = -loadFactor; // Mark hashCode computation complete
		return h;
	}

	//java.io.Serializable的写入方法
	private synchronized void writeObject(java.io.ObjectOutputStream s)
			throws IOException {
		// Write out the length, threshold, loadfactor
		s.defaultWriteObject();

		// Write out length, count of elements and then the key/value objects
		//写入Hashtable的总容量
		s.writeInt(table.length);
		//写入Hashtable的实际大小
		s.writeInt(count);
		//写入所有的Entry
		for (int index = table.length - 1; index >= 0; index--) {
			Entry entry = table[index];

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

	//java.io.Serializable的读取函数:根据写入方式读出
	private void readObject(java.io.ObjectInputStream s) throws IOException,
			ClassNotFoundException {
		// Read in the length, threshold, and loadfactor
		s.defaultReadObject();

		// Read the original length of the array and number of elements
		//读入总容量
		int origlength = s.readInt();
		//读入实际大小
		int elements = s.readInt();

		// Compute new size with a bit of room 5% to grow but
		// no larger than the original size. Make the length
		// odd if it's large enough, this helps distribute the entries.
		// Guard against the length ending up zero, that's not valid.
		int length = (int) (elements * loadFactor) + (elements / 20) + 3;
		if (length > elements && (length & 1) == 0)
			length--;
		if (origlength > 0 && length > origlength)
			length = origlength;

		Entry[] table = new Entry[length];
		count = 0;

		// Read the number of elements and then all the key/value objects
		for (; elements > 0; elements--) {
			K key = (K) s.readObject();
			V value = (V) s.readObject();
			// synch could be eliminated for performance
			reconstitutionPut(table, key, value);
		}
		this.table = table;
	}

	//重建Entry[]数组,主要是供readObject方法使用
	private void reconstitutionPut(Entry[] tab, K key, V value)
			throws StreamCorruptedException {
		if (value == null) {
			throw new java.io.StreamCorruptedException();
		}
		// Makes sure the key is not already in the hashtable.
		// This should not happen in deserialized version.
		int hash = key.hashCode();
		int index = (hash & 0x7FFFFFFF) % tab.length;
		for (Entry<K, V> e = tab[index]; e != null; e = e.next) {
			if ((e.hash == hash) && e.key.equals(key)) {
				throw new java.io.StreamCorruptedException();
			}
		}
		// Creates the new entry.
		Entry<K, V> e = tab[index];
		tab[index] = new Entry<K, V>(hash, key, value, e);
		count++;
	}

	//Entry元素类,其实质上是一个单链表
	private static class Entry<K, V> implements Map.Entry<K, V> {
		int hash;
		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;
		}

		protected Object clone() {
			return new Entry<K, V>(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;
		}

		//重写equals方法,如果两个元素的key与value都相等则认为他们相等
		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 ^ (value == null ? 0 : value.hashCode());
		}

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

	// Types of Enumerations/Iterations
	private static final int KEYS = 0;
	private static final int VALUES = 1;
	private static final int ENTRIES = 2;

	//Hashtable的枚举器类,实现了Enumeration与Iterator接口
	private class Enumerator<T> implements Enumeration<T>, Iterator<T> {
		Entry[] table = Hashtable.this.table;
		int index = table.length;
		Entry<K, V> entry = null;
		Entry<K, V> lastReturned = null;
		int type;

		//指明该枚举是迭代器还是枚举,当为true是为迭代器
		boolean iterator;

		/**
		 * The modCount value that the iterator believes that the backing
		 * Hashtable should have. If this expectation is violated, the iterator
		 * has detected concurrent modification.
		 */
		//当该枚举器为iterator形式时用来判断其修改状态,用以实现快速失败机制
		protected int expectedModCount = modCount;

		Enumerator(int type, boolean iterator) {
			this.type = type;
			this.iterator = iterator;
		}

		//遍历Hashtable,以遍历是否结束作为返回值,遍历方式为从数组末尾向前
		public boolean hasMoreElements() {
			Entry<K, V> e = entry;
			int i = index;
			Entry[] t = table;
			/* Use locals for faster loop iteration */
			while (e == null && i > 0) {
				e = t[--i];
			}
			entry = e;
			index = i;
			return e != null;
		}

		//获取下一个元素,遍历方式为从数组末尾向前,链表由表头到表尾(?)
		public T nextElement() {
			Entry<K, V> et = entry;
			int i = index;
			Entry[] t = table;
			/* Use locals for faster loop iteration */
			while (et == null && i > 0) {
				et = t[--i];
			}
			entry = et;
			index = i;
			if (et != null) {
				Entry<K, V> e = lastReturned = entry;
				entry = e.next;
				return type == KEYS ? (T) e.key : (type == VALUES ? (T) e.value
						: (T) e);
			}
			throw new NoSuchElementException("Hashtable Enumerator");
		}

		// Iterator methods
		public boolean hasNext() {
			return hasMoreElements();
		}

		public T next() {
			if (modCount != expectedModCount)
				throw new ConcurrentModificationException();
			return nextElement();
		}

		//迭代器的删除方法,对该Hashtable同步后删除对应的元素
		public void remove() {
			if (!iterator)
				throw new UnsupportedOperationException();
			if (lastReturned == null)
				throw new IllegalStateException("Hashtable Enumerator");
			if (modCount != expectedModCount)
				throw new ConcurrentModificationException();

			synchronized (Hashtable.this) {
				Entry[] tab = Hashtable.this.table;
				int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;

				for (Entry<K, V> e = tab[index], prev = null; e != null; prev = e, e = e.next) {
					if (e == lastReturned) {
						modCount++;
						expectedModCount++;
						if (prev == null)
							tab[index] = e.next;
						else
							prev.next = e.next;
						count--;
						lastReturned = null;
						return;
					}
				}
				throw new ConcurrentModificationException();
			}
		}
	}

	private static Enumeration emptyEnumerator = new EmptyEnumerator();
	private static Iterator emptyIterator = new EmptyIterator();

	//空枚举器,当Hashtable为空时返回该枚举器
	private static class EmptyEnumerator implements Enumeration<Object> {

		EmptyEnumerator() {
		}

		public boolean hasMoreElements() {
			return false;
		}

		public Object nextElement() {
			throw new NoSuchElementException("Hashtable Enumerator");
		}
	}

	//空迭代器,当Hashtable为空时返回该迭代器
	private static class EmptyIterator implements Iterator<Object> {

		EmptyIterator() {
		}

		public boolean hasNext() {
			return false;
		}

		public Object next() {
			throw new NoSuchElementException("Hashtable Iterator");
		}

		public void remove() {
			throw new IllegalStateException("Hashtable Iterator");
		}

	}

}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值