集合框架学习

集合框架综述

集合 --- 把批量数据集中合并在一起进行操作。 数组是集合的一种,而且是最简单最原始的一种。体现在三个方面: 1、只能存放同一数据类型元素; 2、大小一旦确定不能更改; 3、所有元素存放在连续内存地址空间。

之前的学习中,我们通过面向对象的多态特性,解决了问题1;我们通过面向对象的类的封装性,解决了问题2。但是,我们没有解决问题3,因为这涉及到了数据在内存中的存放形式问题(数据结构)。

但是,在JDK当中,早就写好了解决以上3个问题的集合类。而且不止一个,它们构成了我们今天要学习的Java集合框架(JCF)。

集合框架的组成

  1. Collection接口是所有集合类的根接口;

  2. List、Set、Map代表了三种结构不同使用场景不同的三种集合类型。其中:List和Set直接继承于Collection,Map间接来自于Collection;

  3. 我们在实际使用的时候,用的是List/Set/Map的实现类。需要掌握常见类的使用API。

  4. Collections和Arrays是集合框架提供的两个工具类,自带了大量对于批量数据的操作方法(包括:找最大,找最小,排序等等)

  5. 比较器(Comparator和Comparable)\ 迭代器(Iterator) 也是用于辅助操作集合内容的接口。

  6. 在集合框架的类设计中,还使用了一种叫做"泛型"的Java语法,我们也需要了解一下。

List(列表)

List的特点 - 线性(有序) 我们往List集合中放置的元素,会根据我们放的顺序对应它存的顺序。正是因为有序这个特点,List当中的元素都是有下标的,而且所有的下标都是从0开始的。

ArrayList

这个类从名字上就可以看出来,他是在底层使用数组来实现的,跟我们之前自定义的Array几乎是一摸一样的设计方案。

添加元素 --- add 注意: 添加的元素默认都是Object类型,基本数据类型进去也是用的“自动封箱”; 可以添加重复的; 可以添加null。

获取元素 --- get 根据下标去获取该位置的元素; 下标不对,会报异常。

修改元素 --- set 根据下标去修改该位置的元素; 下标不对,会报异常

删除元素 --- remove removeAll 注意: remove是重载方法,可以根据下标删除, 也可以根据元素删除(如果有重复元素,只删除一个,且是前面的)。

遍历: 1、普通for循环照用; 2、迭代器循环; 将集合当中的元素,从头到尾访问一遍。最大的特点就是不能回头,也不会跳过。 3、for-each循环 是专用于从集合类(包括数组)当中,挨个儿每一个元素的循环语法。只要我们不考虑在遍历操作中增/删/跳的动作,只是简单的从头到尾访问,那么使用这个语法比普通for还要简单。 大家需要知道:集合框架类的for-each语句在底层中是基于迭代器(Iterator)实现的。

    //从集合对象中,每次循环取一个元素赋值给变量
    for(元素类型 变量 : 集合对象){
        //实现对变量的操作
    }
​

LinkedList

拥有和ArrayList一摸一样的API,甚至执行后的效果也是一样的。但是LinkedList在底层的数据结构上采用了“双向链表”的设计。

LinkedList和ArrayList的区别 1、底层区别:前者是双向链表;后者是数组; 2、效率区别:前者在执行元素的增删动作的时候效率更高;后者在执行遍历查找动作的时候效率更高。

ArrayList和Vector的区别 都是List集合,底层都是数组,区别: ArrayList是线程不安全的; Vector是线程安全的。

Set(集)

Set的特点:不能重复。 Set集合中相同的元素只能存放一次,并且由于线性不是它的特点,所以我们放的顺序和它存的顺序不一致,因此在Set当中是没有下标这个概念的。

HashSet

从名称上看,这个集合带有“Hash”这个标识,说明必然它会和我们之前看到的Object类当中的hashcode方法有关系。

增加元素 -- add 当我们重复添加一个已经在Set当中存在的元素时,不会报错,只是没有放进去。 允许放入null元素,当然也只能放一个。

获取元素 --- 没有获取某个元素的方法

修改元素 --- 没有修改指定元素的方法

删除元素 --- remove removeAll remove方法没有重载,只能根据对象进行删除。

获取元素个数 -- size

遍历 : 1、不能使用普通for循环; 2、只能使用for-each语句,从头到尾访问一次。

HashSet是如何判断两个元素重复的? HashSet是通过调用元素的hashcode方法和equals方法来判断两个元素是否重复的。在Java的规范当中要求:两个相同元素理论上应该具有同样的hashcode值,以及equals要返回true。 HashSet也是按照这个规范来做的,它会先判断元素的hashcode值是否一直,然后再调用equals方法看是否返回true。

TreeSet

Map(映射)

从名称上,我们能看出来Map这种集合,它的元素是成对存放的。它的元素是以KV对的形式存放的。Key不能重复,Value是通过Key去映射的。

HashMap

HashMap是通过hashcode值保证Key的唯一性的。

增加 --- put Key和Value都可以为null,但是由于Key不能重复,所以只有一个Key可以为null。

获取 --- get 通过Key去获取值 如果Key不存在,不会报错,只会返回null。

修改 --- put 通过已经存在的key,去修改它的值。

删除 --- remove 通过Key进行删除,如果Key不存在,也不会报错,只是没有执行删除操作而已。

获取个数 --- size

遍历 --- 需要单独遍历所有的Key或所有的值 keySet() --- 获取所有的Key,返回一个Set集合 Values() --- 获取所有的值,返回一个Collection集合 然后我们再通过for-each循环遍历每个Key或每个值。

Properties

它也是Map下的一个实现类,只不过作为集合类型,它比较弱。因为它的key和值都是String类型,它唯一擅长的就是它除了充当容器,还可以操作一种格式特殊的文件(属性文件)。

这种特殊格式的属性文件,虽然结构简单,但是在一些不需要存放复杂数据的情况下,还是可以用用的。你们以后会在一些框架的配置文件中见到它。

泛型语法

在集合框架中的类型都设计了“泛型”的语法,用于限制该集合对象只能存放某种数据类型的元素。

List<String> l = new ArrayList<>();
​

注意: 1、<>里面只能写一种类型; 2、可以把泛型设计为放父类类型,这样它的所有子类都可以放入; 3、泛型只能是引用数据类型,若有这种需求,那么要书写的应该是该基本数据类型对应的“包装类类型”。

1、Java集合框架(JCF)到底是什么? Java为了能够更好的操作集合元素,预先设计的一系列具有继承或实现的关系的类与接口。其中主要包含了两部分的内容: 各种结构的集合类 --- 装元素的容器 操作集合的工具类 --- Collections等

2、JCF的继承结构是什么? 整个JCF当中的集合类的根接口是Collection。由它衍生出了各种存储结构的子接口:List、Set、Queue(队列,了解即可)、Map。然后每个子接口下面又有各种的实现类,他们都是我们可以使用的容器类。

3、List接口 特点--线性(有序),唯一一组像数组一样自带下标的。 常用类:ArrayList LinkedList 常用API:add、addAll、get、set、remove、removeAll、size

遍历方式:普通for、迭代器(了解即可)、for-each

ArrayList和LinkedLst的区分 --- 必须掌握 ArrayList、Vector的区分

4、Set接口 特点:不重复 常用类:HashSet 常用API:add addAll remove removeAll size 遍历方式:for-each

HashSet是怎样去重的? 调用equlas方法和hashcode方法判断两个对象是否同一个。要求:equals()返回true,且两个对象的hashcode要一致。 先人写好的常用类:String、包装类、时间日期等等,都已经实现了equals和hashcode的重写。 我们只需要在自定义类,且要把它的对象放入到Set集合中判断重复的时候才会有这个要求。这个要求就是:凡是重写了equls方法都应该重写hashcode方法,达到如果equls返回true的两个对象,他们的hashcode应该返回同样的值。

5、Map接口 特点:K-V对 常用类:HashMap 常用API:put、get、remove、size、keySet、values 遍历方式:遍历keySet、遍历values

key和value的关系? 对应关系,其中key不能重复。

HashMap和Hashtalbe的区别? HashMap是线程不安全的,允许null做为键和值 Hashtable是线程安全的,不允许用null做键或值。

Properties需要知道 1、它是一个集合类,而且是Map集合,只是所有的Key和Value都是String 2、它可以操作一种特殊格式的文本文件---属性文件。

6、泛型 语法上,在声明集合类型变量的时候用"<>"规范该集合只能存放某种数据类型的元素。

Collections工具类

比较器

用于在类当中去定义该类对象比较大小的规则。 如果不去做这件事情,那么Collections当中所有跟比较有关的行为根本没有办法实现。

Comparable接口 -- 内部比较器

被比较对象自己内部实现的比较规则。先人定义的常用类都已经实现了该接口。

内部比较接口应该实现在被比较对象本身身上实现,然后重写该接口提供的compareTo方法。

compareTo方法的内部实现,记住: 根据在规则下确立的对象位置,分别返回负数、0和整数。 当前对象的位置在传入对象的位置之前,返回“负数”;之后返回“正数”。

Comparator -- 外部比较器

在比较的时候,通过参数指定比较规则,这个规则与内部比较器如果同时存在,以外部为准。

单独书写一个比较器类去实现外部比较器,只需要重写compare --- 接口中的其他方法都是default的。

compare方法的内部实现与上面的内部比较器是一样的,相当于用“第一个参数”的位置 减去 “第二个参数”的位置,分别得到负数、0或正数。

泛型的基本语法

泛型其实早就在其他编程语言当中就已经出现了的,Java其实是比较晚去实现它的语法(JDK1.5之后)。

泛型 在编程语言中的意义 其实比大家想象得要大。我们用专业的方式来描述它的话,它应该叫做“数据类型的参数化”。

也就是说有时候我们在定义一个类或方法的时候,并不能确定我要操作的数据类型是哪一个,需要使用者后期在使用的过程中告之。那么这个时候,我们就可以使用 --- “泛型”的语法来进行设计。

泛型类

假如在一个类的内部,需要使用到某种不确定的类型元素,那么我们就可以在类的声明处通过"<>"来定义泛型。

public class 类名<T>{
​
}

T只是一个标识符,可以自定义,但通常都写为T、E、K、V。 然后,在我们的这个类内部,只要需要用到这个类型的时候,就都可以用这个标识符来表达了。可以用它来申明属性,可以用它来声明形参,也可以用它来做返回类型,甚至是局部变量的类型。

在定义类的内容时,T由于没有具体的类型(这个还不确定),所以在Java中默认它是一个Object类型,所以可以用T定义的变量访问来自于Object的方法,但是不能访问某个具体子类的具体行为。注意,T在外部确定的时候,不能是基本数据类型

使用的时候:

MyClass<某个引用数据类型> mc = new MyClass<>();

mc对象中,所有用到T的地方,就都改变成了你在 "<>"内所规范的数据类型。

泛型接口

在接口中也可以定义泛型,定义语法与泛型类是一样的。

public interface MyInterface<T> {
​
    public void add(T t);
​
    public T delete();
}
​

它的实现类有三种情况: 1、实现类不做泛型设计,那么实现类中的T全部被“擦除”为Object

public class MyClass1  implements MyInterface{
​
    public void add(Object o) {
        System.out.println(o);
    }
    
    public Object delete() {
        return null;
    }
}

2、实现类根据接口指定的泛型类型

public class MyClass1  implements MyInterface<String>{
​
    public void add(String o) {
        System.out.println(o);
    }
​
    public String delete() {
        return null;
    }
}
​

3、实现类继续使用T,让它的调用者去确定T到底是谁?

public class MyClass1<T> implements MyInterface<T>{

    public void add(T o) {
        System.out.println(o);
    }

    public T delete() {
        return null;
    }
}

泛型方法

语法

public <T> 返回类型 方法名(T t){

}

在调用方法的时候,根据实际参数的类型确定T的类型。支持静态方法和非静态方法,以及多态参数。

Java的泛型的不足

Java的泛型和其他编程语言(特别是C#)语言的泛型比起来就是弟弟。 因为Java的泛型是假泛型。Java为了让它的JVM在运行的时候能够兼容之前的版本,所以Java泛型是不会在运行期做限制的,只是在编译期做了一个编译时的检查。 我们可以根据Java当中的“反射”技术,在运行期绕过它的泛型规范。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值