Core Java Tutorial -- HashSet 2022-05-29 15:39 42阅读 0赞 Java HashSet 是 Set 接口最流行的实现。 `java.util.HashSet` 由一个`HashMap` 支持。`HashSet` 继承 `AbstractSet` 类并实现`Set`、`Cloneable` 和 `Serializable`接口。 1. Java HashSet 1. Java HashSet Constructors 2. Java HashSet Methods 3. Java HashSet Example 4. Java HashSet ConcurrentModificationException Example 5. Java HashSet to Array Example 6. Java HashSet to List Example 7. Java HashSet equals() and hashCode() methods # 1. Java HashSet # Java 中有关 HashSet 的一些重点有: 1. HashSet 不允许复制条目。 2. HashSet 允许 null 值。 3. HashSet 不保证元素的插入顺序。 4. HashSet 不是线程安全的。你可以使用 `Collections.synchronizedSet` 方法以性能为代价获得线程安全的 HashSet。你还可以使用 CopyOnWriteArraySet 并发类来保证线程安全。 5. HashSet 的迭代器方法是快速失败的。因此,在创建迭代器后对该集合进行任何结构修改都会引发 `ConcurrentModificationException`。 6. HashSet 支持泛型,这是在运行时避免 `ClassCastException` 异常的推荐方法。 7. HashSet 使用 [HashMap][] 来存储元素,所以对象应该提供 `hashCode()` 和 `equals()` 方法的良好实现,以避免不要的结果。 ## Java HashSet Constructors ## Java HashSet 提供四个构造器: 1. `public HashSet()`:创建一个新的空的 HashSet,由默认初始容量为 16,加载因子为 0.75 的 HashMap 支持。 2. `public HashSet(int initialCapacity)`:创建一个新的空的 HashSet,由指定初始容量,加载因子为 0.75 的 HashMap 支持。 3. `public HashSet(int initialCapacity, float loadFactor)`创建一个新的空的 HashSet,由指定初始容量,指定加载因子的 HashMap 支持。 4. `public HashSet(Collection<? extends E> c)`:创建一个包含指定集合中元素的新 Set。 支持 HashMap 是使用默认加载因子(0.75)创建的,并且初始容量足以包含指定集合中的所有元素。 下面的代码片段显示了所有这些 HashSet 构造函数的示例用法。 package HashSet; import java.util.ArrayList; import java.util.HashSet; import java.util.Set; public class HashSetTest { public static void main(String[] args) { Set<String> set; //initial capacity should be power of 2 set = new HashSet<>(32); //setting backing HashMap initial capacity and load factor set = new HashSet<>(32, 0.80f); //creating HashSet from another Collection Set<String> set1 = new HashSet<>(set); Set<String> set2 = new HashSet<>(new ArrayList<>()); } } ## Java HashSet Methods ## 一些有用的 HashSet 方法: 1. `public boolean add(E e)`:将给定的元素添加到 Set 中(如果该元素在 Set 不存在的话)。此方法内部使用 `equals()` 方法来检查重复项,因此请确保你的对象正确定义 `equals()` 方法。 2. `public void clear()`:删除 Set 中的所有元素。 3. `public Object clone()`:返回 Set 示栗的浅拷贝。 4. `public boolean contains(Object o)`:如果 Set 包含给定元素返回 true,否则返回 false。 5. `public boolean isEmpty()`:如果 Set 不包含任何元素,则返回 true,否则返回 false。 6. `public Iterator<E> iterator()`:返回此 Set 中元素的迭代器。元素以特定顺序返回。 7. `public boolean remove(Object o)`:从此 Set 中移除给定元素,如果存在则返回 true。如果元素不在集合中,则返回 false。 8. `public int size()`:返回 Set 中元素的数量。 9. `public Spliterator<E> spliterator()`:在此集合中的元素上创建一个后期绑定和快速失败的 Spliterator。这是在 Java 8 中引入的,但直到现在我都没用过他。 10. `public boolean removeAll(Collection<?> c)`:HashSet 从 AbstractSet 继承此方法。此方法将删除属于指定集合一部分的集合中的所有元素。 ## Java HashSet Example ## Java HashSet 示栗程序展示了 HashSet 的通常用法。 package HashSet; import java.util.*; public class HashSetExample { public static void main(String[] args) { Set<String> fruits = new HashSet<>(); // add example fruits.add("Apple"); fruits.add("Banana"); // isEmpty example System.out.println("fruits set is empty = " + fruits.isEmpty()); // contains example System.out.println("fruits contains Apple = " + fruits.contains("Apple")); System.out.println("fruits contains Mango = " + fruits.contains("Mango")); // remove example System.out.println("Apple removed from fruits set = " + fruits.remove("Apple")); System.out.println("Mango removed from fruits set = " + fruits.remove("Mango")); // size example System.out.println("fruits set size = " + fruits.size()); // addAll example List<String> list = new ArrayList<>(); list.add("Apple"); list.add("Apple"); list.add("Banana"); list.add("Mango"); System.out.println("fruits set before addAll = " + fruits); System.out.println("list = " + list); fruits.addAll(list); System.out.println("fruits set after addAll = " + fruits); // iterator example Iterator<String> iterator = fruits.iterator(); while (iterator.hasNext()) { System.out.println("Consuming fruit " + iterator.next()); } // removeAll example fruits.add("Orange"); System.out.println("fruits set before removeAll = " + fruits); System.out.println("list = " + list); fruits.removeAll(list); System.out.println("fruits set after removeAll = " + fruits); // clear example fruits.clear(); System.out.println("fruits set is empty = " + fruits.isEmpty()); } } 上面的 HashSet 示例程序的输出如下 fruits set is empty = false fruits contains Apple = true fruits contains Mango = false Apple removed from fruits set = true Mango removed from fruits set = false fruits set size = 1 fruits set before addAll = [Banana] list = [Apple, Apple, Banana, Mango] fruits set after addAll = [Apple, Mango, Banana] Consuming fruit Apple Consuming fruit Mango Consuming fruit Banana fruits set before removeAll = [Apple, Mango, Orange, Banana] list = [Apple, Apple, Banana, Mango] fruits set after removeAll = [Orange] fruits set is empty = true ## Java HashSet ConcurrentModificationException Example ## Java HashSet 迭代器是快速失败的,所以如果 Set 在结构上被修改,则方法将抛出 `java.util.ConcurrentModificationException`。下面是一个简单的例子来说明这一点。 package HashSet; import java.util.HashSet; import java.util.Iterator; import java.util.Set; public class HashSetConcurrentModificationExceptionExample { public static void main(String[] args) { Set<String> fruits = new HashSet<>(); // add example fruits.add("Apple"); fruits.add("Banana"); fruits.add("Orange"); fruits.add("Mango"); Iterator<String> iterator = fruits.iterator(); while (iterator.hasNext()) { String fruit = iterator.next(); System.out.println("Processing " + fruit); // wrong way of removing from Set, can throw java.util.ConcurrentModificationException if ("Orange".equals(fruit)) fruits.remove("Orange"); } } } 上面程序运行后将得到以下输出和异常 Processing Apple Processing Mango Processing Orange Exception in thread "main" java.util.ConcurrentModificationException at java.util.HashMap$HashIterator.nextNode(HashMap.java:1442) at java.util.HashMap$KeyIterator.next(HashMap.java:1466) at HashSet.HashSetConcurrentModificationExceptionExample.main(HashSetConcurrentModificationExceptionExample.java:20) 注意!HashSet 元素不保证是有序的,调用 `iterator.next()` 将抛出 `ConcurrentModificationException` 异常。所以如果”Orange”是迭代器中的最后一个,则不会得到异常,因为 `iterator.hasNext()` 将返回 false,且不会调用 `iterator.hasNext()` 我们应该总是使用 Iterator 方法进行结构修改,如下所示: package HashSet; import java.util.HashSet; import java.util.Iterator; import java.util.Set; public class HashSetConcurrentModificationExceptionExample2 { public static void main(String[] args) { Set<String> fruits = new HashSet<>(); fruits.add("Apple"); fruits.add("Banana"); fruits.add("Orange"); fruits.add("Mango"); Iterator<String> iterator = fruits.iterator(); while (iterator.hasNext()) { String fruit = iterator.next(); System.out.println("Processing " + fruit); // correct way of structural modification of Set if ("Orange".equals(fruit)) { iterator.remove(); } } System.out.println("fruits set after iteration = " + fruits); } } 上面 HashSet 迭代器栗子不会抛出异常,你会得到下面的输出: Processing Apple Processing Mango Processing Orange Processing Banana fruits set after iteration = [Apple, Mango, Banana] ## Java HashSet to Array Example ## 有时我们必须将 HashSet 转换为数组,反之亦然。下面是一个简单的程序,显示将 HashSet 转换为数组,然后将 Array 转换为 HashSet 的正确方法。 package HashSet; import java.util.Arrays; import java.util.HashSet; import java.util.Set; public class HashSetToArrayExample { public static void main(String[] args) { Set<Integer> ints = new HashSet<>(); for (int i = 0; i < 10; i++) { ints.add(i); } System.out.println("ints set = " + ints); // set to array example Integer[] intArray = new Integer[ints.size()]; intArray = ints.toArray(intArray); System.out.println("intArray = " + Arrays.toString(intArray)); ints.remove(0); ints.remove(1); System.out.println("intArray = " + Arrays.toString(intArray)); // array to set example ints = new HashSet<>(Arrays.asList(intArray)); System.out.println("ints from array = " + ints); ints.remove(0); ints.remove(1); System.out.println("ints from array after remove = " + ints); System.out.println("intArray = " + Arrays.toString(intArray)); } } 上面 HashSet 转数组的栗子输出如下: ints set = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] intArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] intArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ints from array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ints from array after remove = [2, 3, 4, 5, 6, 7, 8, 9] intArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ## Java HashSet to List Example ## Set 和 List 之间没有太大的区别,但有时候我们必须将 Set 转换为 Set 或 List 转换为 Set。面是一个简单的例子,显示了将 Set 转换为为 List,将List 设置为 Set 的正确方法。 package HashSet; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class HashSetToListExample { public static void main(String[] args) { Set<String> vowels = new HashSet<>(); vowels.add("a"); vowels.add("e"); vowels.add("i"); //set to list example List<String> vowelsList = new ArrayList<>(vowels); System.out.println("vowels set = " + vowels); System.out.println("vowelsList = " + vowelsList); vowels.add("o"); vowelsList.add("a"); vowelsList.add("u"); System.out.println("vowels set = " + vowels); System.out.println("vowelsList = " + vowelsList); //list to set example vowels = new HashSet<>(vowelsList); System.out.println("vowels set = " + vowels); } } ## Java HashSet equals() and hashCode() methods ## HashSet 利用 HashMap 来存储它的元素。当i尝试添加元素时,HashSet 用 `equals()` 和 `hashCode()` 方法检查重复元素。让我们看看,如果你的 Set 对象没有提供 `equals()` 方法实现会发生什么。 package HashSet; import java.util.HashSet; import java.util.Set; public class HashSetEqualsMethodImportance { public static void main(String[] args) { Set<Emp> emps = new HashSet<>(); emps.add(new Emp(1, "Pankaj")); emps.add(new Emp(2, "David")); emps.add(new Emp(1, "Pankaj")); System.out.println(emps); } } class Emp { private String name; private int id; public Emp(int i, String n) { this.id = i; this.name = n; } @Override public String toString() { return "{" + id + "," + name + "}"; } } 当我们运行上面程序,我们将得到以下输出: [{2,David}, {1,Pankaj}, {1,Pankaj}] 所以看起来我们能够在 Set 中存储重复元素。其实不是,这是因为 Emp 类没有定义 `equals()` 方法,因此使用了 Object 类的 `equals()` 方法实现。Object 类的 `equals` 方法定义如下: public boolean equals(Object obj) { return (this == obj); } 所以当添加一个新元素时,对象引用被检查而不是内容。因此,我们拥有重复内容的对象,但它们具有不同的引用。让我们看看当我们在 Emp 类中定义 `equals()` 和 `hashCode()` 方法后会发生什么。 package HashSet; import java.util.HashSet; import java.util.Set; public class HashSetEqualsMethodImportance2 { public static void main(String[] args) { Set<Emp> emps = new HashSet<>(); emps.add(new Emp(1, "Pankaj")); emps.add(new Emp(2, "David")); emps.add(new Emp(1, "Pankaj")); System.out.println(emps); Emp e = new Emp(3, "Lisa"); emps.add(e); System.out.println(emps); //set values to make it duplicate e.setId(1); System.out.println(emps); e.setName("Pankaj"); System.out.println(emps); } } class Emp { private String name; private int id; public Emp(int id, String name) { this.id = id; this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } @Override public boolean equals(Object o) { if (o == null || !(o instanceof Emp)) return false; Emp emp = (Emp) o; if (emp.getId() == this.getId() && this.getName().equals(emp.getName())) return true; return false; } @Override public int hashCode() { return getId(); } @Override public String toString() { return "{" + getId() + "," + getName() + "}"; } } 输出如下: [{1,Pankaj}, {2,David}] [{1,Pankaj}, {2,David}, {3,Lisa}] [{1,Pankaj}, {2,David}, {1,Lisa}] [{1,Pankaj}, {2,David}, {1,Pankaj}] 注意,当我们尝试添加一个元素时,HashSet 能够检查重复。但是我们可以使用 setter 方法更改对象值并使其重复。它的工作原理是因为 Set 上没有完成任何操作。这就是为什么不可变对象更适合于 Set 和 Map。 [HashMap]: https://www.journaldev.com/11560/java-hashmap 文章版权声明:注明蒲公英云原创文章,转载或复制请以超链接形式并注明出处。
相关 Core Java Tutorial -- SortedMap Java SortedMap 是一个提供了在键上完全排序的 Map。 1. Java SortedMap 1. Java SortedMa 朱雀/ 2022年05月28日 08:50/ 0 赞/ 76 阅读
相关 Core Java Tutorial -- TreeMap Java TreeMap 是一个 [Map][] 的实现,它是 [Java 集合框架][Java]的一部分。 1. Java TreeMap 迷南。/ 2022年05月28日 08:52/ 0 赞/ 60 阅读
相关 Core Java Tutorial -- Queue Java Queue 是 `java.util` 包中的一个接口,用于扩展 `java.util.Collection` 接口。就像 Java List 一样,Java Que 妖狐艹你老母/ 2022年05月28日 08:54/ 0 赞/ 20 阅读
相关 Core Java Tutorial -- Iterator 在本文中我们将讨论一些关于 Java Enumeration 的基础,深入讨论 Java 迭代器。由于 Enumeration 接口被弃用了,所以不建议在应用中使用。 1. 我会带着你远行/ 2022年05月28日 08:57/ 0 赞/ 27 阅读
相关 Core Java Tutorial -- Thread Local Java ThreadLocal 被用于创建线程局部变量。我们知道一个对象的所有线程共享它的变量,所以这个变量不是线程安全的。我们可以为线程安全使用同步,但如果我们像避免同步, ╰半橙微兮°/ 2022年05月28日 15:20/ 0 赞/ 37 阅读
相关 Core Java Tutorial -- Deadlock Java 中的死锁是一种编程情况,两个或更多的线程永远被阻塞。至少有两个线程和两个或更多资源出现 Java 死锁情况。在这里我写了一个简单的程序,它会导致 Java 死锁情况, 你的名字/ 2022年05月28日 15:20/ 0 赞/ 12 阅读
相关 Core Java Tutorial -- Timer Thread Java `java.util.Timer` 是一个使用程序类,可用于安排将来某个时间执行的线程。Java Timer 类可用于安排一次于运行的任务或定期运行的任务。 Ja ╰+攻爆jí腚メ/ 2022年05月28日 15:20/ 0 赞/ 44 阅读
相关 Core Java Tutorial -- ArrayList Java ArrayList 是最广泛使用的 Collection 类。`java.util.ArrayList` 类实现类了 `java.util.List` 接口。Java 妖狐艹你老母/ 2022年05月29日 15:14/ 0 赞/ 29 阅读
相关 Core Java Tutorial -- LinkedList Java LinkedList 是 List 和 Deque 接口的实现。它是经常使用的 List 实现类之一。 它 extends 了 AbstractSequential 末蓝、/ 2022年05月29日 15:23/ 0 赞/ 21 阅读
相关 Core Java Tutorial -- HashSet Java HashSet 是 Set 接口最流行的实现。 `java.util.HashSet` 由一个`HashMap` 支持。`HashSet` 继承 `AbstractS 待我称王封你为后i/ 2022年05月29日 15:39/ 0 赞/ 43 阅读
还没有评论,来说两句吧...