java-集合-set(不重复集合)知识分解——庖丁解牛版
Posted IT_ZI-O
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java-集合-set(不重复集合)知识分解——庖丁解牛版相关的知识,希望对你有一定的参考价值。
目录
4、注意事项(特殊之处,遍历无序的原因不是排序的无序,而是底层哈希值的存放地址的原因)
庖丁解牛术法总纲
吾生也有涯,而知也无涯 。以有涯随无涯,殆已!已而为知者,殆而已矣!为善无近名,为恶无近刑。缘督以为经,可以保身,可以全生,可以养亲,可以尽年。
“庖丁为文惠君解牛,手之所触,肩之所倚,足之所履,膝之所踦,砉然向然,奏刀騞然,莫不中音。合于《桑林》之舞,乃中《经首》之会。
文惠君曰:“嘻,善哉!技盖至此乎?”
庖丁释刀对曰:“臣之所好者,道也,进乎技矣。始臣之解牛之时,所见无非牛者。三年之后,未尝见全牛也。方今之时,臣以神遇而不以目视,官知止而神欲行。依乎天理,批大郤,导大窾,因其固然,技经肯綮之未尝,而况大軱乎!良庖岁更刀,割也;族庖月更刀,折也。今臣之刀十九年矣,所解数千牛矣,而刀刃若新发于硎。彼节者有间,而刀刃者无厚;以无厚入有间,恢恢乎其于游刃必有余地矣,是以十九年而刀刃若新发于硎。虽然,每至于族,吾见其难为,怵然为戒,视为止,行为迟。动刀甚微,謋然已解,如土委地。提刀而立,为之四顾,为之踌躇满志,善刀而藏之。”
——《庄子·养生主》
呔,妖怪。看法宝!
第一重境界:所见莫非全牛者
1、概述:
Set集合类似于一个罐子,程序可以依次把多个对象“丢进”Set集合,而Set集合通常不能记住元素的添加顺序。实际上Set就是Collection,只是行为略有不同(Set不允许包含重复元素)。
Set集合不允许包含相同的元素,如果试图把两个相同元素加入同一个Set集合中,则添加操作失败,add()方法返回false,且新元素不会被加入。
2、Set集合特点:
(1)、不包含重复元素的集合
(2)、没有带索引的方法,所以不能使用普通for循环遍历
3、分类(实现子类):
(1)、HashSet
(2)、TreeSet
4、所有已知实现类:
AbstractSet
,ConcurrentHashMap.KeySetView
,ConcurrentSkipListSet
,CopyOnWriteArraySet
,EnumSet
,HashSet
,JobStateReasons
,LinkedHashSet
,ReadOnlySetProperty
,ReadOnlySetPropertyBase
,ReadOnlySetWrapper
,SetBinding
,SetExpression
,SetProperty
,SetPropertyBase
,SimpleSetProperty
,TreeSet
5、注意事项
不包含重复元素的集合。 更正式地,集合不包含一对元素
e1
和e2
,使得e1.equals(e2)
,并且最多只有一个空元素。 正如其名称所暗示的那样,这个接口模拟了数学集抽象。该
Set
接口放置额外的约定,超过从继承Collection
接口,所有构造函数的合同,而位于该合同add
,equals
和hashCode
方法。 其他继承方法的声明也包括在这里以方便。 (这些声明中附带的规格已针对Set
接口进行了定制,但不包含任何其他规定。)构造函数的额外规定并不奇怪,所有构造函数都必须创建一个不包含重复元素的集合(如上所定义)。
注意:如果可变对象用作设置元素,则必须非常小心。 如果对象的值以影响
equals
比较的方式更改,而对象是集合中的元素,则不指定集合的行为。 这种禁止的一个特殊情况是,一个集合不允许将其本身作为一个元素。一些集合实现对它们可能包含的元素有限制。 例如,一些实现禁止空元素,有些实现对元素的类型有限制。 尝试添加不合格元素会引发未经检查的异常,通常为
NullPointerException
或ClassCastException
。 尝试查询不合格元素的存在可能会引发异常,或者可能只是返回false; 一些实现将展现出前者的行为,一些实现将展现出后者。 更一般来说,尝试对不符合条件的元素的操作,其完成不会导致不合格元素插入到集合中,可能会导致异常,或者可能会成功执行该选项。 此异常在此接口的规范中标记为“可选”。
6、所有方法
Modifier and Type 方法 描述 boolean
add(E e)
如果指定的元素不存在,则将其指定的元素添加(可选操作)。
boolean
addAll(Collection<? extends E> c)
将指定集合中的所有元素添加到此集合(如果尚未存在)(可选操作)。
void
clear()
从此集合中删除所有元素(可选操作)。
boolean
contains(Object o)
如果此集合包含指定的元素,则返回
true
。boolean
containsAll(Collection<?> c)
如果此集合包含指定集合的所有元素,则返回
true
。boolean
equals(Object o)
将指定的对象与此集合进行比较以实现相等。
int
hashCode()
返回此集合的哈希码值。
boolean
isEmpty()
如果此集合不包含元素,则返回
true
。Iterator<E>
iterator()
返回此集合中元素的迭代器。
static <E> Set<E>
of()
返回一个包含零个元素的不可变集合。
static <E> Set<E>
of(E e1)
返回一个包含一个元素的不可变集合。
static <E> Set<E>
of(E... elements)
返回一个包含任意数量元素的不可变集合。
static <E> Set<E>
of(E e1, E e2)
返回一个包含两个元素的不可变集合。
static <E> Set<E>
of(E e1, E e2, E e3)
返回一个包含三个元素的不可变集合。
static <E> Set<E>
of(E e1, E e2, E e3, E e4)
返回一个包含四个元素的不可变集合。
static <E> Set<E>
of(E e1, E e2, E e3, E e4, E e5)
返回一个包含五个元素的不可变集合。
static <E> Set<E>
of(E e1, E e2, E e3, E e4, E e5, E e6)
返回一个包含六个元素的不可变集合。
static <E> Set<E>
of(E e1, E e2, E e3, E e4, E e5, E e6, E e7)
返回一个包含七个元素的不可变集合。
static <E> Set<E>
of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8)
返回一个包含八个元素的不可变集合。
static <E> Set<E>
of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9)
返回一个包含九个元素的不可变集合。
static <E> Set<E>
of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10)
返回一个包含十个元素的不可变集合。
boolean
remove(Object o)
如果存在,则从该集合中删除指定的元素(可选操作)。
boolean
removeAll(Collection<?> c)
从此集合中删除指定集合中包含的所有元素(可选操作)。
boolean
retainAll(Collection<?> c)
仅保留该集合中包含在指定集合中的元素(可选操作)。
int
size()
返回此集合中的元素数(其基数)。
default Spliterator<E>
spliterator()
在此集合中的元素上创建一个
Spliterator
。Object[]
toArray()
返回一个包含此集合中所有元素的数组。
<T> T[]
toArray(T[] a)
返回一个包含此集合中所有元素的数组; 返回的数组的运行时类型是指定数组的运行时类型。
第二重境界:未尝见全牛也
HashSet
1、HashSet特点:
(1)底层数据结构是哈希表(查询速度快),使用HashCode哈希值
(2)对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致
(3)没有带索引的方法,所以不能使用普通for循环遍历
(4)由于是Set集合,所以是不包含重复元素的集合
2、HashSet集合添加一个元素的过程:
3、代码演示
package SetDemo;
import java.util.HashSet;
public class Set01 {
public static void main(String[] args) {
//导包创建对象HashSet
HashSet<String> set=new HashSet<String>();
//添加数据
set.add("aaa");
set.add("bbb");
set.add("ccc");
set.add("ddd");
//增强for循环遍历
for (String i:set){
System.out.println(i);
}
}
}
//代码输出结果
E:\\develop\\JDK\\bin\\java.exe "-javaagent:E:\\IDEA\\IntelliJ IDEA Community Edition
aaa
ccc
bbb
ddd
Process finished with exit code 0
4、注意事项(特殊之处,遍历无序的原因不是排序的无序,而是底层哈希值的存放地址的原因)
public static void main(String[] args) {
HashSet<String> set=new HashSet<String>();
set.add("hello");
set.add("world");
set.add("java");
//集合自带排序方式
System.out.println(set);
System.out.println("--------");
//迭代器方式
Iterator<String> it = set.iterator();
while (it.hasNext()){
String s = it.next();
System.out.println(s);
}
System.out.println("--------");
//增强for循环
for (String s:set){
System.out.println(s);
}
}
//输出结果
[world, java, hello]
--------
world
java
hello
--------
world
java
hello
Process finished with exit code 0
上述代码是正常输出的结果,但是以下的代码的输出,请看:
public static void main(String[] args) {
HashSet<String> set=new HashSet<String>();
set.add("6");
set.add("7");
set.add("8");
set.add("9");
set.add("10");
//集合自带排序方式
System.out.println(set);
System.out.println("--------");
//迭代器方式
Iterator<String> it = set.iterator();
while (it.hasNext()){
String s = it.next();
System.out.println(s);
}
System.out.println("--------");
//增强for循环
for (String s:set){
System.out.println(s);
}
}
//代码输出结果
[6, 7, 8, 9, 10]
--------
6
7
8
9
10
--------
6
7
8
9
10
Process finished with exit code 0
public static void main(String[] args) {
HashSet<String> set=new HashSet<String>();
set.add("a");
set.add("b");
set.add("c");
set.add("d");
set.add("e");
//集合自带排序方式
System.out.println(set);
System.out.println("--------");
//迭代器方式
Iterator<String> it = set.iterator();
while (it.hasNext()){
String s = it.next();
System.out.println(s);
}
System.out.println("--------");
//增强for循环
for (String s:set){
System.out.println(s);
}
}
//代码输出结果
[a, b, c, d, e]
--------
a
b
c
d
e
--------
a
b
c
d
e
Process finished with exit code 0
上述两则代码结果,可以看到输出结果都是有序的,而HashSet特点有一,就是不保证迭代顺序。这不就是矛盾的一处地方吗?
原因:但并不是HashSet的自相矛盾,其原因就是底层实现的特点。哈希表实现,计算哈希值,通过哈希值代替索引的作用,查询存储的值。
而哈希值的计算,有一套自己的计算规则,当一串字符串计算哈希值时,哈希值的区别也许会很大,但如果是有规律的数字、字符进行计算哈希值时,其哈希值有会时有序的。
表现出来的结果就是在控制台输出有顺序有规律的单节字符时,输出也是有规律的。
5、LinkedHashSet集合概述和特点
(1)集合特点:
哈希表和链表实现的Set接口,具有可预测的迭代顺序。
由链表保证元素有序,也就是说元素的存储和取出顺序是一致的。
由哈希表保证元素唯一,也就是说没有重复的元素。
(2)代码案例
TreeSet
1、TreeSet集合特点
(1)元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
TreeSet():根据其元素的自然排序进行排序
TreeSet(Comparator<? super E> comparator) :根据指定的比较器进行排序(2)没有带索引的方法,所以不能使用普通for循环遍历
(3)由于是Set集合,所以不包含重复元素的集合
2、注意事项
用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法
重点:如何重写方法
重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
重点:主要条件与次要条件
用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法
重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
3、代码展示(比较器排序)
package SetDemo;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
public class Set003 {
public static void main(String[] args) {
Set<News> set=new TreeSet<News>(new Comparator<News>() {
@Override
public int compare(News n1, News n2) {
int num=n1.getTitle().compareTo(n2.getTitle());
return num;
}
});
News news1=new News("中国多地遭雾霾笼罩空气质量再成热议话题");
News news2=new News("民进党台北举行“火大游行”");
News news3=new News("春节临近北京“卖房热”");
News news4=new News("春节临近北京“卖房热”");
System.out.println("新闻一与新闻二的比较:"+news1.equals(news2));
System.out.println("新闻三与新闻四的比较:"+news3.equals(news4));
System.out.println("--------");
set.add(news1);
set.add(news2);
set.add(news3);
set.add(news4);
for (int i = 0; i < set.size(); i++) {
}
Set<News> set1=new HashSet<News>();
set1.addAll(set);
for (News n:set1){
System.out.println(n);
}
System.out.println("--------");
System.out.println("集合中新闻的长度为:"+set1.size());
}
}
第三重境界:官知止而神欲行
1、哈希值(hashCode)
概念:
是Jdk根据对象的地址/String/数字算出来一串数字(int),hashCode()是Object类的方法,所以说Java的对象都可以调用这个hashCode方法返回哈希值。
特点:
(1)如果自定义类没有重写hashCode方法,那么自定义类的对象生成的哈希值是根据对象的内存地址值生成的,所以说即便两个对象的属性一样,哈希值也不一样。
(2)诉求:如果两个对象属性一样,那么两个对象哈希值也要一样,所以在自定义的类中重写了 hashCode方法(不调用Object类hashCode),是根据对象的属性生成哈希值。
(3)两个对象哈希值一样,不代表两个对象的属性一样.两个对象的属性一样,则两个对象的哈希值肯定一样。
(4)数字的哈希值是它本身。
代码展示:
public static void main(String[] args) {
//创建学生类对象,实例化
Student s1 = new Student02("盘古大神", 100, 100, 100,300);
Student s2 = new Student02("女娲娘娘", 100, 95, 90,285);
Student s3 = new Student02("天皇伏羲", 98, 100, 90,288);
Student s4 = new Student02("地皇神农", 99, 95, 90,284);
Student s5 = new Student02("人皇轩辕", 95, 98, 80,273);
//输出哈希值
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
System.out.println(s3.hashCode());
System.out.println(s4.hashCode());
System.out.println(s5.hashCode());
}
//输出结果
356573597
1735600054
21685669
2133927002
1836019240
Process finished with exit code 0
2、哈希表
概念:
散列表(Hash table,也叫哈希表),是根据键(Key)而直接访问在内存存储位置的数据结构。也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做散列函数,存放记录的数组称做散列表。
哈希表的实现:
实现哈希表我们可以采用两种方法:
1、数组+链表
2、数组+二叉树
哈希表的优缺点:
优点:
- 无论数据有多少,处理起来都特别的快
- 能够快速地进行
插入修改元素
、删除元素
、查找元素
等操作- 代码简单(其实只需要把哈希函数写好,之后的代码就很简单了)
缺点:
- 哈希表中的数据是没有顺序的
- 数据不允许重复
哈希表的学习:
如果大家想再深入学习哈希表的知识,下附链接:
ending!!!!!
以上是关于java-集合-set(不重复集合)知识分解——庖丁解牛版的主要内容,如果未能解决你的问题,请参考以下文章