Set---HashSet
概述
This class implements the <tt>Set</tt> interface, backed by a hash table (actually a <tt>HashMap</tt> instance).
It makes no guarantees as to the iteration order of the set; in particular, it does not guarantee that the order will remain constant over time.
This class permits the <tt>null</tt> element.
hashSet基于hashmap实现;
不保证iterator顺序;
hashSet允许null;
This class offers constant time performance for the basic operations (<tt>add</tt>, <tt>remove</tt>, <tt>contains</tt> and <tt>size</tt>), assuming the hash function disperses the elements properly among the buckets.
Iterating over this set requires time proportional to the sum of the <tt>HashSet</tt> instance's size (the number of elements) plus the "capacity" of the backing <tt>HashMap</tt> instance (the number of buckets).
Thus, it's very important not to set the initial capacity too high (or the load factor too low) if iteration performance is important.
hashSet为add、remove、contains、size操作提供O(1)时间复杂度,因为hash将元素分散到buckets;
iterator需要的时间与size成正比;
Note that this implementation is not synchronized.
If multiple threads access a hash set concurrently, and at least one of the threads modifies the set, it <i>must</i> be synchronized externally.
This is typically accomplished by synchronizing on some object that naturally encapsulates the set.
If no such object exists, the set should be "wrapped" using the {@link Collections#synchronizedSet Collections.synchronizedSet} method.
线程非同步的;
如果多个线程并发访问hashset,需要在外部进行同步;
可以使用Collections.synchronizedSet;
The iterators returned by this class's <tt>iterator</tt> method are <i>fail-fast</i>:
if the set is modified at any time after the iterator is created, in any way except through the iterator's own <tt>remove</tt> method, the Iterator throws a {@link ConcurrentModificationException}.
Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
hashset的iterator方法是fail-fast;
如果在iterator时,hashSet被修改(除了iterator的remove),将会抛出ConcurrentModificationException;
时间复杂度
add:O(1)
remove:O(1)
contains:O(1)
链路
new HashSet<>(10)
1 2 3 4 | // java.util.HashSet.HashSet(int) public HashSet( int initialCapacity) { map = new HashMap<>(initialCapacity); } |
add(E e)
1 2 3 4 5 6 7 8 9 | // java.util.HashSet.add public boolean add(E e) { return map.put(e, PRESENT)== null ; // hashSet的元素是在hashmap的key,v永远是一个Object对象 } // java.util.HashMap.put public V put(K key, V value) { return putVal(hash(key), key, value, false , true ); } |
iterator
1 2 3 4 5 6 7 8 9 10 11 12 | Set<String> set = new HashSet<>( 10 ); set.add( "a" );set.add( "b" );set.add( "c" );set.add( "d" ); Iterator<String> iterator = set.iterator(); while (iterator.hasNext()){ String next = iterator.next(); if (next.equals( "a" )){ set.remove(next); } } |
iterator.iterator()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | // set.iterator() -> java.util.HashMap.KeyIterator.iterator() // java.util.HashSet.iterator public Iterator<E> iterator() { return map.keySet().iterator(); } // java.util.HashMap.keySet public Set<K> keySet() { Set<K> ks = keySet; if (ks == null ) { ks = new KeySet(); keySet = ks; } return ks; } // java.util.HashMap.KeySet final class KeySet extends AbstractSet<K> { public final Iterator<K> iterator() { return new KeyIterator(); } } // java.util.HashMap.KeyIterator final class KeyIterator extends HashIterator implements Iterator<K> { public final K next() { return nextNode().key; } } // java.util.HashMap.HashIterator abstract class HashIterator { public final boolean hasNext() { return next != null ; } final HashMap.Node<K,V> nextNode() { HashMap.Node<K,V>[] t; HashMap.Node<K,V> e = next; if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (e == null ) throw new NoSuchElementException(); if ((next = (current = e).next) == null && (t = table) != null ) { do {} while (index < t.length && (next = t[index++]) == null ); } return e; } public final void remove() { HashMap.Node<K,V> p = current; if (p == null ) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); current = null ; K key = p.key; removeNode(hash(key), key, null , false , false ); expectedModCount = modCount; } } |
hashSet.remove()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | // java.util.HashSet.remove public boolean remove(Object o) { return map.remove(o)==PRESENT; } // java.util.HashMap.remove(java.lang.Object) public V remove(Object key) { HashMap.Node<K,V> e; return (e = removeNode(hash(key), key, null , false , true )) == null ? null : e.value; } // java.util.HashMap.removeNode final HashMap.Node<K,V> removeNode( int hash, Object key, Object value, boolean matchValue, boolean movable) { HashMap.Node<K,V>[] tab; HashMap.Node<K,V> p; int n, index; if ((tab = table) != null && (n = tab.length) > 0 && (p = tab[index = (n - 1 ) & hash]) != null ) { HashMap.Node<K,V> node = null , e; K k; V v; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) node = p; else if ((e = p.next) != null ) { if (p instanceof HashMap.TreeNode) node = ((HashMap.TreeNode<K,V>)p).getTreeNode(hash, key); else { do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { node = e; break ; } p = e; } while ((e = e.next) != null ); } } if (node != null && (!matchValue || (v = node.value) == value || (value != null && value.equals(v)))) { if (node instanceof HashMap.TreeNode) ((HashMap.TreeNode<K,V>)node).removeTreeNode( this , tab, movable); else if (node == p) tab[index] = node.next; else p.next = node.next; ++modCount; // 此处remove操作后,++modCount --size; afterNodeRemoval(node); return node; } } return null ; } |
set.remove抛ConcurrentModificationException
当使用iterator迭代时,iterator的next方法有个modCount != expectedModCount,
而hashset的remove方法执行后有个++modCount,导致二者不一致;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)