热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

java定义静态set集合_Java集合—Set(转载)

一、HashSet类HashSet简介HashSet是Set接口的典型实现,实现了Set接口中的所有方法,并没有添加额外的方法,大多数时候

一、HashSet类

HashSet简介

HashSet是Set接口的典型实现,实现了Set接口中的所有方法,并没有添加额外的方法,大多数时候使用Set集合时就是使用这个实现类。HashSet按Hash算法来存储集合中的元素。因此具有很好的存取和查找性能。

HashSet特点

1.不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化。

2.HashSet不是同步的,如果多个线程同时访问一个HashSet,则必须通过代码来保证其同步。

3.集合元素值可以是null。

除此之外,HashSet判断两个元素是否相等的标准也是其一大特点。HashSet判断两个元素相等的标准:两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法返回值也相等。

写到这里,我们就要介绍下equals()和hashCode()方法了。

equals()

equals() 的作用是用来判断两个对象是否相等。

equals() 定义在JDK的Object.java中。通过判断两个对象的地址是否相等(即,是否是同一个对象)来区分它们是否相等。源码如下:

public booleanequals(Object obj) {return (this ==obj);

}

既然Object.java中定义了equals()方法,这就意味着所有的Java类都实现了equals()方法,所有的类都可以通过equals()去比较两个对象是否相等。 但是,使用默认的“equals()”方法,等价于“==”方法。我们也可以在Object的子类中重写此方法,自定义“equals()”方法,在其中定义自己的判断逻辑,如果满足则返回true,不满足则返回false。下面我们自定义一个类 Person,并认为年龄,身高相等的两个Person对象,equals()方法比较结果相等。

public classPerson {public intage;public intheight;

@Overridepublic booleanequals(Object obj) {if (this ==obj)return true;if (obj == null)return false;if (getClass() !=obj.getClass())return false;

Person other=(Person) obj;if (age !=other.age)return false;if (height !=other.height)return false;return true;

}

}public classEqualTest {public static voidmain(String[] args){

Person p1= newPerson();

Person p2=newPerson();

System.out.println(p1.equals(p2));

}

}

输出结果:

true

下面根据“类是否覆盖equals()方法”,将它分为2类。

若某个类没有覆盖equals()方法,当它的通过equals()比较两个对象时,实际上是比较两个对象是不是同一个对象,即两个对象的内存地址是否相同。

我们可以覆盖类的equals()方法,来让equals()通过其它方式比较两个对象是否相等。通常的做法是:若两个对象的内容相等,则equals()方法返回true;否则,返回fasle。

hashCode()

hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。

hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode() 函数。但是,仅仅当创建包含某个对象的散列表时(通常是HashMap,Hashtable,HashSet),该类的hashCode() 才有用。

在散列表中,hashCode()作用是:在散列表中hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。

hashCode()也分两种情况。一种是Object类中默认的方法,另一种是在子类中重写的方法。

若某个类没有覆盖hashCode()方法,当它的通过hashCode()比较两个对象时,实际上是比较两个对象是不是同一个对象。这时,等价于通过“==”去比较这两个对象,即两个对象的内存地址是否相同。

我们可以覆盖类的hashCode()方法,来让hashCode()通过其它方式比较两个对象是否相等。通常的做法是:若两个对象的内容相等,则hashCode()方法返回true;否则,返回fasle。

通过对以上两个方法的了解。我们可以接下来学习HashSet集合中如何判断两个元素是否相等?

HashSet中判断集合元素相等

两个对象比较 具体分为如下四个情况:

如果有两个元素通过equal()方法比较返回false,但它们的hashCode()方法返回不相等,HashSet将会把它们存储在不同的位置。

如果有两个元素通过equal()方法比较返回true,但它们的hashCode()方法返回不相等,HashSet将会把它们存储在不同的位置。

如果两个对象通过equals()方法比较不相等,hashCode()方法比较相等,HashSet将会把它们存储在相同的位置,在这个位置以链表式结构来保存多个对象。这是因为当向HashSet集合中存入一个元素时,HashSet会调用对象的hashCode()方法来得到对象的hashCode值,然后根据该hashCode值来决定该对象存储在HashSet中存储位置。

如果有两个元素通过equal()方法比较返回true,但它们的hashCode()方法返回true,HashSet将不予添加。

HashSet判断两个元素相等的标准:两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法返回值也相等。

注意:HashSet是根据元素的hashCode值来快速定位的,如果HashSet中两个以上的元素具有相同的hashCode值,将会导致性能下降。所以如果重写类的equals()方法和hashCode()方法时,应尽量保证两个对象通过hashCode()方法返回值相等时,通过equals()方法比较返回true。

LinkedHashSet类

LinkedHashSet是HashSet对的子类,也是根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序,使得元素是以插入的顺序来保存的。当遍历LinkedHashSet集合里的元素时,LinkedHashSet将会按元素的添加顺序来访问集合里的元素。但是由于要维护元素的插入顺序,在性能上略低与HashSet,但在迭代访问Set里的全部元素时有很好的性能。

注意:LinkedHashSet依然不允许元素重复,判断重复标准与HashSet一致。

补充:HashSet的实质是一个HashMap。HashSet的所有集合元素,构成了HashMap的key,其value为一个静态Object对象。因此HashSet的所有性质,HashMap的key所构成的集合都具备。可以参考后续文章中HashMap的相关内容进行比对。

二、TreeSet类

TreeSet简介

TreeSet是SortedSet接口的实现类,正如SortedSet名字所暗示的,TreeSet可以确保集合元素处于排序状态。此外,TreeSet还提供了几个额外的方法。

TreeSet的方法

comparator():返回对此 set 中的元素进行排序的比较器;如果此 set 使用其元素的自然顺序,则返回null。

first():返回此 set 中当前第一个(最低)元素。

last(): 返回此 set 中当前最后一个(最高)元素。

lower(E e):返回此 set 中严格小于给定元素的最大元素;如果不存在这样的元素,则返回 null。

higher(E e):返回此 set 中严格大于给定元素的最小元素;如果不存在这样的元素,则返回 null。

subSet(E fromElement, E toElement):返回此 set 的部分视图,其元素从 fromElement(包括)到 toElement(不包括)。

headSet(E toElement):返回此 set 的部分视图,其元素小于toElement。

tailSet(E fromElement):返回此 set 的部分视图,其元素大于等于 fromElement。

TreeSet的排序方式

TreeSet中所谓的有序,不同于之前所讲的插入顺序,而是通过集合中元素属性进行排序方式来实现的。

TreeSet支持两种排序方法:自然排序和定制排序。在默认情况下,TreeSet采用自然排序。

1.自然排序

在讲自然排序之前,要先讲一下Comparable接口。

Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现该接口的类必须实现该方法,实现了该接口的类的对象就可以比较大小了。当一个对象调用该方法与另一个对象比较时,例如obj1.compareTo(obj2),如果该方法返回0,则表明两个对象相等;如果该方法返回一个整数,则表明obj1大于obj2;如果该方法返回一个负整数,则表明oj1小于obj2。

TreeSet会调用集合中元素所属类的compareTo(Object obj)方法来比较元素之间的大小关系,然后将集合元素按升序排列,即把通过compareTo(Object obj)方法比较后比较大的的往后排。这种方式就是自然排序。

Java的一些常用类已经实现了Comparable接口,并提供了比较大小的标准。例如,String按字符串的UNICODE值进行比较,Integer等所有数值类型对应的包装类按它们的数值大小进行比较。

除了这些已经实现Comparable接口类之外,如果试图把一个对象添加到TreeSet时,则该对象的类必须实现Comparable接口,否则就会出现异常。

注意:TreeSet中只能添加同一种类型的对象,否则无法比较,会出现异常。

TreeSet中判断集合元素相等

对于TreeSet集合而言,判断两个对象是否相等的唯一标准是:两个对象通过compareTo(Object obj)方法比较是否返回0——如果通过compareTo(Object obj)方法比较返回0,TreeSet则会认为它们相等,不予添加入集合内;否则就认为它们不相等,添加到集合内。

TreeSet是根据红黑树结构找到集合元素的存储位置。

2.定制排序

TreeSet的自然排序是根据集合元素中compareTo(Object obj)比较的大小,以升序排列。而定制排序是通过Comparator接口的帮助。该接口包含一个int compare(T o1,T o2)方法,该方法用于比较o1,o2的大小:如果该方法返回正整数,则表明o1大于o2;如果该方法返回0,则表明o1等于o2;如果该方法返回负整数,则表明o1小于o2。

如果要实现定制排序,则需要在创建TreeSet时,调用一个带参构造器,传入Comparator对象。并有该Comparator对象负责集合元素的排序逻辑,集合元素可以不必实现Comparable接口。下面具体演示一下这种用法:

public static voidmain(String[] args){

Person p1= newPerson();

p1.age=20;

Person p2=newPerson();

p2.age= 30;

Comparator comparator = new Comparator() {

@Overridepublic intcompare(Person o1, Person o2) {//年龄越小的排在越后面

if(o1.age

}else if(o1.age>o2.age){return -1;

}else{return 0;

}

}

};

TreeSet set = new TreeSet(comparator);

set.add(p1);

set.add(p2);

System.out.println(set);

}

[Person[age=30], Person[age=20]]

总结:无论使用自然排序还是定制排序,都可以通过自定义比较逻辑实现各种各样的排序方式。

注意:如果向TreeSet中添加了一个可变对象后,并且后面程序修改了该可变对象的实例变量,这将导致它与其他对象的大小顺序发生了改变,但TreeSet不会再次调整它们。下面程序演示这一现象:

TreeSet set = new TreeSet();

Person p1= newPerson();

p1.setAge(10);

Person p2=newPerson();

p2.setAge(30);

Person p3=newPerson();

p3.setAge(40);

set.add(p1);

set.add(p2);

set.add(p3);

System.out.println("初始年龄排序");

System.out.println(set);//p1的年龄修改成50 最大

p1.age = 60;

System.out.println("修改p1年龄后集合排序");

System.out.println(set);

p2.age= 40;

System.out.println("修改p2年龄后集合排序");

System.out.println(set);

Person p4= new Person();

其中Person实现Comparable接口,将Person对象按照年龄从小到大升序排列。

输出结果:

初始年龄排序

[Person[age=10], Person[age=30], Person[age=40]]

修改p1年龄后集合排序

[Person[age=60], Person[age=30], Person[age=40]]

修改p2年龄后集合排序

[Person[age=60], Person[age=40], Person[age=40]]

可以看到并没有发生变化,而且如果修改后进行元素删除操作可能会不成功,具体比较复杂。总之,推荐不要修改放入TreeSet集合中元素的关键实例变量。

补充:TreeSet也是非线程安全的。

三、EnumSet类

EnumSet简介

EnumSet是一个专为枚举类设计的集合类,EnumSet中的所有元素都必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet时显示或隐式地指定。EnumSet的集合元素也是有序的,EnumSet以枚举值在EnumSet类内的定义顺序来决定集合元素的顺序。

EnumSet特点

1.EnumSet集合不允许加入null元素。EnumSet中的所有元素都必须是指定枚举类型的枚举值。

2.EnumSet类没有暴露任何构造器来创建该类的实例,程序应该通过它提供的类方法来创建EnumSet对象。

EnumSet没有其他额外增加的方法,只是增加了一些创建EnumSet对象的方法。

EnumSet创建对象的方法

format,png

补充:EnumSet 也是非线程安全的。

四、HashSet、TreeSet和EnumSet的性能对比

EnumSet内部以位向量的形式存储,结构紧凑、高效,且只存储枚举类的枚举值,所以最高效。

HashSet以hash算法进行位置存储,特别适合用于添加、查询操作。

LinkedHashSet由于要维护链表,性能比HashSet差点,但是有了链表,LinkedHashSet更适合于插入、删除以及遍历操作。

TreeSet需要额外的红黑树算法来维护集合的次序,性能最次。

但是具体使用要考虑具体的使用场景:

当需要一个特定排序的集合时,使用TreeSet集合。

当需要保存枚举类的枚举值时,使用EnumSet集合。

当经常使用添加、查询操作时,使用HashSet。

当经常插入排序或使用删除、插入及遍历操作时,使用LinkedHashSet。



推荐阅读
  • 本文由编程笔记#小编为大家整理,主要介绍了源码分析--ConcurrentHashMap与HashTable(JDK1.8)相关的知识,希望对你有一定的参考价值。  Concu ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • HashMap的相关问题及其底层数据结构和操作流程
    本文介绍了关于HashMap的相关问题,包括其底层数据结构、JDK1.7和JDK1.8的差异、红黑树的使用、扩容和树化的条件、退化为链表的情况、索引的计算方法、hashcode和hash()方法的作用、数组容量的选择、Put方法的流程以及并发问题下的操作。文章还提到了扩容死链和数据错乱的问题,并探讨了key的设计要求。对于对Java面试中的HashMap问题感兴趣的读者,本文将为您提供一些有用的技术和经验。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
  • HashMap的扩容知识详解
    本文详细介绍了HashMap的扩容知识,包括扩容的概述、扩容条件以及1.7版本中的扩容方法。通过学习本文,读者可以全面了解HashMap的扩容机制,提升对HashMap的理解和应用能力。 ... [详细]
  • 1Lock与ReadWriteLock1.1LockpublicinterfaceLock{voidlock();voidlockInterruptibl ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
author-avatar
秋静222
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有