一:Collection.remove()与ArrayList.remove()方法的不同

           1. Collection中只能根据元素移除
           2.ArrayList可以根据元素删除,也可以根据下标删除

二:完成ArrayList去重的案例

   观察以下代码,并得出代码执行后的结果
Dog d = new Dog("小黑",5);
List<Dog> ls=new ArrayList<Dog>();
ls.add(d);
ls.add(d);
ls.add(d);
System.out.println(ls.size());
    得出结果后推导原因: List接口的特点之一就是可重复

问:如何在ArrayList出完成去除重复值的功能

答:使用contains方法进行判断,判断新的集合newLs是否包含指定的元素,如果包含,那么不添加新集合中,如果不包含,那么添加到新集合中


public static List<Dog> singleList(List<Dog> oldLs) {
List<Dog> newLs = new ArrayList<Dog>();// 去除重复值之后的集合
for (Dog d : oldLs) {// 遍历需要去除重复值的集合
if (!newLs.contains(d)) {// 如果在新集合中不存在
newLs.add(d);// 加入到新集合中
}
}
return newLs;
}
写完方法之后再调用之前的案例查看结果 :
Dog d = new Dog("小黑",5);
List<Dog> ls = new ArrayList<Dog>();
ls.add(d);
ls.add(d);
ls.add(d);
List<Dog> newLs = singleList(ls);
System.out.println("集合:"+newLs);
System.out.println("长度:"+newLs.size());
输出结果:

集合:[Dog [name=小黑, age=5]]
长度:1
换种方式在试:

Dog d1 = new Dog("小黑",5);
Dog d2 = new Dog("小黑",5);
Dog d3 = new Dog("小黑",5);
List<Dog> ls = new ArrayList<Dog>();
ls.add(d1);
ls.add(d2);
ls.add(d3);
List<Dog> newLs = singleList(ls);
System.out.println("集合:"+newLs);
System.out.println("长度:"+newLs.size());
输出结果:
集合:[Dog [name=小黑, age=5], Dog [name=小黑, age=5], Dog [name=小黑,
age=5]]长度:3
  • == 与 equals 的区别:

      == 比较的对象在内存中的地址
      equals 比较的是对象的内容(通过重写方法,可以自定义判断规则)
在没有重写equals的情况下 默认使用的是Object类的equals 比较的是对象在内存中的
地址

重写Dog的 equals 方法 :
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Dog other = (Dog) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
重写了equals方法,判断只要名字相同,就认为是同一只狗
重新执行上面的代码,输出结果:

集合:[Dog [name=小黑, age=1]]
长度:1
讲解原理:

        contains方法与remove方法的内部都是根据对象的equals方法来做判断的

问: 除了这种方式之外,有没有别的方法能够去除重复值?

答:引入Set集合

三:Set接口介绍

           Set接口是继承自Collection的子接口,特点:元素不重复
Set<String> set = new HashSet<String>();
set.add("jack");//第一个jack
set.add("rose");
set.add("alien");
set.add("jack");//第二个jack
for (String s : set) {
System.out.println(s);
}
输出结果:

rosejackalien

四:Set接口下的实现类

  • HashSet

  • 无序
Set<String> set = new HashSet<String>();
set.add("a");
set.add("d");
set.add("c");
set.add("b");
for (String s : set) {
System.out.println(s);
}

输出结果:

 abcd

     要注意强调无序指的是插入的顺序与取出的顺序不一致(从输出结果可以看到),原因:
              在对象进入HashSet内部时,会使用对象内部HashCode方法计算hash值后自动进行排序,
所以读取的是经过内部排序后的数据,所以存入的顺序会与取出的顺序有不同(内部通过排序重
新整理了元素的位置),并且在数据没变的情况下,每次数据输出结果都是一样的顺序

  • 不重复
         HashSet底层数据结构是哈希表, HashSet不是线程安全的,但是效率高,同时集合元素可以是null
查看源码:
private transient HashMap<E,Object> map;
public HashSet() {
map = new HashMap<>();
}
当我们在调用add方法往HashSet中添加元素时:

public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
HashMap源码:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
原理:

                 在HashSet中放入元素的时候,相当于将元素放在了一个Map哈希表中,在Map这种哈希表中,分为键值对形式存储。在上面存入元素时,该元素的hash值 hash(key) 会被作为键,而元素

则会被作为值。

HashSet集合中如何判断两个元素相等的标准:
     通过两个对象 hashCode() 方法拿到哈希值,如果两个哈希值相同,再调用 equals()
方法比较
如果都相同,则认定为同一个元素,无法存入

重点:HashSet保证元素唯一性是靠元素重写hashCode()和equals()方法来保证的,如果不重写则无法保证
  • 代码演示
1.重写hashCode()方法 这个方法的作用就是返回对象的hash值
@Override
public int hashCode() {
System.out.println("调用了hashCode");//调用时打印
return age;//该对象的hash值就是他的年龄
}
@Override
public boolean equals(Object obj) {
System.out.println("调用了equals");//调用时打印
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Dog other = (Dog) 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;//只要名字相同 则equals返回真
}
Dog d1 = new Dog("小黑",1);
Dog d2 = new Dog("小黑",1);
Dog d3 = new Dog("小白",1);
HashSet<Dog> set = new HashSet<Dog>();
  • 将 d1 添加到集合中

set.add(d1)

控制台输出:

             调用了hashCode

原因:

        往集合中添加 d1 元素时,集合内没有hash值为1的其他元素,所以只调用了hashCode()来获取
对象的hash值

  • 将 d2 添加到集合中
set.add(d2);
  • 控制台输出:
              调用了hashCode
               调用了equals

原因:

      往集合中添加 d2 元素时,集合内已经有一个d1了,并且它的hash值也为1
      讲解判断规则:两个哈希值相同,再调用 equals() 方法比较

提问:这个时候集合中长度?
                set集合的长度:1
  • 将 d3 添加到集合中
set.add(d3);
         调用了hashCode
         调用了equals

问:此处的集合长度?

       set集合的长度:2

原因讲解:

    hash值虽然相同,但是equals时得到的结果不对应,所以不被认为是相同的元素,所以添加成

注意:

判断元素是否存在,以及删除等操作,用作判断的条件同样是hashCode()+equals()方法

五:TreeSet

            TreeSet是一个有序的集合,它的作用是提供有序的Set集合(没有重复值)
  • 有序
          底层数据结构是二叉树,集合中元素唯一,并且可以对元素进行排序
TreeSet<String> set=new TreeSet<String>();
set.add("jack");
set.add("rose");
set.add("alien");
for (String s : set) {
System.out.println(s);
}

输出结果:

alienjackrose
  • 排序方式:自然排序
          让需要排序的类实现 Comparable 接口,重写方法 (因为String实现了这个接口,所以上面的数据是有序的)
@Override
public int compareTo(Object o) {
// TODO Auto-generated method stub
return 0;
}

返回值:

  1. 如果返回值>0,他大
  2. 如果返回值=0,相等
  3. 如果返回值<0,我大

将代码修改为按照年龄排序:

@Override
public int compareTo(Object o) {
// TODO Auto-generated method stub
return this.age-((Dog)o).getAge();
}

代码:

Dog d1 = new Dog("小黑", 23);
Dog d2 = new Dog("小白", 35);
Dog d3 = new Dog("小青", 18);
Dog d4 = new Dog("小绿", 22);
TreeSet<Dog> ts = new TreeSet<Dog>();
ts.add(d1);
ts.add(d2);
ts.add(d3);
ts.add(d4);
for (Dog d : ts) {
System.out.println(d);
}

输出结果:

 Dog [name=小青, age=18]Dog [name=小绿, age=22]Dog [name=小黑, age=23]Dog [name=小白, age=35]

如果年龄相同,则可以按照其他条件来排序,比如:名字

@Override
public int compareTo(Object o) {
// TODO Auto-generated method stub
Dog d=(Dog)o;
int num=this.age-d.getAge();
if(num==0) {
return this.name.compareTo(d.getName());
}
return num;
}
  • 比较器排序

          使用公安局给出数据的例子,来提出怎么实现这些数据的排序,引出比较器排序。
          定义一个比较器(实现Comparator接口):

public class DogComparator implements Comparator<Dog>{
}

重写compare方法:

@Override
public int compare(Dog o1, Dog o2) {
return 0;
}
按照年龄排序
@Override
public int compare(Dog o1, Dog o2) {
return o1.getAge()-o2.getAge();
}
在年龄相同的时候(主条件),可以根据名字来排序(次条件)
@Override
public int compare(Dog o1, Dog o2) {
int x=o1.getAge()-o2.getAge();
if(x==0) return o1.getName().compareTo(o2.getName());
return x;
}

在初始化TreeSet的时候将我们写好的比较器传入就可以了(提醒要注意):

Dog d1 = new Dog("小黑", 23);
Dog d2 = new Dog("小白", 35);
Dog d3 = new Dog("小青", 18);
Dog d4 = new Dog("小绿", 22);
TreeSet<Dog> ts = new TreeSet<Dog>(new DogComparator());//指定比较器
ts.add(d1);
ts.add(d2);
ts.add(d3);
ts.add(d4);
for (Dog d : ts) {
System.out.println(d);
}

输出结果:

Dog [name=小青, age=18]Dog [name=小绿, age=22]Dog [name=小黑, age=23]Dog [name=小白, age=35]

问:如何将集合中的排列改成从大到小?

比较优先级:比较器排序大于自然排序
  • 二叉树

特点:左子树的键值小于根的键值,右子树的键值大于根的键值

六:泛型

  • 为什么会需要泛型
演示Object集合的转换报错问题:
List ls=new ArrayList();
ls.add(1);
ls.add("a");
for (Object o : ls) {
System.out.println((Integer)o);
}
          如果集合对于元素类型没有限制,对于取数据的人来说,还需要先转为对应的类型再来操作,并且可能会报异常
          引出泛型的作用,在编译时期就限制数据类型

package com.zking.collection01.util;import java.util.List;
import java.util.ListIterator;
import java.util.ArrayList;
import java.util.Iterator;public class Demo2 {public static void main(String[] args) {//泛型:以类型作为参数的类就叫泛型//作用:提供程序的健壮性、简化代码//泛型的默认类型为Object//泛型是从jdk1.5之后推出的List<Integer> lst=new ArrayList<>();//lst.add("zs");lst.add(1);lst.add(2);lst.add(7);lst.add(6);//获取迭代器ListIterator<Integer> it = lst.listIterator();//循环遍历while(it.hasNext()) {//获取集合中的元素(先移动下标)//Object val = it.next();//转换数据类型//int num=Integer.parseInt(val.toString());Integer num = it.next();//获取偶数数据if(num%2==0)System.out.println(num);}/*** 装箱、拆箱(快递)值类型->引用类型 装箱引用类型->值类型 拆箱jdk1.5之后引入了自动装箱及自动拆箱功能*///装箱int a=1;Integer num=new Integer(a);//拆箱int b = num.intValue();}
}

七:提问

  • 如何去除ArrayList中的重复值?

  1. 编写代码
  2. List与Set的转换
  • HashSet与TreeSet的区别?

  1. 底层数据结构
  2. 顺序
  • TreeSet是有序的吗?

  1. 插入顺序
  2. 数据顺序
  • TreeSet在排序时如何比较元素?

  1. 自然排序
  2. 选择器排序
  • HashSet如何检查重复?

  1. hashCode()+equals()

J2EE 快速入门之第二章 Set集合详解相关推荐

  1. Elastricsearch 索引操作详解(快速入门、索引管理、映射详解、索引别名)

    一.快速入门 1. 查看集群的健康状况 http://localhost:9200/_cat http://localhost:9200/_cat/health?v 说明:v是用来要求在结果中返回表头 ...

  2. 机械3D设计软件快速入门技巧:零件设计功能详解

    上期和大家分享过如何快速上手SolidWorks软件,这一期和大家分享同样具备功能全面.超强兼容.简洁易用等特点的浩辰3D设计软件.接下来给大家分享浩辰3D设计软件零件设计功能的使用技巧吧! 1.打开 ...

  3. 第二章 关系映射详解

    本章学习目标 generator 主键策略 对象关系映射之一对多映射 cascade 和 inverse 配置详解 对象关系映射之多对多映射 对象关系映射之一多一映射 1. generator主键策略 ...

  4. 第二章 Message组成详解

     Message组成详解 1.1JMS message组成说明 JMS message包含两部分,头和承载体. 头中提供被客户端和provider使用的元数据, 承载体包含着实际的数据. 客户端sen ...

  5. Flutter修仙传第二章:路由详解

    兄弟们,咱们又见面了. 在上一章中,咱们入门学习了Flutter神功,会了些皮毛,知道了输入框,单选复选等这些基础组件的使用,小生并没有讲解按钮这种基础组件的使用,像这种easy的不能再easy的组件 ...

  6. 第二章 计算机网络应用层详解

    文章目录 一.应用层协议原理 网络应用程序体系结构 进程通信 客户和服务器进程 进程与计算机网络之间的接口 进程寻址 可供应用程序的运输服务 可靠数据传输 吞吐量 定时 安全性 因特网的运输服务 TC ...

  7. 第二章 Python数据类型详解

    基本概念 迭代(iteration):如果给定一个list或tuple,我们可以通过for循环来遍历,这种遍历我们称为迭代(iteration) 可变:value改变,id不变,可变类型是不可hash ...

  8. 大学物理第二章 质点动力学详解

    牛顿运动定律 一.牛顿第一定律 惯性参考系 定义: 任何物体都要保持其静止或匀速直线运动状态,直到外力破迫使它改变运动状态为止 物体的这种运动状态通常称为惯性运动,而物体保持原有运动状态的特性称之为惯 ...

  9. Java快速入门到精通— Java break语句详解

    所有流行的编程语言中都有循环语句.JAVA 中采用的循环语句与C语言中的循环语句相似,主要有 while.do-while 和 for! 那么在某些时候需要在某种条件出现时强行终止循环,而不是等到循环 ...

最新文章

  1. give root password for maintenance 启动异常的解决
  2. Android Touch事件传递机制 二:单纯的(伪生命周期)
  3. spark wordcount完整工程代码(含pom.xml)
  4. office软件的发展前景_2018年办公软件产业发展趋势
  5. SCOI 2014 new :未来展望
  6. 硬盘序列号示例_序列化代理模式示例
  7. Android开发技术周报 Issue#72
  8. 安卓逆向_12 --- jeb工具的使用 ( 动态调试 smali 代码 【 普通调试 和 debug调试 】)
  9. 网络人的未来分享讲义_酒品看人品,未来酱分享饮酒识人技巧!谁是你值得深交的人?...
  10. JS和JS是IE上JavaScript或JScript的缩写。
  11. node js npm grunt安装,elasticsearch-head 5.X安装
  12. vue 小写金额转换为大写金额
  13. 通达OA 商务平台OA2017新版本简易评测(图文)
  14. GUI 自动测试工具[2021清单]
  15. 极速office(Excel)怎么把边框线条加粗
  16. python人工智能入门纳米学位_最近看到udacity的纳米学位很火,号称学完可以找到工作了,这是真的吗?...
  17. 动态路由器ensp二层三层交换_eNSP模拟实验-路由器和交换机在不同网段互通配置...
  18. Android开发艺术探索完结篇——天道酬勤
  19. 教你如何用android系统通过Remote Desktop远程控制电脑
  20. 2023 电脑PC 素材解析浏览器插件 支持20网

热门文章

  1. 平台测试—— 平台验证
  2. 按照从大到小的顺序输出四位数中的个位+百位=十位+千位
  3. JAVA 完成一个网页计算器
  4. 给一个培训还是自学的理由
  5. java 压缩/解压【tar.gz】
  6. el vue 手机号_Vue实现数字输入框中分割手机号码实例教程
  7. 源程序与源文件的区别
  8. mini2440的nor flash与nand flash启动过程区别
  9. ChatGPT神奇应用:定制化学习体验,get专属家教
  10. 使用生成式模型来改进旅游产品和服务的生成质量 GANs for Travel:A Review of Generative Recommendations