HashSet集合
集合简介
概述
什么是集合呢?集合规范而言是一类引用数据类型。当我们有多个数据需要存储的时候我们一般使用数组来存储。但数组有一个很不讨喜的特点就是数组在初始化的时候其长度就被定死了,不可改变。其存储的数量不变,因此在使用的时候一般用于存储数量不变的场景中。当遇到存储数量需要改变的时候数组就显得不太合适。因此我们引入了集合!
作用
集合,可以保存数量不确定的数据,更重要的是,集合能保存具有映射关系的数据。集合中只能保存对象(对象的引用变量),而数组元素可以是基本数据类型的值,也可以是对象(对象的引用变量)。
集合类
集合类,主要由两个接口派生而来:Collection 和 Map
Collection接口
其子类关系图如下:
Collection 接口派生的子接口:Set 和 List 接口
Java 提供的队列实现:Queue(类似 List)

Collection集合
Collection本身是接口不能实例化对象,我们使用时需要使用向上转型,使用接口接收子类对象来操作Collection接口中特有的方法。
Collection中常用方法
| 方法 | 作用 |
|---|---|
| add(Object obj) | 在集合中,添加一个元素 |
| addAll(Collection c) | 把集合 c 中的所有元素添加到指定的集合中。 |
| clear() | 清除集合中所有元素。 |
| contains(Object obj) | 常用于判断集合中是否包含指定元素。 |
| containsAll(Collection c) | 常用于判断某集合中是否包含指定的 c 集合中的所有元素。 |
| isEmpty() | 判断集合是否为空。 |
| iterator() | 获取一个 Iterator 迭代器对象,用来遍历集合里的元素。 |
| remove(Object obj) | 删除指定元素 |
| removeAll(Collection c) | 在某集合中删除指定 c 集合中包含的所有元素。 |
| size() | 获取集合中元素的个数。length() 获取长度 |
| toArray() | 把集合转成一个数组,集合元素会变成数组元素。 |
这些常用的方法,都可以通过开发者 API 文档中找到,非常多但不建议死记硬背,无非就是添加对
象、删除对象、清空集合、判断集合是否为空等几个经典动作。建议,初学阶段要多去了解每个方法
的作用,才能更好地知道都有哪些功能将来能用到实际开发中。
演示用例
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionDemo {
public static void main(String[] args) {
//注意导包需要导入java.util包下
//Collection本身是 无序可重复
//向上转型 调用共有的方法和属性 以实现子类ArrayList()为例
Collection c1 = new ArrayList();
Collection c2 = new ArrayList();
//添加 add() 只能添加引用数据类型 因此实际添加的是基本数据类型的包装类类型
c1.add("权力的游戏");
c1.add(true);
c1.add(188);
c1.add(222);
c2.add(16.5);
c2.add(188);
c2.add(222);
//直接添加集合内的数据 addAll
c1.addAll(c2);
//集合内的数据可以直接打印输出
System.out.println(c1);
//集合长度size()
System.out.println(c1.size());
//判断
//判断此集合是否为空 为空返回true
System.out.println(c1.isEmpty());
//判断此元素是否存在于此集合contains() 存在返回true
System.out.println(c1.contains(188));
//判断此是否存在于此集合contains() 存在返回true
System.out.println(c1.containsAll(c2));
//移除单个元素和移除集合
//单个移除 移除发现的第一个
c1.remove(188);
System.out.println(c1);
//集合移除 移除此集合中所有的元素 包含原集合中重复的元素
c1.removeAll(c2);
System.out.println(c1);
//清空全部元素 clear()
c1.clear();
System.out.println(c1);
//转换为数组 toArray()
System.out.println(c2.toArray());
Object[] object = c2.toArray();
System.out.println(object[1]);
//遍历 因为Collection本身是无序的 不能通过下标定(索引)定位元素 故遍历一般有三种方式
System.out.println("foreach-------------------------------------------------------");
//foreach
for(Object obj:c2) {
System.out.println(obj);
}
System.out.println("迭代器-----------------------------------------------------------");
//迭代器
//1.得到迭代器
Iterator i2 = c2.iterator();
//执行过程 1.判断游标后面是否存在元素 存在返回ture
while(i2.hasNext()) {
//执行过程 2.返回游标的下一个元素 并将游标后移
System.out.println(i2.next());
}
System.out.println("Lambda--------------------------------------------------");
//Lambda 表达式遍历集合
c2.forEach(obj -> System.out.println(obj));
}
}

Set集合
Set 集合,元素是无序的,而且不能有重复的。
常用的实现子类有HashSet、LinkedHash、SetTreeSet
HashSet实现类
特点
◕添加的元素是无序的、不可重复。
◕HashSet 不是同步的,如果多个线程同时访问一个 HashSet,比如在修改时,一定要手动通过代码来保证同步,也就是保证安全。
◕集合元素值可以是 null。
主要特点便是插入的数据不可重复
示例
import java.util.HashSet;
import java.util.Iterator;
public class HashsetDemo {
public static void main(String[] args) {
//Hashset 存储数据唯一(不可重复) 无序
HashSet<String> student = new HashSet<String>();
student.add("张三");
student.add("张三");//重复的数据不会被添加
student.add("李四");
student.add("王五");
student.add("宋六");
System.out.println(student);
Iterator<String> iterator = student.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}

LinkedHashSet实现类
特点
••LinkedHashSet 从名字上看,跟“链式”有关,它也是根据元素的 hashCode 值来决定元素的存储位置,但还多使用链表来维护元素的顺序。
••访问它的时候,会根据元素的添加顺序来访问集合中的元素,有个好处,访问时的性能会很好。但,就因为是链式的结构,在插入数据的时候,性能略差于 HashSet,插入元素时不建议使用。
总结
访问多使用LinkedHashSet 插入多使用HashSet
示例
import java.util.LinkedHashSet;
public class LinkHashSetDemo {
public static void main(String[] args) {
//无序唯一
//链表结构 与hashset相比 访问快 插入慢
LinkedHashSet<String> teacher = new LinkedHashSet<String>();
teacher.add("张老师");
teacher.add("张老师");//重复数据不会被插入
teacher.add("王老师");
teacher.add("李老师");
teacher.add("高老师");
teacher.add("刘老师");
System.out.println(teacher);
teacher.remove("高老师");//删除高老师
for(String str:teacher) {
System.out.println(str);
}
}
}
存储对象 并修改重复判断标准
集合可以存储引用数据类型自然可以存储对象
那么我们常规判断对象是否相同是是判断对象的内存地址是否相同
在Mpa中也是如此 但是有时这种判断不符合我们的显示需求
以员工类举例而言 我们的判断标准一般是以员工编号为准
往 HashSet 集合中添加一个元素的时候,默认调用 hashCode() 方法得到该对象的 hashCode 值,用
于决定该对象在 HashSet 中存放的位置。
如果两个元素,通过 equals() 方法比较返回 true,但 hashCode() 返回的值不一样,说明两个元素是不
一样的,则允许添加。
所以,HashSet 集合判断两个元素是否相等,就是通过 equals() 方法比较,还有 hashCode 值也一起
比较。
因此我们首先需要重写员工类中的equals()方法比较员工编号的值 然后需要重写hashCode()方法返回固定的值
示例
import java.util.HashSet;
import java.util.Iterator;
public class PersonnelDemo {
String number;//员工编号
String name;
int age;
//使用快捷方式生成构造函数 用于赋予初始值
public PersonnelDemo(String number, String name, int age) {
super();
this.number = number;
this.name = name;
this.age = age;
}
public PersonnelDemo() {
}
//重写equlas()将比较对象由变为员工对象
@Override
public boolean equals(Object obj) {
// TODO 自动生成的方法存根
PersonnelDemo personnel = (PersonnelDemo)obj;
return this.number.equals(personnel.number);
}
//重写hashcode() 确定返回的值都唯一
@Override
public int hashCode() {
// TODO 自动生成的方法存根
return 1000;
}
//快捷toString 方便打印输出
@Override
public String toString() {
return "PersonnelDemo [number=" + number + ", name=" + name + ", age=" + age + "]";
}
public static void main(String[] args) {
PersonnelDemo zhangsan= new PersonnelDemo("001","张三",28);
PersonnelDemo zhangsan2= new PersonnelDemo("001","张三",28);//在正常存储的时候因为是两个对象 会存储成功 为了满足需求需重写 equals() hashcode()
PersonnelDemo lisi= new PersonnelDemo("002","李四",23);
PersonnelDemo wangwu= new PersonnelDemo("003","王五",23);
PersonnelDemo songliu= new PersonnelDemo("004","宋六",26);
HashSet<PersonnelDemo> h = new HashSet<PersonnelDemo>();
h.add(zhangsan);
h.add(zhangsan2);//不会存储成功
h.add(lisi);
h.add(wangwu);
h.add(songliu);
System.out.println(h);
System.out.println("迭代器输出-------------------------------------------");
//遍历
Iterator< PersonnelDemo> iterator = h.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}

TreeSet实现类
有个强悍的特点,它可以进行排序(并不代表TreeSet是有序的),因为 TreeSet 是 SortedSet 接口的实现类。
TreeSet 可保证元素处于排序的状态,所以 TreeSet 采用的是红黑树的数据结构来存储集合元素的。
排序规则有:自然排序(默认)和定制排序。
示例 (自然排序)
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
//存储红黑二叉树数据结构 会对数进行排序存储 但是依然是无序的
TreeSet ts = new TreeSet();
ts.add(16);
ts.add(7);
ts.add(4);
ts.add(12);
ts.add(17);
System.out.println(ts);
for(Object object : ts) {
System.out.println(object);
}
}
}

定制排序(了解几乎几乎不用)
如果需要实现定制排序,则需要在创建 TreeSet 集合对象时,提供一个 Comparator 对象与该 TreeSet
集合关联,由此对象负责集合元素的排序逻辑。因为它是一个函数式接口,可以使用 Lambda 表达式来
替代 Comparator 对象。
示例(定制降序)
import java.util.TreeSet;
class M {
int age;
public M(int age) {
this.age = age;
}
public String toString() {
return "M [age:" + age + "]";
}
}
public class TreeSetDemoTwo {
public static void main(String[] args) {
// 此处 Lambda 表达式的目标类型是 Comparator
TreeSet ts = new TreeSet((o1, o2) -> {
// 此处,将两个变量直接强转
M m1 = (M) o1;
M m2 = (M) o2;
// 根据M对象的age属性来决定大小,age越大,M对象反而越小
return m1.age > m2.age ? -1 : m1.age < m2.age ? 1 : 0;
});
ts.add(new M(5));
ts.add(new M(-3));
ts.add(new M(9));
System.out.println(ts);
}
}
EnumSet实现类
❤EnumSet 中的元素都必须是指定枚举类型的枚举值,EnumSet 以枚举值定义顺序来决定集合元素的具体顺序。
❤EnumSet 内部主要以位向量的形式存储,特点是非常紧凑、高效、占用内存小、运行效率很好,所以执行速度非常快,特别适合批量操作。
❤EnumSet 不能添加 null,否则会报空指针异常。
❤EnumSet 需要使用类方法来创建对象实例。
示例
import java.util.EnumSet;
import java.util.Iterator;
public class EnumSetDemo {
//创建一个Number枚举
enum Number{ONE,TWO,THREE,FOR}
//allof() 获取所有元素
public static void main(String[] args) {
EnumSet es= EnumSet.allOf(Number.class);
System.out.println(es);
//迭代器循环输出
System.out.println("迭代器------------------------");
Iterator<Number> iterator = es.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
//noneOf创建一个空的EnumSet集合
EnumSet esTwo= EnumSet.noneOf(Number.class);
esTwo.add(Number.ONE);//添加单个元素
System.out.println(esTwo);
}
}

Java集合框架详解与示例
1401

被折叠的 条评论
为什么被折叠?



