Java集合概述、Set集合(HashSet类、LinkedHashSet类、TreeSet类、EnumSet类)
Java集合概述、Set集合(HashSet类、LinkedHashSet类、TreeSet类、EnumSet类)
1.Java集合概述
1)数组可以保存多个对象,但数组长度不可变,一旦在初始化数组时指定了数组长度,这个数组长度就是不可变的,如果需要保存数量变化的数据,数组就有点无能为力了;而且数组无法保存具有映射关系的数据。为了保存数量不确定的数据,以及保存具有映射关系的数据,Java提供了集合类。集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类。
2)Java集合类可用于存储数量不等的多个对象,并可以实现常用的数据结构,如栈、队列等。还可以用于保存具有映射关系的关联数组。Java集合大致可以分为Set、List、Map三种体系,其中Set代表无序、不可重复的集合;List代表有序、重复的集合;Map则代表具有映射关系的集合。Queue体系集合代表一种队列集合实现。
3)集合类和数组不一样,数组元素既可以是基本类型的值,也可以是对象(实际上保存的是对象的引用变量);而集合类里只能保存对象(实际上保存的是对象的引用变量)。
4)Java集合类主要由两个接口派生出:Collection和Map。Set和List接口是Collection接口派生的两个子接口,他们分别代表了无序集合和有序集合;Queue是Java提供的队列实现。Map实现类用于保存具有映射关系的数据。Map保存的每项数据都是key-value对,也就是由key和value两个值组成。Map里的key是不可重复的,key用于标识集合里的每项数据,如果需要查阅Map中的数据时,总是根据Map的key来获取。
5)Collection接口是List、Set和Queue接口的父接口,该接口里定义的方法既可以用于操作Set集合、也可以用于操作List集合和Queue集合。
boolean add(Object o):该方法用于向集合里添加一个元素。
boolean addAll(Collection c):该方法把集合c里的所有元素添加到指定集合里。
void clear():清除集合里的所有元素,将集合长度变为0。
boolean contains(Object o):返回集合里是否包含指定元素。
boolean containsAll(Collection c):返回集合里是否包含集合c里的所有元素。
boolean isEmpty():返回集合是否为空。当集合长度为0时返回true,否则返回false。
Iterator iterator():返回一个Iterator对象,用于遍历集合里的元素。
boolean remove(Object o):删除集合中的指定元素o,当集合中包含了一个或多个元素o时,这些元素将被删除,该方法将返回true。
boolean removeAll(Collection c):将集合中删除集合c里包含的所有元素(相当于用调用该方法的集合减集合c),如果删除了一个或一个以上的元素,则该方法返回true。
boolean retainAll(Collection c):将集合中删除集合c里不包含的元素(相当于把调用该方法的集合变成该集合的集合c的交集),如果该操作改变了调用该方法的集合,则该方法返回true。
int size():该方法返回集合里元素的个数。
Object[] toArray():该方法把集合转换成一个数组,所有的集合元素变成对应的数组元素。
eg:

package cn.it.lsl; import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet; public class CollectionTest {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add("小明");
c.add(6);
System.out.println("c集合的元素个数为:"+c.size());
c.remove(6);
System.out.println("c集合的元素个数为:"+c.size());
System.out.println("c集合是否包含\"小明\"字符串:"+c.contains("小明"));
c.add("JavaEE");
System.out.println("c集合的元素:"+c); Collection books = new HashSet();
books.add("JavaEE");
books.add("Android");
System.out.println("c集合是否完全包含books集合?"+c.containsAll(books));
c.removeAll(books);
System.out.println("c集合的元素:"+c);
c.clear();
System.out.println("c集合的元素:"+c);
//books集合里只剩下c集合里也包含的元素
books.retainAll(c);
System.out.println("books集合的元素:"+books); }
}

6)Iterator接口遍历集合元素
Iterator接口也是Java集合框架的成员,主要用于遍历Collection集合中的元素,Iterator对象也被称为迭代器。
Iterator接口里定义了如下三个方法:
boolean hasNext():如果被迭代的集合元素还没有被遍历,则返回true。
Object next():返回集合里的下一个元素。
void remove():删除集合里上一次next方法返回的元素。
eg:

package cn.it.lsl; import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator; public class IteratorTest {
public static void main(String[] args) {
Collection books = new HashSet();
books.add("Java ee");
books.add("Java");
books.add("Andrroid");
//获取books集合对应的迭代器
Iterator it = books.iterator();
while(it.hasNext()){
//it.next()方法返回的数据类型是Object类型
String book = (String)it.next();
System.out.println(book);
if(book.equals("Java")){
it.remove();
}
book = "测试字符串";
}
System.out.println(books);
}
}

如果要创建Iterator对象,则必须有一个被迭代的集合。

package cn.it.lsl; import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator; public class IteratorTest {
public static void main(String[] args) {
Collection books = new HashSet();
books.add("Java ee");
books.add("Java");
books.add("Android");
//获取books集合对应的迭代器
Iterator it = books.iterator();
while(it.hasNext()){
//it.next()方法返回的数据类型是Object类型
String book = (String)it.next();
System.out.println(book);
if(book.equals("Android")){
//it.remove();
books.remove(book);
}
//book = "测试字符串";
}
//System.out.println(books);
}
}

当使用Iterator迭代访问Collection集合元素时,Colleection集合里的元素不能被改变,只有通过Iterator的remove方法删除上一次next方法返回集合元素才可以。

package cn.it.lsl; import java.util.Collection;
import java.util.HashSet; public class ForeachTest {
public static void main(String[] args) {
Collection books = new HashSet();
books.add("Java ee");
books.add("Java");
books.add("Android");
for(Object obj : books){
String book = (String)obj;
System.out.println(book);
if(book.equals("Android")){
//以下代码会引发异常
//books.remove(book);
}
}
System.out.println(books);
}
}

2.Set集合
Set集合与Collection基本上完全一样,它没有提供任何额外的方法。实际上Set就是Collection,只是行为略有不同。(Set不允许包含重复元素)。
Set集合不允许包含相同的元素,如果试图把两个相同的元素加入同一个Set集合中,则添加操作失败。

package cn.it.lsl; import java.util.HashSet;
import java.util.Set; public class SetTest {
public static void main(String[] args) {
Set books = new HashSet();
books.add(new String("java"));
boolean result = books.add(new String("java"));
System.out.println(result + "-->" + books);
}
}

1)HashSet类
(1)HashSet是Set接口的实现。HashSet按Hash算法来存储集合中的元素,具有很好的存取和查找性能。
(2)HashSet不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化。
(3)当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据该HashCode值决定该对象在HashSet中的存储位置。如果有两个元素
通过equals()方法比较返回true,但它们的hashCode()方法返回值不相等,HashSet将会把它们存储在不同的位置,依然可以添加成功。即,HashSet集合判断两个元素相等的标准是两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法返回值也相等。

package cn.it.lsl; import java.util.HashSet; class A{
public boolean equals(Object obj){
return true;
}
} class B{
public int hashCode(){
return 1;
}
} class C{
public int hashCode(){
return 2;
}
public boolean equals(Object obj){
return true;
}
} public class HashSetTest {
public static void main(String[] args) {
HashSet books = new HashSet();
books.add(new A());
books.add(new A());
books.add(new B());
books.add(new B());
books.add(new C());
books.add(new C());
System.out.println(books);
}
}

注意问题:当把一个对象放入HashSet中时,如果需要重写该对象对应类的equals()方法,则也应该重写其hashCode()方法。其规则是:如果两个对象通过equals()方法比较返回true,则两个对象的hashCode值也应该相同。
重写hashCode()方法的基本规则:
1)在程序运行过程中,同一个对象多次调用hashCode()方法应该返回相同的值。
2)当两个对象通过equals()方法比较返回true时,这两个对象的hashCode()方法应返回相等的值。
3)对象中用作equals()方法比较标准的Field,都应该用来计算hashCode值。
如果向HashSet中添加一个可变对象后,后面程序修改了该可变对象的Field,则可能导致它与集合中的其他元素相同,这就可能导致HashSet中包含两个相同的对象。

package cn.it.lsl; import java.util.HashSet;
import java.util.Iterator; class R{
int count;
public R(int count){
this.count = count;
}
public String toString(){
return "R[count:" + count + "]";
}
public boolean equals(Object obj){
if(this == obj)
return true;
if(obj != null && obj.getClass() == R.class){
R r = (R)obj;
if(r.count == this.count){
return true;
}
}
return false;
}
public int hashCode(){
return this.count;
}
}
public class HashSetTest2 {
public static void main(String[] args) {
HashSet hs = new HashSet();
hs.add(new R(5));
hs.add(new R(-3));
hs.add(new R(9));
hs.add(new R(-2));
System.out.println(hs);
Iterator it = hs.iterator();
R first = (R)it.next();
first.count = -3;
System.out.println(hs);
hs.remove(new R(-3));
System.out.println(hs);
System.out.println("hs是否包含count为-3的R对象?" + hs.contains(new R(-3)));
System.out.println("hs是否包含count为5的R对象?" + hs.contains(new R(5)));
}
}

当向HashSet中添加可变对象时,必须十分小心。如果修改HashSet集合中的对象,有可能导致该对象与集合中的其他对象相等,从而导致HashSet无法准确访问该对象。
2)LinkedHashSet类
HashSet还有一个子类LinkedHashSet,LinkedHashSet集合也是根据元素的hashCode值来决定元素的存储位置,但它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的。
也就是说,当遍历LinkedHashSet集合里的元素时,LinkedHashSet将会按元素的添加顺序来访问集合里的元素。

package cn.it.lsl; import java.util.LinkedHashSet; public class LinkedHashSetTest {
public static void main(String[] args) {
LinkedHashSet books = new LinkedHashSet();
books.add("java");
books.add("Android");
System.out.println(books);
books.remove("java");
books.add("java");
System.out.println(books);
}
}

输出LinkedHashSet集合的元素时,元素的顺序总是与添加顺序一致。
虽然LinkedHashSet使用了链表记录集合元素的添加顺序,但LinkedHashSet依然是HashSet,因此它依然不允许集合元素重复。
3)TreeSet类
TreeSet是SortedSet接口的实现类,可以确保集合元素处于排序状态。
TreeSet中的几个方法:
Object first():返回集合中的第一个元素。
Object last():返回集合中的最后一个元素。
Object lower(Object e):返回集合中位于指定元素之前的元素(即小于指定元素的最大元素,参数元素不需要是TreeSet集合里的元素)。
Object higher(Object e):返回集合中位于指定元素之后的元素(即大于指定元素的最小元素,参数元素不需要是TreeSet集合里的元素)。
SortedSet subSet(formElement,toElement):返回次Set的子集合,范围从formElement(包含)到toElement(不包含)。
SortedSet headSet(toElement):返回此Set的子集,由小于toElement的元素组成。
SortedSet tailSet(fromElement):返回此Set的子集,由大于或等于fromElement的元素组成。

package cn.it.lsl; import java.util.TreeSet; public class TreeSetTree {
public static void main(String[] args) {
TreeSet nums = new TreeSet();
nums.add(5);
nums.add(2);
nums.add(10);
nums.add(-9);
System.out.println(nums);
System.out.println(nums.first());
System.out.println(nums.last());
System.out.println(nums.headSet(4)); //不包含4
System.out.println(nums.tailSet(5)); //包含5
System.out.println(nums.subSet(-3, 4));
}
}

4)EnumSet类
EnumSet是一个专为枚举类设计的集合类,EnumSet中的所有元素都必须是指定枚举类型的枚举值。
EnumSet类没有暴露任何构造器来创建该类的实例,程序应该通过它提供的static方法来创建EnumSet对象。
static EnumSet allOf(Class elementType):创建一个包含指定枚举类里所有枚举值的EnumSet集合。
static EnumSet complementOf(EnumSet s):创建一个其元素类型与指定EnumSet里元素类型相同的EnumSet集合,新EnumSet集合包含原EnumSet集合所不包含的、此枚举类剩下的枚举值(即新EnumSet集合和原EnumSet集合的集合元素加起来就是该枚举类的所有枚举值)。
static EnumSet copyOf(Collection c):使用一个普通集合来创建EnumSet集合。
static EnumSet copyOf(EnumSet s):创建一个与指定EnumSet具有相同元素类型、相同集合元素的EnumSet集合。
static EnumSet noneOf(Class elementType):创建一个元素类型为指定枚举类型的空EnumSet。
static EnumSet of(E first, E...rest):创建一个包含一个或多个枚举值的EnumSet集合,传入的多个枚举值必须属于同一个枚举类。
static EnumSet range(E from, E to):创建一个包含从from枚举值到to枚举值范围内所有枚举值的EnumSet集合。

package cn.it.lsl; import java.util.EnumSet; enum Season{
SPRING,SUMMER,FAIL,WINTER
}
public class EnumSetTest {
public static void main(String[] args) {
EnumSet es1 = EnumSet.allOf(Season.class);
System.out.println(es1);
EnumSet es2 = EnumSet.noneOf(Season.class);
System.out.println(es2);
es2.add(Season.WINTER);
es2.add(Season.SPRING);
System.out.println(es2);
EnumSet es3 = EnumSet.of(Season.SUMMER , Season.WINTER);
System.out.println(es3);
EnumSet es4 = EnumSet.range(Season.SUMMER, Season.WINTER);
System.out.println(es4);
EnumSet es5 = EnumSet.complementOf(es4);
System.out.println(es5);
}
}

复制另一个EnumSet集合中的所有元素来创建新的EnumSet集合,或者复制另一个Collection集合中的所有元素来创建新的EnumSet集合。当复制Collection集合中的所有元素来创建新的EnumSet集合时,要求Collection集合中的所有元素必须是同一个枚举类的枚举值。

package cn.it.lsl; import java.util.Collection;
import java.util.EnumSet;
import java.util.HashSet; public class EnumSetTest2 {
public static void main(String[] args) {
Collection c = new HashSet();
c.clear();
c.add(Season.FAIL);
c.add(Season.SPRING);
EnumSet enumSet = EnumSet.copyOf(c);
System.out.println(enumSet);
// c.add("java");
// c.add("Android");
// enumSet = EnumSet.copyOf(c);
}
}

当试图复制一个Collection集合里的元素来创建EnumSet集合时,必须保证Collection集合里的所有元素都是同一个枚举类的枚举值。
总结:
HashSet的性能总是比TreeSet好(特别是最常用的添加、查询元素等操作),因为TreeSet需要额外的红黑树算法来维护集合元素的次序。只有当需要一个保持排序的Set时,才应该使用TreeSet,否则都应该使用HashSet。
对于普通的插入、删除操作,LinkedHashSet比HashSet要略微慢一点,这是由维护链表所带来的额外开销造成的;不过,因为有了链表,遍历LinkedHashSet会更快。
EnumSet是所有Set实现类中性能最好的,但它只能保存同一个枚举类的枚举值作为集合元素。
Java集合概述、Set集合(HashSet类、LinkedHashSet类、TreeSet类、EnumSet类)的更多相关文章
- Java集合框架(二)—— HashSet、LinkedHashSet、TreeSet和EnumSet
Set接口 前面已经简绍过Set集合,它类似于一个罐子,一旦把对象'丢进'Set集合,集合里多个对象之间没有明显的顺序.Set集合与Collection基本上完全一样,它没有提供任何额外的方法. Se ...
- Java中的集合HashSet、LinkedHashSet、TreeSet和EnumSet(二)
Set接口 前面已经简绍过Set集合,它类似于一个罐子,一旦把对象'丢进'Set集合,集合里多个对象之间没有明显的顺序.Set集合于Collection基本上完全一样,它没有提供任何额外的方法. Se ...
- Java集合系列(三):HashSet、LinkedHashSet、TreeSet的使用方法及区别
本篇博客主要讲解Set接口的三个实现类HashSet.LinkedHashSet.TreeSet的使用方法以及三者之间的区别. 注意:本文中代码使用的JDK版本为1.8.0_191 1. HashSe ...
- 【Java_集合框架Set】HashSet、LinkedHashSet、TreeSet使用区别
HashSet:哈希表是通过使用称为散列法的机制来存储信息的,元素并没有以某种特定顺序来存放: LinkedHashSet:以元素插入的顺序来维护集合的链接表,允许以插入的顺序在集合中迭代: Tree ...
- Java Set 常用集合 HashSet、LinkedHashSet、TreeSet
Java 中的 Set 是非常常用的数据类型.Set 是无序的 Collection,Java Set 有三个常用的实现类,分别是:HashSet.LinkedHashSet.TreeSet 本文基于 ...
- Java自学-集合框架 HashSet、LinkedHashSet、TreeSet之间的区别
HashSet. LinkedHashSet.TreeSet之间的区别 步骤 1 : HashSet LinkedHashSet TreeSet HashSet: 无序 LinkedHashSet: ...
- 一、集合框架(关于ArrayList,LinkedList,HashSet,LinkedHashSet,TreeSet)
一.ArrayList 解决了数组的局限性,最常见的容器类,ArrayList容器的容量capacity会随着对象的增加,自动增长.不会出现数组边界的问题. package collection; ...
- 09 Collection,Iterator,List,listIterator,Vector,ArrayList,LinkedList,泛型,增强for,可变参数,HashSet,LinkedHashSet,TreeSet
09 Collection,Iterator,List,listIterator,Vector,ArrayList,LinkedList,泛型,增强for,可变参数,HashSet,LinkedHas ...
- HashSet、LinkedHashSet、TreeSet
以下内容基于jdk1.7.0_79源码: 关于HashSet.LinkedHashSet.TreeSet Set接口的实现类,最大特点是不允许出现重复元素: HashSet:基于HashMap实现,一 ...
- 规则集之探究何时使用HashSet、LinkedHashSet以及TreeSet?
前言 Java集合框架三种主要类型的集合:规则集(Set).线性表(List).队列(Queue).Set用来存储不可重复的元素:List用来存储有元素构成的有序的集合:而Queue则用于存储用先进先 ...
随机推荐
- 加快XCode编译链接速度(200%+)—XCode编译慢液
最近在一个大型项目的开发的时候遇到一个很头疼的问题,由于该项目的代码更,每次建立联系1纪要.浪费时间调试.因此,一些研究如何提高编译链接速度,这里给大家分享. 为了提高编译和链接的是以下三种方式的速度 ...
- [CLR via C#]1.1 将源代码编译成托管代码
原文:[CLR via C#]1.1 将源代码编译成托管代码 1. 公共语言运行时(Common Language Runtime,CLR)是一种可由多种编程语言使用的"运行时". ...
- TCP通信中的大文件传送
TCP通信中的大文件传送 源码 (为节省空间,不包含通信框架源码,通信框架源码请另行下载) 文件传送在TCP通信中是经常用到的,本文针对文件传送进行探讨 经过测试,可以发送比较大的文件,比如1个G ...
- JS代码的几个注意点规范
也谈谈规范JS代码的几个注意点 也谈谈规范JS代码的几个注意点 写JS代码差不多也有两年了吧,从刚开始的“初生牛犊不怕虎”乱写一通到后来也慢慢知道去规范一下自己写的代码.这种感觉就像是代码是你的作品, ...
- linux 在系统启动过程
从学习<鸟哥linux私人厨房> 用于在计算机系统启动,计算机硬件和软件由(它包含的操作系统软件)包括.对于操作系统在同一台计算机硬件方面的表现,该系统配备有硬件是公用,不同的系统是 的操 ...
- 通过扩展改善ASP.NET MVC的验证机制[实现篇]
原文:通过扩展改善ASP.NET MVC的验证机制[实现篇] 在<使用篇>中我们谈到扩展的验证编程方式,并且演示了本解决方案的三大特性:消息提供机制的分离.多语言的支持和多验证规则的支持, ...
- MonkeyDevcie API 实践全记录
1. 背景 使用SDK自带的NotePad应用作为实践目标应用,目的是对MonkeyDevice拥有的成员方法做一个初步的了解. 以下是官方列出的方法的Overview. Return Type ...
- 【转】NuGet的安装与使用
学习了一段时间的MVC,今天想自己尝试初步搭建一个MVC框架,结果新建MVC4.0(MVC3.0同样)项目时,弹出一个错误提示框,如下图.上网一搜,说是要安装一个第三方组件NuGet.刚接触MVC,更 ...
- Fedora 20 Gnome安装及配置记录
下载了F20的Gnome版,原先安装的是19KDE的,原因是昨晚看书,觉得电脑开着也没什么事情,倒不如看看能不能升级或下载点东西 原先是KDE的界面,所以打算换换风格,使用下Gnome,不过更换过程总 ...
- AJAX入门——工作原理
同步和异步交互,了解互动 对于一个样本:一般B/S模式(同步) AJAX技术(异步) * 同步: 提交请求->等待server处理->处理完成返回 ...