Java HashSet类使用教程

x33g5p2x  于2021-08-21 转载在 Java  
字(8.0k)|赞(0)|评价(0)|浏览(493)

在本指南中,我们要看一下JavaSet框架中的HashSet类。

我们将学习HashSet实现提供的所有API。

HashSet类实现了Set接口,由一个哈希表(实际上是一个HashMap实例)支持。它不保证Set的迭代顺序;特别是不保证顺序会随着时间的推移保持不变。这个类允许空元素。
HashSet的实现是不同步的--如果多个线程同时访问一个HashSet合,并且至少有一个线程修改了这个Set,那么它必须在外部进行同步。

例子:

Set s = Collections.synchronizedSet(new HashSet(...));

本指南涵盖了重要的HashSet实现API,并附有示例。所有的API都是由HashSet JavaDoc引用的。

我们将学到什么?

  1. Set接口概述
  2. HashSet类概述
  3. 创建一个HashSet并添加新元素 示例
  • add()方法
  • addAll()方法
  1. HashSet的删除API示例
  • remove(Object o)
  • removeAll(Collection<?> c)
  • removeIf(Predicate<? super Integer> filter)
  • clear()
  1. 示范Set如何包含重复和空值?
  2. HashSet批量操作API的例子
  • addAll()
  • retainAll()
  • containsAll()
  • removeAll()
  1. 遍历一个HashSet
  • iterator()
  • Advance for()循环
  • forEachRemaining()
  • forEach()
  1. 带有用户定义对象的HashSet
  2. 如何使HashSet的线程安全?

1. Set接口概述

  • Set接口只包含继承自Collection接口的方法,并增加了禁止重复元素的限制。
  • 一个不包含重复元素的Set。更正式地说,Set不包含任何一对元素e1和e2,使得e1.eals(e2),以及最多一个空元素。
  • 正如它的名字所暗示的,这个接口是数学上的Set抽象的模型。
  • Set并不保证插入的顺序。

2. HashSet类概述

Java HashSet类用于创建一个使用哈希表进行存储的Set。它继承了AbstractSet类并实现了Set接口。

关于Java HashSet类的要点是。

  • HashSet通过使用一种叫做散列的机制来存储元素。
  • HashSet只包含唯一的元素。
  • List和Set的区别
  • List可以包含重复的元素,而Set只包含唯一的元素。

3. 创建一个HashSet并添加新元素 示例

add()

下面的例子显示了如何使用HashSet()构造函数创建一个HashSet,并使用add()方法向其中添加新元素。

// Creating a HashSet
Set<String> daysOfWeek = new HashSet<>();

// Adding new elements to the HashSet
daysOfWeek.add("Monday");
daysOfWeek.add("Tuesday");
daysOfWeek.add("Wednesday");
daysOfWeek.add("Thursday");
daysOfWeek.add("Friday");
daysOfWeek.add("Saturday");
daysOfWeek.add("Sunday");

// Adding duplicate elements will be ignored
daysOfWeek.add("Monday");

System.out.println(daysOfWeek);

addAll()

从Set中创建HashSet示例

List<Integer> list = new ArrayList<>();
list.add(5);
list.add(10);
list.add(15);
list.add(20);
list.add(25);

List<Integer> list2 = new ArrayList<>();
list2.add(3);
list2.add(6);
list2.add(9);
list2.add(12);
list2.add(15);

// Creating a HashSet from another collection (ArrayList)
Set<Integer> set = new HashSet<>(list);

// Adding all the elements from an existing collection to a HashSet
set.addAll(list2);

System.out.println(set);

4. HashSet remove API 示例

remove(Object o)

从HashSet中删除一个元素(如果该元素不存在于HashSet中,remove()方法返回false)。

Set<Integer> numbers = new HashSet<>();
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
numbers.add(6);
numbers.add(7);
numbers.add(8);
numbers.add(9);
numbers.add(10);

System.out.println("numbers : " + numbers);

boolean isRemoved = numbers.remove(10);
System.out.println("After remove(10) => " + numbers);

removeAll(Collection<?> c)

从HashSet中删除属于一个给定Set的所有元素。

Set<Integer> numbers = new LinkedHashSet<>();
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
numbers.add(6);
numbers.add(7);
numbers.add(8);
numbers.add(9);
numbers.add(10);
List<Integer> perfectSquares = new ArrayList<>();
perfectSquares.add(4);
perfectSquares.add(9);

numbers.removeAll(perfectSquares);
System.out.println("After removeAll(perfectSquares) => " + numbers);

removeIf(Predicate<? super Integer> filter)

删除所有匹配给定谓词的元素

numbers.removeIf(num -> num % 2 == 0);
System.out.println("After removeIf() => " + numbers);

clear()

从HashSet中删除所有元素(完全清除)。

numbers.clear();
System.out.println("After clear() => " + numbers);

5. 演示一下Set如何包含重复的和空值?

public class SetInterfaceHashImpl {

 public static void main(String[] args) {
  nullValueDemo();
  duplicateValueDemo();
  //bulkOperationDemo();
 }

 // Set can contain one null value
 private static void nullValueDemo() {
  Set<String> set = new HashSet<>();
  set.add(null);
  set.add(null);
  System.out.println(set.toString());
 }

 // it is not contain duplicate elements
 private static void duplicateValueDemo() {
  Set<String> set = new HashSet<>();
  set.add("element1");
  set.add("element1");
  // displays only one element
  System.out.println(set.toString());
 }
}

输出:

[null]

[element1]

6. HashSet批量操作API的例子

让我们用例子来写一下下面的批量操作方法

  • addAll()
  • retainAll()
  • containsAll()
  • removeAll()
// Set bulk operations
private static void bulkOperationDemo() {
 Set<String> set = new HashSet<>();
 set.add("element1");
 set.add("element2");
 set.add("element3");
 set.add("element4");

 // Appends all of the elements in the specified collection to the end of
 // this list,
 // in the order that they are returned by the specified collection's
 // iterator (optional operation).
 Set<String> union = new HashSet<String>();
 union.addAll(set);
 printMessage(union, "addALL operation example ");

 // Retains only the elements in this list that are contained in
 // the specified collection (optional operation).
 Set<String> intersection = new HashSet<String>();
 intersection.add("element 1");
 intersection.add("element 2");
 intersection.add("element 3");
 intersection.add("element 4");
 System.out.println("retainAll -- > " + intersection.retainAll(set));

 // Removes from this list all of its elements that are
 // contained in the specified collection (optional operation).
 Set<String> difference = new HashSet<String>();
 difference.add("element 1");
 difference.add("element 2");
 difference.add("element 3");
 difference.add("element 4");
 System.out.println("removeAll operation example ---> " + difference.removeAll(set));
 printMessage(difference, "removeAll operation example ");

 Set<String> checking = new HashSet<String>();
 checking.add("element 1");
 checking.add("element 2");
 checking.add("element 3");
 checking.add("element 4");
 System.out.println("containsAll operation example ---- > " + checking.containsAll(set));
}

private static void printMessage(Set<String> difference, String msg) {
 difference.forEach(key -> System.out.println(msg + key));
}

7. 遍历一个HashSet

下面的例子展示了对HashSet进行迭代的不同方法

  • 使用Java 8 forEach和lambda表达式遍历一个HashSet。
  • 使用iterator()对HashSet进行遍历。
  • 使用iterator()和Java 8 forEachRemaining()方法对HashSet进行遍历。
  • 使用简单的for-each循环对HashSet进行迭代。

iterator()

Set<String> list = new HashSet<>();
list.add("element 1");
list.add("element 2");
list.add("element 3");
list.add("element 4");

// using Iterator
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
 String str = iterator.next();
 System.out.println(" only forward direction ---" + str);
}

进阶for()循环

// Using advanced for loop
for (String str : list) {
 System.out.println(" only forward direction ---" + str);
}

forEachRemaining()

// Java 8
list.forEachRemaining(str -> System.out.println(" only forward direction ---" + str));

forEach()

// Java 8
list.forEach(str -> System.out.println(" only forward direction ---" + str));

8. 带有用户定义对象的HashSet

这个例子显示了如何创建一个用户定义对象的HashSet。

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

class Customer {
    private long id;
    private String name;

    public Customer(long id, String name) {
        this.id = id;
        this.name = name;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    // Two customers are equal if their IDs are equal
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Customer customer = (Customer) o;
        return id == customer.id;
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }

    @Override
    public String toString() {
        return "Customer{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

public class HashSetUserDefinedObjectExample {
    public static void main(String[] args) {
        Set<Customer> customers = new HashSet<>();
        customers.add(new Customer(101, "Rajeev"));
        customers.add(new Customer(102, "Sachin"));
        customers.add(new Customer(103, "Chris"));

        //*
HashSet will use the `equals()` & `hashCode()` implementations 
of the Customer class to check for duplicates and ignore them
/*/
        customers.add(new Customer(101, "Rajeev"));

        System.out.println(customers);
    }
}

输出

[Customer{id=101, name='Rajeev'}, Customer{id=102, name='Sachin'}, Customer{id=103, name='Chris'}]

9. 如何使HashSet成为线程安全的?

这个类的实现是不同步的,所以它不是线程安全的。如果多个线程同时访问一个HashSet,并且至少有一个线程修改了该集,那么它必须在外部进行同步。HashSet必须在外部进行同步。
例子:

// HashSet is not synchronized
private static void synchronizedHashSetDemo() {
 Set<String> set = new HashSet<>();
 Set<String> synchronizedSet = Collections.synchronizedSet(set);
}

相关文章