目录
(1)无重复元素:添加重复元素时会被自动过滤(add() 方法返回 false),这是通过元素的 hashCode() 和 equals() 方法判断的。
(2)无序性:元素的存储顺序与添加顺序无关,且遍历顺序可能随时间变化(不保证稳定性).
(3)允许 null 元素:但最多只能存储一个 null(因为不允许重复)。
(4)高效性能:添加、删除、查询元素的平均时间复杂度为 O(1),这得益于哈希表的快速访问特性。
(5)非线程安全:多线程环境下需手动同步(如使用 Collections.synchronizedSet() 包装)。
(1)public HashSet():创建一个默认为空的HashSet
(3)public HashSet(Collection c):创建一个容器内容为c的集合。
(1)public boolean add(E e):向集合中添加元素。
(2)public boolean remove(Object o):集合中删除元素。
(3)public void clear():清空集合元素。
(4)public int size():返回集合中元素的数量。
(5)boolean addAll(Collection c): 插入一个collection 集合,插入的时候只要成功的添加一个元素,返回值就为true。
(7)boolean contains(Object o): 判断元素是否存在。
(1)有序性:与 HashSet 的无序性不同,LinkedHashSet 会通过双向链表记录元素的插入顺序,因此遍历元素时会按照添加顺序输出。有序指的是元素放入的顺序
(2)无重复元素:和所有 Set 实现类一样,LinkedHashSet 不允许存储重复元素,判断重复的逻辑与 HashSet 一致(依赖 hashCode() 和 equals() 方法)。
(3)允许 null 元素:最多只能包含一个 null 元素。
(1)有序性:元素会按照自然排序(默认)或自定义排序规则进行排序,而非插入顺序。
(2)无重复元素:与所有 Set 实现类一样,不允许重复元素,重复元素会被自动过滤。
(3)不允许 null 元素:插入 null 会抛出 NullPointerException(因为需要比较元素大小)。
(4)性能:添加、删除、查询元素的时间复杂度为 O(log n),适合需要排序且频繁操作的场景。
(5)非线程安全:多线程环境下需手动同步(如使用 Collections.synchronizedSortedSet() 包装)。
(1)public TreeSet(): 构造一个空的有序set集合
(2)public TreeSet(Comparator comparator)构造一个传入排序规则的有序Set集合
(3)public TreeSet(Collection c):构造一个集合元素为c的有序Set集合
四、HashSet、LinkedHashSet、TreeSet三者区别:
set集合:Java 中的 Set
是一种集合接口,属于 Java 集合框架(java.util
包)的一部分,它继承自 Collection
接口。Set
集合的核心特点是不允许包含重复元素,并且最多只能包含一个 null
元素(具体取决于HashSet、LinkedHashSet、TreeSet三个实现类)。
一、HashSet:
1、底层原理:
HashSet
内部通过 HashMap
实现(维护一个 HashMap
实例):存储的元素被作为 HashMap
的 key,而 value 则是一个固定的空对象(PRESENT
)。元素的去重逻辑依赖于 HashMap
的 key 不重复特性.
2、主要特性:
(1)无重复元素:添加重复元素时会被自动过滤(add()
方法返回 false
),这是通过元素的 hashCode()
和 equals()
方法判断的。
(2)无序性:元素的存储顺序与添加顺序无关,且遍历顺序可能随时间变化(不保证稳定性).
(3)允许 null 元素:但最多只能存储一个 null
(因为不允许重复)。
(4)高效性能:添加、删除、查询元素的平均时间复杂度为 O(1),这得益于哈希表的快速访问特性。
(5)非线程安全:多线程环境下需手动同步(如使用 Collections.synchronizedSet()
包装)。
3、初始化:
(1)public HashSet():创建一个默认为空的HashSet
(2)public HashSet(int initialCapacity, float loadFactor):initialCapacity
(初始容量)指哈希表在创建时的初始桶(bucket)数量,默认值为 16。容量必须是 2 的幂次方(哈希表会自动调整为最接近的 2 的幂),这是为了优化哈希计算的效率。loadFactor
(加载因子)是哈希表扩容的阈值比例,默认值为 0.75f,计算公式:扩容值 = 当前容量 × 加载因子
。当元素数量超过阈值时,哈希表会自动扩容(容量翻倍),以减少哈希冲突。
(3)public HashSet(Collection<? extends E> c):创建一个容器内容为c的集合。
4、常用方法:
(1)public boolean add(E e):向集合中添加元素。
(2)public boolean remove(Object o):集合中删除元素。
(3)public void clear():清空集合元素。
(4)public int size():返回集合中元素的数量。
(5)boolean addAll(Collection<? extends E> c): 插入一个collection 集合,插入的时候只要成功的添加一个元素,返回值就为true。
(6)boolean isEmpty():判空
(7)boolean contains(Object o): 判断元素是否存在。
HashSet<String> hs1 = new HashSet<>();
//添加元素集合
boolean b1 = hs1.add("曹操");
hs1.add("周瑜");
hs1.add("晁盖");
boolean b2 = hs1.add("曹操");
System.out.println("第一次添加元素:"+b1);
System.out.println("第二次添加元素:"+b2);
//快速生成list集合
List<String> list = Arrays.asList("小乔","大乔","诸葛亮","晁盖");
boolean b3 = hs1.addAll(list); //添加元素集合,只要成功的添加一个元素,返回值就为true
System.out.println("添加集合是否成功:"+b3);
System.out.println(hs1);
System.out.println("元素个数为:"+hs1.size());
System.out.println("元素是否存在:"+hs1.contains("小乔"));
System.out.println("元素是否为空:"+hs1.isEmpty());
//删除
boolean b1=hs1.remove("小乔");
System.out.println("删除元素是否成功:"+b1);
System.out.println(hs1);
//清空
hs1.clear();
System.out.println(hs1);
5、集合遍历:
迭代器遍历:Iterator<E> iterator()
Iterator<String> itor = hs1.iterator();
while(itor.hasNext()){
System.out.println(itor.next());
}
System.out.println("===============");
for (String str:
hs1) {
System.out.println(str);
}
二、LinkedHashSet:
1、底层原理:
基于 HashMap
实现(与 HashSet
相同),但额外通过链表记录元素顺序,本质是一个 "Linked HashMap" 的 key 集合。
2、主要特性:
(1)有序性:与 HashSet
的无序性不同,LinkedHashSet
会通过双向链表记录元素的插入顺序,因此遍历元素时会按照添加顺序输出。有序指的是元素放入的顺序
(2)无重复元素:和所有 Set
实现类一样,LinkedHashSet
不允许存储重复元素,判断重复的逻辑与 HashSet
一致(依赖 hashCode()
和 equals()
方法)。
(3)允许 null 元素:最多只能包含一个 null
元素。
(4)与HashSet 的初始化、常用方法、遍历一样
三、TreeSet:
1、底层实现:
TreeSet
内部依赖 TreeMap
实现(维护一个 TreeMap
实例):存储的元素被作为 TreeMap
的 key,value 是一个固定的空对象(PRESENT
)。排序逻辑完全依赖 TreeMap
的 key 排序特性。元素必须可比较(要么实现 Comparable
,要么提供 Comparator
),否则会抛出 ClassCastException
。排序规则需与 equals()
方法保持一致(即 compareTo()
返回 0 时,equals()
应返回 true
),否则可能出现逻辑矛盾(如元素被视为不同但排序位置相同)。
2、主要特性:
(1)有序性:元素会按照自然排序(默认)或自定义排序规则进行排序,而非插入顺序。
(2)无重复元素:与所有 Set
实现类一样,不允许重复元素,重复元素会被自动过滤。
(3)不允许 null 元素:插入 null
会抛出 NullPointerException
(因为需要比较元素大小)。
(4)性能:添加、删除、查询元素的时间复杂度为 O(log n),适合需要排序且频繁操作的场景。
(5)非线程安全:多线程环境下需手动同步(如使用 Collections.synchronizedSortedSet()
包装)。
3、初始化:
(1)public TreeSet(): 构造一个空的有序set集合
(2)public TreeSet(Comparator<? super E> comparator)构造一个传入排序规则的有序Set集合
(3)public TreeSet(Collection<? extends E> c):构造一个集合元素为c的有序Set集合
4、Set集合如何过滤重复元素
核心原理:(1)hashCode()
方法:计算元素的哈希值,用于快速定位元素在底层数据结构(如哈希表)中的存储位置。两个相同的元素必须返回相同的哈希值。(2)equals()
方法:用于精确比较两个元素是否为 "逻辑上的相同元素"。当两个元素的哈希值相同时,会通过 equals()
进一步确认是否为重复元素。
自定义对象去重的关键:对于自定义类的对象,需同时重写 hashCode()
和 equals()
方法(TreeSet
需保证排序逻辑与 equals()
一致),否则可能导致去重失效。
例如:
package com.yuan.treeset;
import java.util.Comparator;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
public class Book implements Comparable<Book> {
private String bookName;
private String authorName;
private int pageSize;
private double price;
public Book(String bookName, String authorName, int pageSize, double price) {
this.bookName = bookName;
this.authorName = authorName;
this.pageSize = pageSize;
this.price = price;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return pageSize == book.pageSize && Double.compare(book.price, price) == 0 && Objects.equals(bookName, book.bookName) && Objects.equals(authorName, book.authorName);
}
@Override
public String toString() {
return "Book{" +
"bookName='" + bookName + '\'' +
", authorName='" + authorName + '\'' +
", pageSize=" + pageSize +
", price=" + price +
'}' + "\n";
}
@Override
public int compareTo(Book o) {
int result = this.pageSize - o.pageSize;
if (result == 0) {
return this.bookName.compareTo(o.bookName);
}
return result;
}
@Override
public int hashCode() {
return Objects.hash(bookName, authorName, pageSize, price);
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getAuthorName() {
return authorName;
}
public void setAuthorName(String authorName) {
this.authorName = authorName;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
class Demo01 {
public static void main(String[] args) {
//TreeSet是可排序的,且唯一的:
// 在使用TreeSet时需要注意,一定要提供排序规则给对象(泛型类实现comparable接口或者对象提供Comparator对象)
//1.无参的构造方法,排序规则使用泛型类型这个类定义的排序规则
//2.有参构造TreeSet(Comparator comparator)排序比较规则按照传入的参数的规则为准
Set<Book> set = new TreeSet<>();
Book b1 = new Book("明朝那些事1", "当年明月1", 1231, 89.0);
Book b2 = new Book("明朝那些事2", "当年明月2", 121, 89.0);
Book b3 = new Book("明朝那些事3", "当年明月3", 11, 90.0);
Book b4 = new Book("明朝那些事4", "当年明月1", 1231, 89.0);
set.add(b1);
set.add(b2);
set.add(b3);
set.add(b4);
System.out.println(set);
}
}
class Demo02 {
public static void main(String[] args) {
//如果泛型类型不是Comparable类型,必须要提供一个Comparator对象提供比较规则。
Set<Book> set = new TreeSet<>(new Comparator<Book>() {
@Override
public int compare(Book o1, Book o2) {
//按照书的价格进行比较
if(o1.getPrice()>o2.getPrice()){
return 1;
}else if (o1.getPrice() == o2.getPrice()) {
return 0;
}else {
return -1;
}
}
});
Book b1 = new Book("明朝那些事1", "当年明月1", 1231, 89.0);
Book b2 = new Book("明朝那些事2", "当年明月2", 121, 100.0);
Book b3 = new Book("明朝那些事3", "当年明月3", 11, 90.0);
Book b4 = new Book("明朝那些事4", "当年明月1", 1231, 89.0);
set.add(b1);
set.add(b2);
set.add(b3);
set.add(b4);
System.out.println(set);
}
}
四、HashSet、LinkedHashSet、TreeSet三者区别:
实现类 | 排序特性 | 底层结构 | 时间复杂度(增删查) |
---|---|---|---|
HashSet | 无序 | 哈希表 | O (1)(平均) |
LinkedHashSet | 保持插入顺序 | 哈希表 + 链表 | O (1)(平均) |
TreeSet | 自然排序 / 自定义排序 | 红黑树 | O(log n) |