Java HashSet示例

时间:2020-01-09 10:35:02  来源:igfitidea点击:

Java中的HashSet是Java Collections框架的一部分,并且是Java应用程序中最常用的Set实现之一。 HashSet类扩展AbstractSet类,并实现Set,Cloneable和Serializable接口。 HashSet由HashMap实例支持,这意味着HashSet类在内部使用HashMap来存储其元素。

Java中HashSet的功能

这篇文章中讨论的HashSet的一些功能如下:

  • HashSet仅存储唯一元素。

  • HashSet是一个无序集合,这意味着插入顺序不会像ArrayList中那样维护。

  • Java中的HashSet允许为null。

  • HashSet不是线程安全的。

  • HashSet的迭代器方法返回的迭代器是快速失败的。这意味着,如果在创建迭代器之后的任何时候修改了集合,则除了通过迭代器自己的remove方法之外,它会以任何方式进行修改,迭代器都会引发ConcurrentModificationException。

Java HashSet构造函数

Java中的HashSet类具有4个构造函数。

  • HashSet()–此构造函数构造一个新的空集;支持的HashMap实例具有默认的初始容量(16)和负载因子(0.75)。

  • HashSet(int initialCapacity)–此构造函数构造一个新的空集合;支持的HashMap实例具有指定的初始容量和默认负载因子(0.75)。

  • HashSet(int initialCapacity,float loadFactor)–此构造函数构造一个新的空集合;支持的HashMap实例具有指定的初始容量和指定的负载系数。

  • HashSet(Collection <?extends E> c)–此构造函数构造一个新集合,其中包含指定集合中的元素。

HashSet的容量和负载因子

如前所述,Java中的HashSet类在内部使用HashMap来存储其元素。 HashMap依次使用Node类型的数组存储元素。如果在创建HashSet时未指定任何容量,则该数组的默认初始容量为16.
在HashMap中,使用存储桶的概念,因此将数组的每个索引概念化为一个存储桶。因此,总共有16个水桶。对于添加到HashSet的每个值,将基于该哈希值计算一个哈希,以选择这些存储桶中的一个存储元素。这样,HashSet能够为基本操作(如添加,删除,包含和大小)提供恒定的时间性能。

负载因子提供HashSet存储的阈值。一旦达到阈值,容量就会增加一倍。默认负载因子为0.75,这意味着如果使用了容量的75%,则将调整HashSet的大小。

要了解有关Java中HashSet内部实现的更多信息,请参阅此帖子。

创建HashSet的Java示例

我们来看一个基本示例,其中创建了一个HashSet并向其中添加了元素。然后显示添加的元素。

import java.util.HashSet;
import java.util.Set;

public class HashSetDemo {
  public static void main(String[] args) {
    Set<String> capitalSet = new HashSet<String>();
    // adding elements
    capitalSet.add("New Delhi");
    capitalSet.add("Lisbon");
    capitalSet.add("Buenos Aires");
    capitalSet.add("Beijing");
    // Displaying set elements
    for(String capital : capitalSet){
      System.out.println("Capital city- " + capital);
    }
  }
}

输出:

Capital city- Beijing
Capital city- New Delhi
Capital city- Lisbon
Capital city- Buenos Aires

如我们所见,使用此语句创建了默认容量的HashSet。

Set<String> capitalSet = new HashSet<String>();

现在,所有Collections类都是通用的,因此我们可以在开始时指定将在Set中存储哪种类型的元素。本示例中使用的Set只能存储字符串。

从输出中,我们可以看到在HashSet中未维护插入顺序。

HashSet类中的方法

这是Java中HashSet类中的一些方法的列表。

  • add(E e)–将指定的元素添加到该集合(如果尚不存在)。

  • clear()–从此集合中删除所有元素。

  • clone()–返回此HashSet实例的浅表副本:元素本身未克隆。

  • contains(Object o)–如果此集合包含指定的元素,则返回true。

  • isEmpty()–如果此集合不包含任何元素,则返回true。

  • iterator()–返回对此集合中的元素进行迭代的迭代器。

  • remove(Object o)–从指定的元素集中删除指定的元素(如果存在)。

  • size()–返回此集合中元素的数量。

  • spliterator()–在此集合中的元素上创建后绑定和故障快速的Spliterator。

HashSet中不允许重复

public class HashSetDemo {
  public static void main(String[] args) {
    Set<String> capitalSet = new HashSet<String>();
    // adding elements
    capitalSet.add("New Delhi");
    capitalSet.add("Lisbon");
    capitalSet.add("Buenos Aires");
    capitalSet.add("Beijing");
    // added again
    capitalSet.add("New Delhi");
    System.out.println("HashSet size-- " + capitalSet.size());
    // Displaying set elements
    for(String capital : capitalSet){
      System.out.println("Capital city- " + capital);
    }
  }
}

输出:

HashSet size-- 4
Capital city- Beijing
Capital city- New Delhi
Capital city- Lisbon
Capital city- Buenos Aires

如我们所见,即使两次添加了"新德里",也只会插入一次。 HashSet的大小也为4.

HashSet中允许插入Null

我们可以在HashSet中插入null,但只能添加一次。在下面的示例中,null被添加了两次,在输出中我们可以看到它仅被插入了一次。

public class HashSetDemo {
  public static void main(String[] args) {
    Set<String> capitalSet = new HashSet<String>();
    // adding elements
    capitalSet.add(null);
    capitalSet.add("New Delhi");
    capitalSet.add("Lisbon");
    capitalSet.add("Buenos Aires");
    capitalSet.add("Beijing");
    capitalSet.add(null);
    System.out.println("HashSet size-- " + capitalSet.size());
    // Displaying set elements
    for(String capital : capitalSet){
      System.out.println("Capital city- " + capital);
    }
  }
}

输出:

HashSet size-- 5
Capital city- null
Capital city- Beijing
Capital city- New Delhi
Capital city- Lisbon
Capital city- Buenos Aires

从HashSet中删除元素的示例代码

public class HashSetDemo {
  public static void main(String[] args) {
    Set<String> capitalSet = new HashSet<String>();
    // adding elements
    capitalSet.add("New Delhi");
    capitalSet.add("Lisbon");
    capitalSet.add("Buenos Aires");
    capitalSet.add("Beijing");
    
    capitalSet.remove("Buenos Aires");
      
    // Displaying set elements
    for(String capital : capitalSet){
      System.out.println("Capital city- " + capital);
    }
    // Removing all elements
    capitalSet.clear();      
    System.out.println("HashSet size after clearing -- " + capitalSet.size());
  }
}

输出:

Capital city- Beijing
Capital city- New Delhi
Capital city- Lisbon
HashSet size after clearing – 0

Java HashSet迭代器示例

我们可以使用迭代器来迭代HashSet。我们可以使用HashSet类的iterator()方法获取Iterator。 HashSet的迭代器方法返回的迭代器是快速失败的。如果在创建迭代器之后的任何时候修改了集合,则除了通过迭代器自己的remove方法以外,都可以通过其他方式修改该集合,否则迭代器将抛出ConcurrentModificationException。

请参阅在Java中迭代HashSet的不同方法,以查看在Java中遍历HashSet的不同方法。

让我们尝试举例说明一下。在代码中,使用迭代器迭代HashSet时,我们将尝试使用HashSet的remove()方法而非迭代器的remove方法来删除元素。

public class HashSetDemo {
  public static void main(String[] args) {
    Set<String> capitalSet = new HashSet<String>();
    // adding elements
    capitalSet.add("New Delhi");
    capitalSet.add("Lisbon");
    capitalSet.add("Buenos Aires");
    capitalSet.add("Beijing");
    
    Iterator<String> itr = capitalSet.iterator();
    while(itr.hasNext()){
      String capital = itr.next();
      System.out.println("Capital city- " + capital);
      if(capital.equals("Lisbon")){
        capitalSet.remove(capital);
      }
    }
  }
}

如我们所见,当我们尝试使用迭代器对HashSet进行结构化修改时,会抛出ConcurrentModificationException异常。

输出:

Capital city- Beijing
Capital city- New Delhi
Capital city- Lisbon
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.HashMap$HashIterator.nextNode(Unknown Source)
	at java.util.HashMap$KeyIterator.next(Unknown Source)
	at com.theitroad.HashSetDemo.main(HashSetDemo.java:19)

使用迭代器的remove方法

public class HashSetDemo {
  public static void main(String[] args) {
    Set capitalSet = new HashSet();
    // adding elements
    capitalSet.add("New Delhi");
    capitalSet.add("Lisbon");
    capitalSet.add("Buenos Aires");
    capitalSet.add("Beijing");
		
    Iterator itr = capitalSet.iterator();
    while(itr.hasNext()){
      String capital = itr.next();
      System.out.println("Capital city- " + capital);
      if(capital.equals("Lisbon")){
        itr.remove();
      }
    }
    System.out.println("** After element removal **");
    // Displaying set elements
    for(String capital : capitalSet){
      System.out.println("Capital city- " + capital);
    }
  }
}

输出:

Capital city- Beijing
Capital city- New Delhi
Capital city- Lisbon
Capital city- Buenos Aires
** After element removal **
Capital city- Beijing
Capital city- New Delhi
Capital city- Buenos Aires

如我们所见,使用迭代器的remove方法,可以在HashSet进行迭代时删除元素。

HashSet不是线程安全的

Java中的HashSet不是线程安全的。如果在多线程环境中使用HashSet,在该环境中HashSet的实例在许多线程之间共享,则应在外部对其进行同步。为了同步Set,可以使用Collections.synchronizedSet()方法,该方法将由传递的集支持的同步集返回到此方法。

例如

Set tempSet = Collections.synchronizedSet(capitalSet);

在此处查看在Java中同步HashSet的示例如何在Java中同步HashSet