Set集合代表一个无序集合,集合中的元素不可以重复,访问集合中的元素只能根据元素本身来访问。
即Set集合无索引、不可以重复(元素唯一)、无序(存取顺序不一致)。
实现Set接口的集合主要有:HashSet、LinkedHashSet、TreeSet。
HashSet类HashSet是一种没有重复元素的无序集合,HashSet继承于AbstractSet,实现接口Set,内部使用HashMap来存储数据,数据存储在HashMap的key中,value只是同一个默认值,所以HashSet存储的值是不能重复的。
1、HashSet类的原理
(1)底层是哈希表(散列/hash)算法的封装。
(2)HashSet是线程不安全的。如果需要多线程访问,文档推荐如下:
截取的一段源码:
2、HashSet类的常用方法(父类重写了toString()方法)具体查看API
-
-
boolean
add(E e)
将指定的元素添加到此集合(如果尚未存在)。
boolean
contains(Object o)
如果此集合包含指定的元素,则返回 true 。
boolean
isEmpty()
如果此集合不包含元素,则返回 true 。
Iterator
iterator()
返回此集合中元素的迭代器。
boolean
remove(Object o)
如果存在,则从该集合中删除指定的元素。
int
size()
返回此集合中的元素数(其基数)。
-
测试demo
public static void main(String[] args) {
Set set1 = new HashSet();
set1.add("d");
set1.add("b");
set1.add("c");
set1.add("c");
set1.add("a");
System.out.println(set1.size());// 4
System.out.println(set1);// [a, b, c, d]
set1.remove("b");
System.out.println(set1);// [a, c, d]
// 遍历删除
Iterator it = set1.iterator();
while (it.hasNext()) {
Object ele = it.next();
if ("c".equals(ele)) {
it.remove();
}
}
System.out.println(set1); // [a, d]
}
3、HashSet类中元素不重复原理
由源码分析中add()方法可知,HashSet集合中添加元素,实际是作为HashMap的Key存储,
由于 HashMap 的 put() 方法添加 key-value 时,当新放入 HashMap 的 Entry 中 key 与集合中原有 Entry 的 key 相同(hashCode()返回值相等,通过 equals 比较也返回 true),新添加的 Entry 的 value 会将覆盖原来 Entry 的 value,但 key 不会有任何改变,因此如果向 HashSet 中添加一个已经存在的元素时,新添加的集合元素将不会被放入 HashMap中,原来的元素也不会有任何改变,这也就满足了 Set 中元素不重复的特性。
如果添加元素在 HashSet 中不存在的,则返回 true;如果添加的元素已经存在,返回 false。其原因在于 HashMap 的 put 方法。该方法在添加 key 不重复的键值对的时候,会返回 null。
4、HashSet中判断两个元素对象是否相同:
当添加新的对象到HashSet集合中时,二者缺一不可:
1)先判断该对象与集合对象中的hashCode值是否相同,如果不相同,则添加,否则,进行步骤2
2)再继续判断该对象与集合对象中的equals进行比较,如果返回false,则添加,否则,表示重复,不添加。
5、对象的hashCode和equals方法的重要性:
每一个存储到hash表中的对象,都得提供hashCode和equals方法,用来判断是否是同一个对象。存储在哈希表中的对象,都应该覆盖equals方法和hashCode方法,并且保证equals相等的时候,hashCode也应该相等。
public class SetDemo {
public static void main(String[] args) {
Set set = new HashSet();
set.add(new Student("赵云",1,17));
set.add(new Student("赵云2",3,18));
set.add(new Student("赵云3",1,19));
System.out.println(set.size());// 2 序号重复就判断为同一个人
System.out.println(set);
// [Student{name='赵云', studentNumber=1, age=17}, Student{name='赵云2', studentNumber=3, age=18}]
}
}
public class Student{
private String name;
private int studentNumber;
private int age;
public Student(String name, int studentNumber, int age) {
this.name = name;
this.studentNumber = studentNumber;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", studentNumber=" + studentNumber +
", age=" + age +
'}';
}
//判断学号一样的就为同一个对象 (还可以同时判断学号和名字或者年龄)
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return studentNumber == student.studentNumber;
}
@Override
public int hashCode() {
return Objects.hash(studentNumber);
}
}
LinkedHashSet类
LinkedHashSet类继承于 HashSet类
1、LinkedHashSet类的原理
(1)底层是哈希表和链表算法。
哈希表:来保证唯一性,.此时就是HashSet,在哈希表中元素没有先后顺序.
链表:来记录元素的先后添加顺序.
(2)LinkedHashSet是线程不安全的。如果需要多线程访问,参考文档
截取的一段源码:
即 LinkedHashSet 底层是依靠 LinkedHashMap 来实现数据存取的,而 LinkedHashMap 继承于 HashMap,在内部自己维护了一条双向链表用于保存元素的插入顺序,因此使得 LinkedHashSet 也具有了存取有序,元素唯一的特点。LinkedHashSet类的常用方法使用同HashSet类一样。测试demo
public static void main(String[] args) {
Set set1 = new LinkedHashSet();
set1.add("d");
set1.add("b");
set1.add("c");
set1.add("c");
set1.add("a");
System.out.println(set1.size());// 4
System.out.println(set1);// [d, b, c, a] 记录插入顺序
set1.remove("b");
System.out.println(set1);// [d, c, a]
// 遍历删除
Iterator it = set1.iterator();
while (it.hasNext()) {
Object ele = it.next();
if ("c".equals(ele)) {
it.remove();
}
}
System.out.println(set1); // [d, a]
}
TreeSet类
TreeSet是基于TreeMap实现的一个保证元素有序的集合,所以弄清楚TreeMap是关键。
注意:必须保证TreeSet集合中的元素对象是相同的数据类型,否则报错,
1、TreeSet类的原理
(1)底层是采用红黑树算法,会对存储的元素默认使用自然排序(从小到大).
(2)TreeSet是线程不安全的。如果需要多线程访问,参考文档
截取的一段源码:
2、TreeSet类的常用方法,具体查看api
-
-
boolean
add(E e)
将指定的元素添加到此集合(如果尚未存在)。
E
first()
返回此集合中当前的第一个(最低)元素。
Iterator
iterator()
以升序返回该集合中的元素的迭代器。
E
last()
返回此集合中当前的最后(最高)元素。
-
测试demo
public static void main(String[] args) {
TreeSet s = new TreeSet();
s.add("c");
s.add("f");
s.add("6");
s.add("9");
System.out.println(s); // [6, 9, c, f]
System.out.println(s.first()); // 6
// 遍历删除
Iterator it = s.iterator();
while (it.hasNext()) {
Object ele = it.next();
if ("c".equals(ele)) {
it.remove();
}
}
System.out.println(s); // [6, 9, f]
}
3、TreeSet的排序规则
1)自然排序(从小到大):
TreeSet调用集合元素的compareTo方法来比较元素的大小关系,然后将集合元素按照升序排列(从小到大).
注意:要求TreeSet集合中对象元素得实现 java.util.Comparable接口(可比较的)
覆盖 public int compareTo(Object o)方法,在该方法中编写比较规则(当前对象(this)和参数对象o做比较)。
在TreeSet的自然排序中,认为如果两个对象做比较的compareTo方法返回的是0,则认为是同一个对象.
public class ListDemo {
public static void main(String[] args) {
Set set = new TreeSet();
set.add(new Student("赵云",1,17));
set.add(new Student("赵云2",3,19));
set.add(new Student("赵云3",5,18));
set.add(new Student("赵云4",6,18));
System.out.println(set);
//[Student{name='赵云', studentNumber=1, age=17}, Student{name='赵云3', studentNumber=5, age=18}, Student{name='赵云2', studentNumber=3, age=19}]
}
}
public class Student implements Comparable{
private String name;
private int studentNumber;
private int age;
public Student(String name, int studentNumber, int age) {
this.name = name;
this.studentNumber = studentNumber;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", studentNumber=" + studentNumber +
", age=" + age +
'}';
}
// 自定义的编写比较规则 (按照年龄来排序) 自然排序
@Override
public int compareTo(Student o) {
if (this.age > o.age) {
return 1;
} else if (this.age < o.age) {
return -1;
} else {
return 0;
}
}
}
2)定制排序(从大到小,按照名字的长短来排序):
在TreeSet构造器中传递 java.lang.Comparator对象,并覆盖 public int compare(Object o1, Object o2)再编写比较规则。
public class ListDemo {
public static void main(String[] args) {
Set set = new TreeSet(new Comparator() {
// 自定义编写 定制排序的规则.名字长度升序比较器
@Override
public int compare(Student o1, Student o2) {
if (o1.getName().length() > o2.getName().length()) {
return 1;
} else if (o1.getName().length() < o2.getName().length()) {
return -1;
} else {
if (o1.getAge() < o2.getAge() ) { // 当名字长度相等的时候 按年龄降序来排序
return 1;
} else if (o1.getAge() > o2.getAge() ) {
return -1;
} else {
return 0;
}
}
}
});
set.add(new Student("赵云",1,17));
set.add(new Student("赵云2",3,19));
set.add(new Student("赵云3",5,18));
set.add(new Student("赵云4",6,18));
System.out.println(set);
//[Student{name='赵云', studentNumber=1, age=17}, Student{name='赵云2', studentNumber=3, age=19}, Student{name='赵云3', studentNumber=5, age=18}]
}
}
class Student{
private String name;
private int studentNumber;
private int age;
public Student(String name, int studentNumber, int age) {
this.name = name;
this.studentNumber = studentNumber;
this.age = age;
}
// get/set/toString方法
}
3)对于TreeSet集合来说,要么使用自然排序,要么使用定制排序.
判断两个对象是否相等的规则:
自然排序: compareTo方法返回0;
定制排序: compare方法返回0;
二、Map接口Map接口采用键值对Map的存储方式,保存具有映射关系的数据,因此,Map集合里保存两组值,一组值用于保存Map里的key,另外一组值用于保存Map里的value,key和value可以是任意引用类型的数据。key值不允许重复。如果添加key-value对时Map中已经有重复的key,则新添加的value会覆盖该key原来对应的value。
Map的常用实现类:
1)HashMap类
采用哈希表算法,此时Map中的key不会保证添加的先后顺序,key也不允许重复. null 可作为键和值
key判断重复的标准是: key1和key2是否equals为true,并且hashCode相等.
2)TreeMap类
采用红黑树算法,此时Map中的key会按照自然顺序或定制排序进行排序,,key也不允许重复. null 不可作为键,可作为值
key判断重复的标准是:compareTo/compare的返回值是否为0.
3)LinkedHashMap类
采用链表和哈希表算法,此时Map中的key会保证先后添加的顺序,key不允许重复.
key判断重复的标准和HashMap中的key的标准相同.
4)Hashtable类
采用哈希表算法,是HashMap的前身(古老的集合)。所有的方法都使用synchronized修饰符,线程安全的,但是性能相对HashMap较低. null 不可作为键和值
5)Properties类
Hashtable的子类,此时要求key和value都是String类型,用来加载资源文件
一般的,我们定义Map时,key都使用不可变的类(String),把key作为value的唯一名称.HashMap和TreeMap以及LinkedHashMap都是线程不安全的,但是性能较高。如果需要多线程访问,文档推荐如下:
解决方案: Map m = Collections.synchronizedMap(Map对象);
Map常见方法,具体查看API
-
-
boolean
containsKey(Object key)
如果此映射包含指定键的映射,则返回 true 。
boolean
containsValue(Object value)
如果此地图将一个或多个键映射到指定的值,则返回 true 。
Set
entrySet()
返回此地图中包含的映射的
Set
视图。V
get(Object key)
返回到指定键所映射的值,或
null
如果此映射包含该键的映射。boolean
isEmpty()
如果此地图不包含键值映射,则返回 true 。
default void
forEach(BiConsumer
关注打赏
-
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?