java集合之 HashSet LinkedHashSet

HashSet

  • 这个类实现了 Set 接口,由一个哈希表(实际上是一个 HashMap 实例)支持。 它不保证 set 的迭代顺序; 特别是它不保证该顺序恒久不变。此类允许 null 元素。

  • 假设哈希函数在桶中正确地分散元素,那么这个类为基本操作( addremovecontainssize)提供稳定性能。 迭代这个 set 需要的时间与 HashSet 实例的大小(元素数)和底层 HashMap 实例(桶的数量)的“容量”的和成比例。 因此,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低),这一点非常重要。

  • 请注意,此实现不是同步的。 如果多个线程同时访问一个哈希 set,并且至少有一个线程修改了该 set,则必须在外部进行同步。 这通常是通过同步一些自然封装集合的对象来实现的。 如果不存在这样的对象,则应使用 Collections.synchronizedSet 方法“包装” set。 这最好在创建时完成,以防止对集合的意外不同步访问:Set s = Collections.synchronizedSet(new HashSet(...));

  • 此类的 iterator 方法返回的迭代器是快速失败 的:如果在迭代器创建后的任何时间,除了通过迭代器自己的 remove 方法之外的任何方式修改了 set ,迭代器都会抛出 ConcurrentModificationException。 因此,面对并发修改,迭代器很快就会完全失败,而不是冒着在未来不确定的时间出现任意、非确定性行为的风险。

  • 请注意,无法保证迭代器的快速失败行为,因为一般而言,在存在非同步并发修改的情况下不可能做出任何硬保证。 快速失败的迭代器会尽最大努力抛出 ConcurrentModificationException。 因此,编写一个依赖此异常来确保其正确性的程序是错误的:迭代器的快速失败行为应该仅用于检测 bug。

HashSet构造器

  • HashSet()

    • 构造一个新的空集; 底层 HashMap 实例具有默认初始容量 (16) 和负载因子 (0.75)。
  • HashSet (int initialCapacity)

    • 构造一个新的空 set; 底层 HashMap 实例具有指定的初始容量和默认负载因子 (0.75)。
  • HashSet (int initialCapacity, float loadFactor)

    • 构造一个新的空set; 其底层 HashMap 实例具有指定的初始容量和指定的加载因子。
  • HashSet (Collection<? extends E> c)

    • 构造一个包含指定 collection 中的元素的新 set。

HashSet方法

Modifier and Type Method Description
boolean add (E e) 如果指定元素尚不存在,则将其添加到此set中。
void clear() 从此set中删除所有元素。
Object clone() 返回这个 HashSet 实例的浅拷贝:并没有复制这些元素本身。
boolean contains (Object o) 如果此set合包含指定的元素,则返回 true。
boolean isEmpty() 如果此set不包含任何元素,则返回 true。
Iterator iterator() 返回对此 set 中元素进行迭代的迭代器。
boolean remove (Object o) 如果指定的元素存在,则从该set中移除该元素。
int size() 返回此set中的元素数(set 的容量)。
Spliterator spliterator() 在此set中的元素上创建一个后期绑定和快速失败的 Spliterator。
Object[] toArray() 返回一个包含此set中所有元素的数组。
T[] toArray (T[] a) 返回一个包含此set中所有元素的数组; 返回数组的运行时类型是指定数组的类型。

HashSet底层简要原理

以Integer类型为例,Integer类型计算出的hash值就是其自身value值

public int hashCode() {
    
    
    return Integer.hashCode(value);
}
public static int hashCode(int value) {
    
    
	return value;
}
集合中存入的数据 29 51 95 29 67 73 25
hashCode值 29 51 95 29 67 73 25
通过hash值和一个表达式
计算在数组中的位置(假设)
3 2 5 3 3 4 0

底层数组

扫描二维码关注公众号,回复: 13367491 查看本文章
索引 0 1 2 3 4 5
内容 25 51 29–>67
链表连接
73 95

要放入HashSet中的数据一定要重写hashCode()equals方法,equals比较相同的元素不会用链表连接

HashSet底层原理:数组 + 链表 = 哈希表

相关实践
import java.util.HashSet;
import java.util.Objects;

public class HashT {
    
    
    public static void main(String[] args) {
    
    
        //创建一个HashSet集合:
        HashSet<Integer> hsi = new HashSet<>();
        System.out.println(hsi.add(9));//true
        hsi.add(5);
        System.out.println(hsi.add(9));//false 这个9没有放入到集合中
        hsi.add(8);
        System.out.println(hsi.size());//3 唯一,无序
        System.out.println(hsi); // [5, 8, 9]

        HashSet<String> hss = new HashSet<>();
        hss.add("hello");
        hss.add("world");
        hss.add("hello");
        System.out.println(hss.size()); //2
        System.out.println(hss); //[world, hello]

        //自定义的类不满足唯一,无序的特点。
        HashSet<Student> hst = new HashSet<>();
        hst.add(new Student(19,"fyz"));
        hst.add(new Student(18,"xhr"));
        hst.add(new Student(19,"fyz"));
        System.out.println(hst.size()); //3
        System.out.println(hst);
        //[Student{id=19, name='fyz'}, Student{id=18, name='xhr'}, Student{id=19, name='fyz'}]

        //自定义的类中重写hashCode()和equals方法后满足唯一,无序特点
        HashSet<Studenthash> hsth = new HashSet<>();
        hsth.add(new Studenthash(19,"fyz"));
        hsth.add(new Studenthash(18,"xhr"));
        hsth.add(new Studenthash(19,"fyz"));
        System.out.println(hsth.size()); //2
        System.out.println(hsth);
        //[Student{id=18, name='xhr'}, Student{id=19, name='fyz'}]
    }
}

class Student{
    
    
    int id;
    String name;

    public Student(int id, String name) {
    
    
        this.id = id;
        this.name = name;
    }

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

class Studenthash{
    
    
    int id;
    String name;

    public Studenthash(int id, String name) {
    
    
        this.id = id;
        this.name = name;
    }

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

    @Override
    public boolean equals(Object o) {
    
    
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Studenthash that = (Studenthash) o;
        return id == that.id &&
                name.equals(that.name);
    }

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

LinkedHashSet

唯一,有序(按照输入顺序)。实质上是在HashSet的基础上增加了一个总的链表,将放入的元素串在一起。

  • 具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。 此实现与 HashSet 的不同之处在于它维护一个双向链表,贯穿其所有条目。 这个链表定义了迭代顺序,即元素插入到 set 中的顺序(插入顺序)。 请注意,如果将元素重新插入到集合中,则插入顺序不会受到影响。(如果在 s.contains(e) 返回 true 后立即调用 s.add(e),则元素 e 会被重新插入到 set s 中)

  • 此实现可以让客户免遭未指定的、由HashSet提供的通常杂乱无章的排序工作,而又不致引起与TreeSet关联的成本增加。使用它可以生成一个与原来顺序相同的 set 副本,并且与原 set 的实现无关:

    void foo(Set s) {
          
          
    	Set copy = new LinkedHashSet(s);
    	...
    }
    
    • 如果模块通过输入得到一个 set,复制这个 set,然后返回由此副本决定了顺序的结果,这种情况下这项技术特别有用。(客户通常期望内容返回的顺序与它们出现的顺序相同。)
  • 此类提供所有可选的 Set 操作,并且允许 null 元素。与 HashSet 一样,它可以为基本操作(addcontainsremove)提供稳定的性能,假定哈希函数将元素正确地分布到存储段中。由于增加了维护链接列表的开支,其性能很可能会比 HashSet 稍逊一筹,不过,这一点例外:LinkedHashSet 迭代所需时间与 set 的大小 成正比,而与容量无关。HashSet 迭代很可能支出较大,因为它所需迭代时间与其容量 成正比。

  • 链接的哈希 set 有两个影响其性能的参数:初始容量加载因子initial capacity and load factor)。它们与 HashSet 中的定义极其相同。注意,为初始容量选择非常高的值对此类的影响比对 HashSet 要小,因为此类的迭代时间不受容量的影响。

构造方法

  • LinkedHashSet()
    • 构造一个带默认初始容量 (16) 和加载因子 (0.75) 的新空链接哈希 set。
  • LinkedHashSet(Collection<? extends E> c)
    • 构造一个与指定 collection 中的元素相同的新链接哈希 set。
  • LinkedHashSet(int initialCapacity)
    • 构造一个带指定初始容量和默认加载因子 (0.75) 的新空链接哈希 set。
  • LinkedHashSet(int initialCapacity, float loadFactor)
    • 构造一个带有指定初始容量和加载因子的新空链接哈希 set。

方法

  • 从类 java.util.HashSet 继承的方法 add, clear, clone, contains, isEmpty, iterator, remove, size
  • 从类 java.util.AbstractSet 继承的方法 equals, hashCode, removeAll
  • 从类 java.util.AbstractCollection 继承的方法 addAll, containsAll, retainAll, toArray, toArray, toString
  • 从类 java.lang.Object 继承的方法 finalize, getClass, notify, notifyAll, wait, wait, wait
  • 从接口 java.util.Set 继承的方法 add, addAll, clear, contains, containsAll, equals, hashCode, isEmpty, iterator, remove, removeAll, retainAll, size, toArray, toArray
public class LinkedHashT {
    
    
    public static void main(String[] args) {
    
    
        //创建一个LinkedHashSet集合:
        LinkedHashSet<Integer> lhs = new LinkedHashSet<>();
        System.out.println(lhs.add(9));//true
        lhs.add(5);
        lhs.add(8);
        System.out.println(lhs.add(9));//false 这个9没有放入到集合中
        lhs.add(6);
        System.out.println(lhs.size());//4 唯一,有序
        System.out.println(lhs); //[9, 5, 8, 6]
    }
}

猜你喜欢

转载自blog.csdn.net/m0_46530662/article/details/119769024