Java中的集合框架(下)

由于Java中的集合框架的内容比较多,在这里分为三个部分介绍Java的集合框架,内容是从浅到深,哈哈这篇其实也还是基础,惊不惊喜意不意外 ̄▽ ̄ 写文真的好累,懒得写了。。

温馨提醒:建议从(上)开始看哦~

目 录

前 言

在<浅入深出之Java集合框架(中) >中介绍了Map接口的基本操作。使用的示例是在<浅入深出之Java集合框架(上)>中的模拟学生选课的小程序,不清楚的朋友可以先去阅读<浅入深出之Java集合框架(上)>。

在本篇中我们将解决如下问题:

  • 在课程序列中,如何判断是否包含某门或者某几门课程?
  • 如果课程序列包含某门课程,如何判断该课程的索引位置?
  • 在学生映射表中,如何判断是否包含某个学生id?
  • 又该如何判断是否包含某个学生对象?
  • 如果把课程或者学生对象,按照课程名称或者学生姓名排序又该怎么办?按照ID排序呢?

问题看起来很多很复杂,其实主要就是两个部分:如何判断集合中是否包含某个元素;如何把有序集合按照指定的规则进行排序。

一、集合中contains方法的应用

对于第一个问题,集合提供了一个叫做contains的方法,这个方法就是用来判断该集合是否包含某个元素,对于contains方法我们分别从List、Set、Map集合来看看它的使用方法和注意事项。

1)List 中contains()方法——判断List中课程中是否存在

首先我们来写一个测试方法来用contains方法来判断List中课程是否存在:

 /*
* 测试List的contains方法
*/
public void testListContains(){
//取得备选课程序列的第0个元素
Course cr=this.CoresesToSelect.get(0);
System.out.println("取得课程:" +cr.getId()+","+cr.getName());
System.out.println("备选课程中是否包含课程:"+cr.getName()+","+this.CoresesToSelect.contains(cr));
//创建一个新的课程对象,它的id和name和cr一样
Course cr2=new Course(cr.getId(),cr.getName());
System.out.println("备选课程中是否包含课程:"+cr2.getName()+","+this.CoresesToSelect.contains(cr2));
//通过indexOf方法来取的某元素的索引位置
if(CoresesToSelect.contains(cr2)){
System.out.println("课程:"+cr2.getName()+"的索引位置为:"+CoresesToSelect.indexOf(cr2)); }
}

运行结果:

结果分析:

从运行结果中可以看出第一个“数据结构”课程调用contains方法返回时true,而第二个“数据结构”课程调用contains方法返回时false。为什么同样都是名为“数据结构”的课程为什么会有两种截然不同的结果?

第一个“数据结构”课程是从CoresesToSelect集合中取出来的,所以理所当然地CoresesToSelect集合中包含它,调用contains方法返回时true;而第二个“数据结构”课程是new(新创建)的课程,系统开辟了另一个内存空间,虽然令它的名称和第一个“数据结构”相同,但是对于系统来说是两个不同的对象。contains方法默认是比较引用对象内存地址的(哈希码),那么如何才能使用相同名称的课程来判断List中是否包含该课程呢?

这里我们需要来深入了解contains方法的原理,来帮助我们理解和使用contains方法。如下图所示:

方法中的所有类都是继承于Object类,而在Object类中有一个equals()方法,它是用来比较两个对象是否相等的,关于equals()方法的详解在我的其他博文中,链接:http://www.cnblogs.com/hysum/p/7100874.html

然后我们来看看contains方法的内部原理是怎么样的。

如上图所示,contains方法其实通过遍历集合中的每个对象的equals方法来比较两个对象是否相等,如果相等返回true ,否则返回false。而equals()默认是比较两个对象的引用是否相等,如果我们要通过contains方法来比较两个对象的值是否相等来判断集合中是否含有一个对象,则必须重写该对象的equals()方法。

下面以比较两个课程对象的名称是否一样来重写Course类equals()方法,如:

 public void equals (Object obj)
{
if(this == obj) // 指向同一个内存快 必然是相同的
returned true; //为什么contains(course2) 返回的是false 原因就在在这里 只是比较了一下 引用的值是否相同 没有进一步的比较 内存块中的值 下就是对这里记性了改写
if (obj == null) // 如果obj为空 就直接没有戏了
return false;
if(! (obj instanceof Course)) //如果两者的不属于同一类型则 也直接没戏了
return false;
//经过以上的这么多的比较 终于可以判断二者的值是不是相同了
//首相要把objet型的obj转成 Course
Course course = (Course) obj;
if( this.name == null )

if(course.name == null )
return true;
else return false;
}
else
{
if(this.name.equals(course.name)) return true;
else return false;
)
//如果还有别的属性接着写
}

重写完Course类的equals()方法后,我们重写运行一下程序,结果如下:

可以看到contains方法可以根据课程名称来判断是否包含该课程了。

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

这里的indexOf()方法与lastIndexOf()方法实现原理和contains方法相似:

1、遍历调用每个元素的equals()方法,如果返回true则将次元素的索引返回;

2、如果有多个相同元素,则只返回第一个元素的索引;

3、lastIndexOf()方法则从最后一个元素开始遍历;

2)Set中contains()方法——判断Set中课程是否存在

首先我们也来写一个测试方法来用contains方法来判断Set中课程是否存在:

     /*
* 测试Set的contains方法
*/
public void testSetContains(){
System.out.println("请输入学生已选的课程名称:");
String cName=in.next();
Course c=new Course(cName);
System.out.println("学生已选的课程是否包含课程:"+cName+","+stu.getCourses().contains(c)); }

运行结果:

这就奇怪了,我们不是已经重写了Course类的equals()方法吗?为什么在Set集合里调用contains方法还是返回false?

原来在HashSet里的contains方法和List里略有不同。它的原理如下图所示:

Set.contains(E e)工作原理是先调用从Object继承而来的hashCode方法,然后再调用equals()方法,如果hashCode相同,才会调用equals方法,只有两者都返回true,contains方法 才返回true。jvm运行时,给每个对象分配唯一一个标志身份的标志hashcode。众类鼻祖Object的hashCode()方法在默认情况下,判断哈希码是不是相同。即如同equals默认情况下比较的是二者是不是同一个内存快。Set集的contains方法,内部就是先调用hashCode再调用equals()方法。很多情况要结合实际对hashCode进行改写。

这里给出我对hashCode的重写(系统自动生成):

 public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}

然后我们重写运行一下程序,结果如下:

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

这里我总结一下List和Set中contains方法的注意事项

1.list 与 set 中contain()方法调用机制:

list 中的contain()方法是拿到list中的每个对象来调用Object类中的equals()方法;

Set  中的contain()方法是拿到list中的每个元素来先调用hashcode()方法来返回哈希码值,当哈希码的值相等时,再调用equals()方法,当比较的元素此时也相同时,才会返回true。因此调用Set中的contain()方法时,需要对hashcode()方法及equals()方法进行重写。

2. == 和equals()两种比较方法,在使用时要注意:

1)对于==,如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;

          如果作用于引用类型的变量,则比较的是所指向的对象的地址。

2)对于equals方法,注意:equals方法不能作用于基本数据类型的变量。

如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;诸如String、Date等类应该根据情况覆盖其父类或Object类中的equals()方法,否则默认的equals()方法功能与“==”相同。

3)Mapt中containsKey方法和containsValue方法——判断Map中是否包含指定的key和value

Map中通过containsKey()方法和containsValue()方法来判断键和值是否存在(已经重写Student类的Hashcode和equals方法,这里不再列出):

 /*
* 测试Map中,是否包含某个key值或者某个value值
*/
public void ContainsKeyOrValue(){
//在Map中,用containsKey方法,来判断是否包含某个key值
System.out.println("请输入学生id:");
String id =in.next();
System.out.println("您输入的学生id为"+id+",Map中是否包含此id:"+this.students.containsKey(id));
if(students.containsKey(id)){
System.out.println("对应的学生姓名:"+students.get(id).getName()); }
//在Map中,用containsValue方法,来判断是否包含某个value值
System.out.println("请输入学生姓名:");
String name =in.next();
Student st=new Student();
st.setName(name);
System.out.println("您输入的学生姓名为"+name+",Map中是否包含此姓名:"+this.students.containsValue(st));
if(students.containsValue(st)){ Set<Entry<String, Student>> entrySet =students.entrySet();
for(Entry<String, Student> entry:entrySet){
if(entry.getValue().getName().equals(name)){
System.out.println("对应的学生的id:"+entry.getValue().getId());
} }
} }

运行结果:

注意:

1.Map 中对对象进行了 Key 标记,通过 get(Key)可以取得对应的对象。

2.Map 的containsValue()方法的参数是 Object 对象,因为Map 的 Value 值是对象元素。

3.Map 的containsKey()方法取得 Map 映射的 Key 值。

4.Map中的containsValue()方法原理跟HashSet的contains()相同,所以要重写Hashcode和equals方法。

二、Collections工具类

对于第二个问题,java集合框架还有一个成员就是Collections工具类,这个工具类提供了很多操作集合的方法,其中sort()方法是很常见的方法,它能对有序集合按照默认规则进行排序。接下来我们通过实例来看看Collections工具类是如何通过sort()对List进行排序的。

我写了四个测试方法来测试sort()方法,分别如下:

1)对Integer泛型的List进行排序

 /*
* 1.通过Collections.sort()方法,对Integer泛型的List进行排序
* 创建一个Integer泛型的List,插入十个100以内的不重复随机整数
* 调用Collections.sort()方法对其排序
*/
public void testSort1(){
//创建一个Integer泛型的List,插入十个100以内的不重复随机整数
List<Integer> intList=new ArrayList<Integer>();
int i=0;
do{
Random random=new Random();
Integer n=random.nextInt(100);
if(!intList.contains(n)){
intList.add(n);
System.out.println("成功添加整数:"+n);
i++;
}
}while(i<10);
System.out.println("----------排序前-----------");
for(Integer t:intList){
System.out.print(t+" ");
}
System.out.println();
//调用Collections.sort()方法对其排序 System.out.println("----------排序后-----------");
Collections.sort(intList);
for(Integer t:intList){
System.out.print(t+" ");
}
}

运行结果:

这里我使用了Random类来获取随机数,Random类的出现,可以替代之前的Math.random方法;

Random类有很多方法:nextByte、nextDouble、nextInt、nextDouble、nextBoolean、nextFloat、nextLong,获取各种类型的随机数,但是没有nextShort和nextCharacter。

并且对于nextInt并且支持传入一个int类型参数,作为随机数的取值范围。

常见用法:nextInt对于从某一个String类型的对象中,获取一个随机位的字符很有用,

例如String str = "sdfsfsfsdsafdf"; Random r = new Random();char c = str.charAt(r.nextInt(str.length));

2)对String泛型的List进行排序

 /*
*2.对String泛型的List进行排序
* 创建一个String泛型的List,添加三个乱序的String
* 调用Collections.sort()方法对其排序
*/
public void testSort2(){
//创建一个String泛型的List,添加三个乱序的String
List<String> strList=new ArrayList<String>();
String[] str={"egesg","eDSGvdxbrb","23235sdvfsd"};
strList.addAll(Arrays.asList(str));
System.out.println("----------排序前-----------");
for(String s:strList){
System.out.print(s+",");
}
System.out.println();
//调用Collections.sort()方法对其排序
System.out.println("----------排序后-----------");
Collections.sort(strList);
for(String s:strList){
System.out.print(s+",");
}
}

运行结果:

再写一个稍微复杂一点的例子:

 /*
*
* 创建一个String泛型的List,往其中添加十条随机字符串
* 每条字符串的长度为10以内的随机整数
* 每条字符串的每个字符都为随机生成的字符,字符可以重复
* 每条随机字符串不可以重复
* 调用Collections.sort()方法对其排序
*/
public String getRandomString(int size) {
StringBuilder str= new StringBuilder();//可变字符串
Random random=new Random();
//存放所包含字符62位
String container = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
for(int j=0;j<size;j++){
str.append(container.charAt(random.nextInt(container.length()-1)));
}
return str.toString();
}
public void testSort3(){ //创建一个String泛型的List
List<String> strList=new ArrayList<String>();
Random random=new Random();
String str;
for(int i=0;i<10;i++){//添加十条随机字符串
do{
int size=random.nextInt(10);//每条字符串的长度为10以内的随机整数
str=getRandomString(size);
}while(strList.contains(str)||str.length()==0);
strList.add(str); }
System.out.println("----------排序前-----------");
for(String s:strList){
System.out.println(s);
}
System.out.println();
//调用Collections.sort()方法对其排序
System.out.println("----------排序后-----------");
Collections.sort(strList);
for(String s:strList){
System.out.println(s);
}
}

运行结果:

注意:

对于String类型的sort()方法默认排序顺序为:

数字(0-9)--->大写字母(A-Z)--->小写字母(a-z)

3)对其他类型泛型的list进行排序,以,Student为例。

     /*
* 对其他类型泛型的list进行排序,以,Student为例。
*/
public void testSort4(){
List<Student> stus=new ArrayList<Student>();
Random random=new Random();
stus.add(new Student(random.nextInt(1000)+"","Mike"));
stus.add(new Student(random.nextInt(1000)+"","Angela"));
stus.add(new Student(random.nextInt(1000)+"","Lucy"));
System.out.println("----------排序前-----------");
for(Student s:stus){
System.out.println(s.getId()+","+s.getName()+" ");
}
System.out.println();
System.out.println("----------排序后-----------");
Collections.sort(stus);//程序编译出错
for(Student s:stus){
System.out.println(s.getId()+","+s.getName()+" ");
} }
程序在16行Collections.sort(stus);//程序编译出错
查看信息说是sort()方法类型不匹配,无法对Student类型的List进行排序,为什么会这样呢?我们来看一下API的sort方法说明:

原来collection.sort()方法对元素进行排序,列表中的元素都必需实现 Comparable 接口,否则不能使用 sort()方法排序,那么Comparable 接口是什么,该如何实现呢?请往下看!

三、Comparable & Comparator简介

在上述例子可以看到Collections工具类的sort()方法不能对Student类的List集合进行排序,通过查阅API我们知道因为Student类并没有继承Comparable & Comparator接口,Student类是不可比较的,所以编译器会报错。那么Comparable & Comparator这两个接口有什么不同呢?

compareable 是默认比较规则, comparator是临时比较规则

1.Comparable接口------可比较的

  • 实现该接口表示:这个类的实例可以比较大小,可以进行自然排序
  • 定义了默认的比较规则
  • 其实现类需实现compareTo()方法
  • comparaTo()方法返回正数表示大,负数表示小,0表示相等

2.Comparator接口-----比较工具接口

  • 用于定义临时比较规则,而不是默认比较规则
  • 其实现类需要实现compare()方法

compareTo()方法和compare()方法的返回值有三种:

  • 返回0:两个对象相等
  • 返回1:该对象大于比较的对象
  • 返回-1:该对象小于比较的对象

那么我们给Student类加上Comparable接口和Comparator接口,规定默认规则是比较学生的id大小,临时规则是比较学生的姓名。代码如下:

注意:Comparable接口和Comparator接口后面都要加上泛型!!!

 public class Student implements Comparable<Student>,Comparator<Student>{
.......//省略一段代码
@Override
public int compare(Student arg0, Student arg1) {
// TODO Auto-generated method stub
return arg0.name.compareTo(arg1.getName());
}
@Override
public int compareTo(Student arg0) {
// TODO Auto-generated method stub
//默认与学生id比较
return Integer.valueOf(this.getId()).compareTo(Integer.valueOf(arg0.getId()));
}

再修改testSort4()方法,如下:

 /*
* 对其他类型泛型的list进行排序,以,Student为例。
*/
public void testSort4(){
List<Student> stus=new ArrayList<Student>();
Random random=new Random();
stus.add(new Student(random.nextInt(1000)+"","Mike"));
stus.add(new Student(random.nextInt(1000)+"","Angela"));
stus.add(new Student(random.nextInt(1000)+"","Lucy"));
System.out.println("----------排序前-----------");
for(Student s:stus){
System.out.println(s.getId()+","+s.getName()+" ");
}
System.out.println();
System.out.println("----------排序后-----------");
Collections.sort(stus);
for(Student s:stus){
System.out.println(s.getId()+","+s.getName()+" ");
}
System.out.println("----------根据姓名排序-----------");
Collections.sort(stus,new Student());
for(Student s:stus){
System.out.println(s.getId()+","+s.getName()+" ");
} }

运行结果:

注意:直接调用sort(集合)是使用默认的比较规则;调用sort的重载方法sort(集合,实现Comparator<类名>接口的对象名)是使用临时规则。

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

让没有默认比较规则的类进行比较的方法,步骤总结:

一、让该类实现Comparable接口:

1、在该类中加上implements Comparable<类名>。

2、实现.compareTo(类名 对象名)方法,若this较大则返回正值,若相等则返回0,若this较小则返回负值。(各种类都包含.compareTo()方法)

3、调用Collections.sort(对象名)进行排序。

二、让该类实现Comparator接口:

1、新建一个类加上implements Comparator<类名>

2、实现.compare(类名 对象名1,类名,对象名2)方法,若this较大则返回正值,若相等则返回0,若this较小则返回负值。

3、调用Collections.sort(对象名,实现Comparator<类名>接口的对象名)方法。

四、Java集合框架总结

在本篇的末我们终于认全了Java集合框架,它包含如下内容:

Collection接口,Map接口,Collections工具类,Comparable接口,Comparator接口

本人只是写了一点皮毛,想要了解更多,请参考Java的API喔、、、

浅入深出之Java集合框架(下)的更多相关文章

  1. 浅入深出之Java集合框架(上)

    Java中的集合框架(上) 由于Java中的集合框架的内容比较多,在这里分为三个部分介绍Java的集合框架,内容是从浅到深,如果已经有java基础的小伙伴可以直接跳到<浅入深出之Java集合框架 ...

  2. 浅入深出之Java集合框架(中)

    Java中的集合框架(中) 由于Java中的集合框架的内容比较多,在这里分为三个部分介绍Java的集合框架,内容是从浅到深,如果已经有java基础的小伙伴可以直接跳到<浅入深出之Java集合框架 ...

  3. 浅入深出Vue:工具准备之PostMan安装配置及Mock服务配置

    浅入深出Vue之工具准备(二):PostMan安装配置 由于家中有事,文章没顾得上.在此说声抱歉,这是工具准备的最后一章. 接下来就是开始环境搭建了~尽情期待 工欲善其事必先利其器,让我们先做好准备工 ...

  4. 浅入深出Vue:前言

    浅入深出Vue系列文章 之前大部分是在做后端,后来出于某些原因开始接触Vue.深感前端变化之大,各种工具.框架令人眼花缭乱.不过正是这些变化,让前端开发更灵活. 博主在刚开始时,参考官网的各个步骤以及 ...

  5. 『浅入深出』MySQL 中事务的实现

    在关系型数据库中,事务的重要性不言而喻,只要对数据库稍有了解的人都知道事务具有 ACID 四个基本属性,而我们不知道的可能就是数据库是如何实现这四个属性的:在这篇文章中,我们将对事务的实现进行分析,尝 ...

  6. 浅入深出Vue:第一个页面

    今天正式开始入门篇,也就是实战了~ 首先我们是要做一个博客网站,UI 框架采用江湖传闻中的 ElementUI,今天我们就来利用它确定我们博客网站的基本布局吧. 准备工作 新建一个vue项目(可以参考 ...

  7. 浅入深出Vue:环境搭建

    浅入深出Vue:环境搭建 工欲善其事必先利其器,该搭建我们的环境了. 安装NPM 所有工具的下载地址都可以在导航篇中找到,这里我们下载的是最新版本的NodeJS Windows安装程序 下载下来后,直 ...

  8. 浅入深出Vue:工具准备之WebStorm安装配置

    浅入深出Vue之工具准备(一):WebStorm安装配置 工欲善其事必先利其器,让我们先做好准备工作吧 导航篇 WebStorm安装配置 所有工具的下载地址都可以在导航篇中找到,这里我们下载的是最新版 ...

  9. 浅入深出Vue系列

    浅入深出Vue导航 导航帖,直接点击标题即可. 文中所有涉及到的资源链接均在最下方列举出来了. 前言 基础篇 浅入深出Vue:工具准备之WebStorm搭建及配置 浅入深出Vue之工具准备(二):Po ...

随机推荐

  1. 谈一谈Java8的函数式编程(二) --Java8中的流

    流与集合    众所周知,日常开发与操作中涉及到集合的操作相当频繁,而java中对于集合的操作又是相当麻烦.这里你可能就有疑问了,我感觉平常开发的时候操作集合时不麻烦呀?那下面我们从一个例子说起. 计 ...

  2. cuda学习3-共享内存和同步

    为什么要使用共享内存呢,因为共享内存的访问速度快.这是首先要明确的,下面详细研究. cuda程序中的内存使用分为主机内存(host memory) 和 设备内存(device memory),我们在这 ...

  3. VR全景:vr元年过后,这些企业如何发动“vr+”应用引擎?

    2016年,VR可谓是四处衍生.从如痴如迷的游戏行业到喜闻乐见的影视行业,再到医疗.军事.房地产,随便呼出一个"+",VR便能左右逢源,VR+各行各业,俨然成为一种标配.最近,Ma ...

  4. sql备份(.bak文件备份)

    第一步: 右键需要备份的数据库(这里以 MyDB 为例)-->任务-->备份

  5. python——爬虫&问题解决&思考(四)

    继续上一篇文章的内容,上一篇文章中已经将url管理器和下载器写好了.接下来就是url解析器,总的来说这个模块是几个模块中比较难的.因为通过下载器下载完页面之后,我们虽然得到了页面,但是这并不是我们想要 ...

  6. 网络编程应用:基于UDP协议【实现聊天程序】--练习

    要求: 使用UDP协议实现一个聊天程序 代码: 发送端: package UDP聊天程序; import java.io.IOException; import java.net.DatagramPa ...

  7. MYSQL和JAVA(课堂笔记)

    MYSQL 数据库管理工具 JAVA  编程语言 数据库驱动(JAVA和MYSQL对接方式) 到官网上下载驱动 加载驱动 import java.sql.Connection;import java. ...

  8. Catalog Service - 解析微软微服务架构eShopOnContainers(三)

    上一篇我们说了Identity Service,因为其基于IdentityServer4开发的,所以知识点不是很多,今天我们来看下Catalog Service,今后的讲解都会把不同的.重点的拿出来讲 ...

  9. 织梦dedecms后台发布文章提示“标题不能为空”

    问题症状:V5.7登录后台后,发布英文标题没问题,发布中文会提示“标题不能为空”. 问题根源:htmlspecialchars在php5.4默认为utf8编码,gbk编码字符串经 htmlspecia ...

  10. CentOS 7 for ARM 安装一键Lnmp失败

    背景 前面把树莓派装上了CentOS 7,趁着国庆放假回来赶紧把服务端环境搭起来,为了方便就准备用一键lnmp快速部署一个,结果死活安装不成功... 报错 按照以往的经验进行安装,在我的小树莓派上安装 ...