上一篇《Java中的集合框架-Collection(一)》把Java集合框架中的Collection与List及其常用实现类的功能大致记录了一下,本篇接着记录Collection的另一个子接口Set及其实现类。

一,Collection子接口Set

  Set接口与List接口同时是Collection接口的子接口,但两者区别还是很大的。

  首先,Set里的方法与Collection里的方法完全一样;

  其次,Set是无序的,即Set的存储与取出顺序可能不一致;演示如下:

     private static void function_demo9() {
Set set = new HashSet();
set.add(7);
set.add(4);
set.add(3);
Iterator it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
  程序运行结果:
        
  可见Set存储的顺序与取出的顺序是不一致的,但是可以看到,其实Set采用了某种方式进行默认的排序,这一点在后面会说。
  最后,Set里的元素不可重复;演示如下:

     private static void function_demo9() {
Set set = new HashSet();
set.add(7);
set.add(4);
set.add(3);
set.add(3);
Iterator it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}

  程序运行结果:

        

  可见Set里存储相同的元素时不能被放到容器里。

二,Set常用实现类

  Set接口也有很多的实现类,比较常用的也有三个:HashSet,TreeSet,LinkedHashSet,我们再次完善一下Collection接口的类图,如下:

          

  1,Set常用实现类-HashSet

    HashSet实现了Set接口,它是非同步,不保证顺序的一个集合,查看源码可知,其实HashSet内部维护的是一个HashMap,只是value值的是一个Object对象而已。

    HashSet使用hash算法存储数据,在存储的时候,先去判断hashcode是否相同,若相同的情况下再去判断对象的内容是否相同;

    在判断hashcode是否相同的时候用的是hashcode方法,在判断内容是否相同的时候用的是equals方法;

    若hashcode不同则不必再去判断对象内容即不会再调用equals方法;

    看下面一个例子,HashSet里存储自定义的对象类型

 public class Person {
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
} private String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} private int age;
}
     private static void function_demo10() {
HashSet hs = new HashSet();
hs.add(new Person("张三", 32));
hs.add(new Person("李四", 12));
hs.add(new Person("赵五", 12));
hs.add(new Person("马七", 56));
hs.add(new Person("马七", 56));
for (Iterator iterator = hs.iterator(); iterator.hasNext();) {
System.out.println(((Person)iterator.next()).getName());
}
}

    程序运行结果:

          

    我们知道,Set里是不可以存储重复的元素的,但为何上面可以存储两个“马七”?

  分析一下,我们存储对象的时候用的是new Person()的方式,前面说过,HashSet在存储的时候先会去判断每个对象的hashcode是否相同,那么每次new出来的Person对象的hashcode值肯定是不相同的,所以HashSet直接就判断这两个对象不是同一个对象但存储进去了,若想完成这个去除重复的存储,可以先让Person类重写hashcode方法,修改如下:

 public class Person {
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
} private String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} private int age; @Override
public int hashCode() {
return this.name.hashCode();
}
}

  此时再运行程序便会发现,”马七“还是两个了。其实HashSet在存储元素的时候,会拿两个元素的hashcode去做比较,此时若hashcode相同便去比较两个元素的内容,调用的是该元素的equals方法;若hashcode不同则存储该元素。因为Person重写了hashcode方法,但hashcode方法里我们返回的是当前对象的name的hashcode值;当往HashSet存储Person对象时,存储第二个”马七“时hashcode值还是一样的,便会去调用对象的equals方法,但是equals方法比较的是堆里的地址,两个对象在堆里的地址是不一样的,所以判定还是两个不同的对象,所以还是出来了两个”马七“。为了解决这个问题,我们再把Person类的equals方法重写一下,代码如下:

 public class Person {
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
} private String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} private int age; @Override
public int hashCode() {
return this.name.hashCode();
} @Override
public boolean equals(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 (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
} }

    此时再运行程序,发现只有一个”马七“了。

  2,Set常用类-LinkedHashSet

    LinkedHashSet继承自HashSet,不同步;

    LinkedHashSet与HashSet的区别是它内部数据结构是双向链表的形式,它能保证存储的顺序与取出的顺序是一致的;

    LinkedHashSet里面所有的方法都是从HashSet继承而来的,这里演示一下它有HashSet的区别,如下:

     private static void function_demo12() {
HashSet hs = new HashSet();
hs.add(1);
hs.add(6);
hs.add(2);
hs.add(4);
hs.add(3);
System.out.println("HashSet存取不一致:"+hs);
LinkedHashSet lhs = new LinkedHashSet();
lhs.add(1);
lhs.add(6);
lhs.add(2);
lhs.add(4);
lhs.add(3);
System.out.println("LinkedHashSet存取一致:"+lhs);
}

  3,Set常用类-TreeSet

    TreeSet内部其实是TreeMap的形式, 是二叉树的数据结构,它是非同步的;

    TreeSet可对存储的元素按照自然顺序进行排序;

     private static void function_demo13() {
TreeSet ts = new TreeSet();
ts.add("1");
ts.add("4");
ts.add("2");
ts.add("3");
ts.add("abc");
ts.add("zzd");
ts.add("adef");
ts.add("mnjuk");
ts.add("aaac");
System.out.println(ts);
}
    程序运行结果:
          
    但是当TreeSet存储自定义对象时,演示如下(Person类还是上面的Person类):

     private static void function_demo14() {
TreeSet ts = new TreeSet();
ts.add(new Person("张三", 32));
ts.add(new Person("李四", 12));
ts.add(new Person("赵五", 12));
ts.add(new Person("马七", 56));
ts.add(new Person("马七", 56));
}

    程序运行x结果:

               

  报错说Person类不能转成Comparable。原因就是当往TreeSet存储元素的时候,因为TreeSet会使用二叉树算法对元素进行排序,它会找比较的依据进行存储,那么这个被存储的对象就要具备比较的功能,这个具备比较功能的接口就是Comoparable;那么存储字符串的时候为什么没报错呢?原因是String已经实现了Comparable接口,具备了比较的功能,下面我们让Person类实现这个接口,代码如下:

 public class Person implements Comparable {
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
} private String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} private int age; @Override
public int compareTo(Object o) {
return 0;
} }

    Comparable接口强行对实现它的类的对象的元素进行整体的排序,它里面只有一个compareTo方法,此方法返回0表示相等,负数表示小于,正数表示大于,此时程序运行结果如下:

                  

   可以看到,我们往TreeSet里添加了五个元素,但是此时只添加了一个Person对象进去,出现这样问题的原因就是我们在重写了Comparable里的compareTo方法后直接返回了0,它说明添加的所有的元素都是同一个元素,所以只添加一个元素到了容器中,我们再次修改Person对象里的coompareTo方法,代码如下:

 public class Person implements Comparable {
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
} private String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} private int age; @Override
public int compareTo(Object o) {
Person person = null;
if (o instanceof Person) {
person = (Person) o;
}
return this.getName().hashCode() - person.getName().hashCode();
} }

  此时程序运行结果是:        

  四个元素添加成功了,并且没有重复的元素。

  TreeSet在存储自定义对象的时候,还有另外一种不去让对象实现Comparable接口而可以进行排序的方法,那就是在实例化TreeSet的时候,调用带比较器Comparator的构造函数;

  Comparator接口有两个方法,一个compare,另一个是equals方法;操作如下:

  实现了Comparator接口的自定义比较器对象代码:

 public class MyComparator implements Comparator {

     @Override
public int compare(Object o1, Object o2) {
Person p1 = (Person) o1;
Person p2 = (Person) o2;
return p1.getName().hashCode() - p2.getName().hashCode();
} }

    主程序代码如下:

     private static void function_demo15() {
TreeSet ts = new TreeSet(new MyComparator());
ts.add(new Person("张三", 32));
ts.add(new Person("李四", 12));
ts.add(new Person("赵五", 12));
ts.add(new Person("马七", 56));
ts.add(new Person("马七", 56));
System.out.println(ts);
}

    程序运结果和让Person类实现Comparable接口的结果是一样的

 

Java中的集合框架-Collection(二)的更多相关文章

  1. Java中的集合框架-Collection(一)

    一,Collection接口 在日常的开发工作中,我们经常使用数组,但是数组是有很多的局限性的,比如:数组大小固定后不可修改,只能存储基本类型的值等等. 基于数组的这些局限性,Java框架就产生了用于 ...

  2. Java中的集合框架-Collections和Arrays

    上一篇<Java中的集合框架-Map>把集合框架中的键值对容器Map中常用的知识记录了一下,本节记录一下集合框架的两个工具类Collections和Arrays 一,Collections ...

  3. Java中的集合框架-Map

    前两篇<Java中的集合框架-Commection(一)>和<Java中的集合框架-Commection(二)>把集合框架中的Collection开发常用知识点作了一下记录,从 ...

  4. 菜鸟日记之 java中的集合框架

    java中的集合框架图 如图所示:java中的集合分为两种Collection和Map两种接口 可分为Collection是单列集合和Map的双列集合 Collection单列集合:继承了Iterat ...

  5. Java中的集合框架(上)

    Java中的集合框架概述 集合的概念: Java中的集合类:是一种工具类,就像是容器,存储任意数量的具有共同属性的对象. 集合的作用: 1.在类的内部,对数据进行组织: 2.简单的快速的搜索大数据量的 ...

  6. Java学习--java中的集合框架、Collection接口、list接口

    与数组相比:1.数组的长度固定,而集合的长度可变2.数组只能通过下表访问元素,类型固定,而有的集合可以通过任意类型查找所映射的具体对象 java集合框架:collection(list序列,queue ...

  7. JAVA 中的集合框架

    java集合框架提供了一套性能优良.使用方便的接口和类,它们位于java.util包中 一.集合与数组 数组:(可以存储基本数据类型)是用来存现对象的一种容器,但是数组的长度固定,不适合在对象数量未知 ...

  8. Java中的集合框架

    概念与作用 集合概念 现实生活中:很多事物凑在一起 数学中的集合:具有共同属性的事物的总体 java中的集合类:是一种工具类,就像是容器,储存任意数量的具有共同属性的对象 在编程时,常常需要集中存放多 ...

  9. Java中的集合框架(下)

    学生选课--判断Set中课程是否存在 package com.imooc.collection; import java.util.ArrayList; import java.util.Arrays ...

随机推荐

  1. Designers, please follow the guidelines

    Skype released big update for its iOS application last week. It brought in a major overhaul of not o ...

  2. CentOS配置multipath

    可以通过2种方式查看HBA的WWN信息: 1. 查看sys文件系统 查看HBA卡型号:[root@localhost ~]# lspci  | grep -i fibre13:00.0 Fibre C ...

  3. vue2 入门 教程 单页应用最佳实战[*****]

    推荐 vue2 入门 教程 -------- 看过其他的,再看作者的,很赞 vue2 入门 教程 单页应用最佳实战 :  具体在 https://github.com/MeCKodo/vue-tuto ...

  4. MySQL数据库(11)----使用子查询实现多表查询

    子查询指的是用括号括起来,并嵌入另一条语句里的那条 SELECT 语句.下面有一个示例,它实现的是找出与考试类别('T')相对应的所有考试事件行的 ID,然后利用它们来查找那些考试的成绩: SELEC ...

  5. 学习servlet心得

    1,关于字符编码问题: // resp.setCharacterEncoding("UTF-8");//这个的作用仅仅只是输出字符,不做格式转换成HTML // resp.setC ...

  6. Android深入四大组件(五)Android8.0 根Activity启动过程(后篇)

    前言 在几个月前我写了Android深入四大组件(一)应用程序启动过程(前篇)和Android深入四大组件(一)应用程序启动过程(后篇)这两篇文章,它们都是基于Android 7.0,当我开始阅读An ...

  7. 基于TCP/IP的程序设计

    TCP特点 (1)面向连接的传输 (2)端到端的通信 (3)高可靠性,确保传输数据的正确性,不会出现丢失或者乱序 (4)全双工方式传输 (5)采用字节流方式,以字节为单位传输字节序列 (6)紧急数据传 ...

  8. Excel录入中实现单元格多选项自动下拉

    当我们在Excel表格中需要输入大量的重复数据时,往往利用数据的有效性来制作一个下拉菜单以提高重复数据的输入速度.但在实际的操作过程中,必须选中需要输入重复数据的单元格并单击该单元格右边的下拉箭头,才 ...

  9. Linu下的Mysql学习详解_【all】

    Linux下Mysql简介 1.什么是Mysql(MariDB) 1.数据库:存储数据的仓库    2.关系型数据库:mysql(主流用5.5,5.6), oracle        本质:二维表   ...

  10. 使用 Nginx 对 ASP.NETCore网站 或 Docker 等进行反向代理,宝塔面板对 ASP.NET Core 反向代理

    1,Nginx 的 配置文件 Nginx 可以配置反向代理.负载均匀等, 其默认配置文件名为 nginx.conf . 一般存放于 /你的安装目录/nginx/conf  下 Nginx 加载配置信息 ...