集合的概述

集合

集合概述

集合:集合是java中提供的一种容器,可以用来存储多个数据。

集合和数组既然都是容器,它们有啥区别呢?

数组的长度是固定的。集合的长度是可变的。

数组中存储的是同一类型的元素,可以存储基本数据类型值。集合存储的都是对象。而且对象的类

型可以不一致。在开发中一般当对象多的时候,使用集合进行存储。

集合框架

JAVASE提供了满足各种需求的API,在使用这些API前,先了解其继承与接口操作架构,才能了解何时采

用哪个类,以及类之间如何彼此合作,从而达到灵活应用。

集合按照其存储结构可以分为两大类,分别是单列集合 java.util.Collection 和双列集合

java.util.Map ,今天我们主要学习 Collection 集合,在day04时讲解 Map 集合。

Collection:单列集合类的根接口,用于存储一系列符合某种规则的元素,它有两个重要的子接

口,分别是 java.util.List 和 java.util.Set 。其中, List 的特点是元素有序、元素可重

复。 Set 的特点是元素无序,而且不可重复。 List 接口的主要实现类有 java.util.ArrayList

和 java.util.LinkedList , Set 接口的主要实现类有 java.util.HashSet 和

java.util.TreeSet 。从上面的描述可以看出JDK中提供了丰富的集合类库,为了便于初学者进行系统地学习,接下来通过一

张图来描述整个集合类的继承体系。

其中,橙色框里填写的都是接口类型,而蓝色框里填写的都是具体的实现类。这几天将针对图中所列举

的集合类进行逐一地讲解。

集合本身是一个工具,它存放在java.util包中。在 Collection 接口定义着单列集合框架中最最共性的

内容。

Collection 常用功能

Collection是所有单列集合的父接口,因此在Collection中定义了单列集合(List和Set)通用的一些方法,

这些方法可用于操作所有的单列集合。方法如下:

  1. public boolean add(E e) : 把给定的对象添加到当前集合中 。
  2. public void clear() :清空集合中所有的元素。
  3. public boolean remove(E e) : 把给定的对象在当前集合中删除。
  4. public boolean contains(E e) : 判断当前集合中是否包含给定的对象。
  5. public boolean isEmpty() : 判断当前集合是否为空。
  6. public int size() : 返回集合中元素的个数。
  7. public Object[] toArray() : 把集合中的元素,存储到数组中。

类集中最大的几个操作接口:Collection、Map、Iterator,这三个接口为以后要使用的最重点的接口。

所有的类集操作的接口或类都在 java.util 包中。

62709628150
62709628150

Collection 接口(重点)

Collection 接口是在整个 Java 类集中保存单值的最大操作父接口,里面每次操作的时候都只能保存一个对象的数据。

此接口定义在 java.util 包中。

此接口定义如下:

public interface Collection<E> extends Iterable<E>

此接口使用了泛型技术,在 JDK 1.5 之后为了使类集操作的更加安全,所以引入了泛型。

此接口的常用方法如下所示。

62709643679
62709643679

本接口中一共定义了 15 个方法,那么此接口的全部子类或子接口就将全部继承以上接口中的方法。

但是,在开发中不会直接使用 Collection 接口。而使用其操作的子接口:List、Set。

之所以有这样的明文规定,也是在 JDK 1.2 之后才有的。一开始在 EJB 中的最早模型中全部都是使用 Collection 操作

的,所以很早之前开发代码都是以 Collection 为准,但是后来为了更加清楚的区分,集合中是否允许有重复元素所以 SUN

在其开源项目 —— PetShop(宠物商店)中就开始推广 List 和 Set 的使用。

Collections常用功能

  • java.utils.Collections 是集合工具类,用来对集合进行操作。部分方法如下:public static boolean addAll(Collection c, T... elements) :往集合中添加一
  • 些元素。
  • public static void shuffle(List<?> list) 打乱顺序 :打乱集合顺序。
  • public static void sort(List list) :将集合中元素按照默认规则排序。
  • public static void sort(List list,Comparator<? super T> ) :将集合中元素按
  • 照指定规则排序。
62709860951
62709860951

Comparator比较器

​ 我们还是先研究这个方法public static void sort(List list) :将集合中元素按照默认规则排序。不过这次存储的是字符串类型。

62709867436
62709867436

​ 我们使用的是默认的规则完成字符串的排序,那么默认规则是怎么定义出来的呢?

​ 说到排序了,简单的说就是两个对象之间比较大小,那么在JAVA中提供了两种比较实现的方式,一种是比较死板的采用 java.lang.Comparable 接口去实现,一种是灵活的当我需要做排序的时候在去选择的 java.util.Comparator 接口完成。

那么我们采用的 public static void sort(List list) 这个方法完成的排序,实际上要求了被排序的类型需要实现Comparable接口完成比较的功能,在String类型上如下:

public final class String implements java.io.Serializable, Comparable<String>, 

CharSequence {

​ String类实现了这个接口,并完成了比较规则的定义,但是这样就把这种规则写死了,那比如我想要字符串按照第一个字符降序排列,那么这样就要修改String的源代码,这是不可能的了,那么这个时候我们可以使用public static void sort(List list,Comparator<? super T> ) 方法灵活的完成,这个里面就涉及到了Comparator这个接口,位于位于java.util包下,排序是comparator能实现的功能之一,该接口代表一个比较器,比较器具有可比性!顾名思义就是做排序的,通俗地讲需要比较两个对象谁排在前谁排在后,那么比较的方法就是:

  • public int compare(String o1, String o2) :比较其两个参数的顺序。

两个对象比较的结果有三种:大于,等于,小于。

如果要按照升序排序,

则o1 小于o2,返回(负数),相等返回0,01大于02返回(正数)

如果要按照降序排序

则o1 小于o2,返回(正数),相等返回0,01大于02返回(负数)

62709881805
62709881805

简述Comparable和Comparator两个接口的区别。

Comparable:强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的compareTo方法被称为它的自然比较方法。只能在类中实现compareTo()一次,不能经常修改类的代码实现自己想要的排序实现此接口的对象列表(和数组)可以通过Collections.sort(和Arrays.sort)进行自动排序,对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。

Comparator强行对某个对象进行整体排序。可以将Comparator 传递给sort方法(如Collections.sort或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用Comparator来控制某些数据结构(如有序set或有序映射)的顺序,或者为那些没有自然顺序的对象collection提供排序。

62709897237
62709897237
62709898699
62709898699
62709900051
62709900051

扩展

62709903107
62709903107
62709903976
62709903976

List集合

我们掌握了Collection接口的使用后,再来看看Collection接口中的子类,他们都具备那些特性呢?

接下来,我们一起学习Collection中的常用几个子类( java.util.List 集合、 java.util.Set 集合)。

在整个集合中 List 是 Collection 的子接口,里面的所有内容都是允许重复的。

List 子接口的定义:

public interface List<E> extends Collection<E>

此接口上依然使用了泛型技术。此接口对于 Collection 接口来讲有如下的扩充方法:

62709665253
62709665253

在 List 接口中有以上 10 个方法是对已有的 Collection 接口进行的扩充。

所以,证明,List 接口拥有比 Collection 接口更多的操作方法。

了解了 List 接口之后,那么该如何使用该接口呢?需要找到此接口的实现类,常用的实现类有如下几个:

· ArrayList(95%)、Vector(4%)、LinkedList(1%)

List的子类

ArrayList集合

java.util.ArrayList 集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用

最多的功能为查询数据、遍历数据,所以 ArrayList 是最常用的集合。

许多程序员开发时非常随意地使用ArrayList完成任何需求,并不严谨,这种用法是不提倡的。

ArrayList 是 List 接口的子类,此类的定义如下:

public class ArrayList<E> extends AbstractList<E> 

implements List<E>, RandomAccess, Cloneable, Serializable

此类继承了 AbstractList 类。AbstractList 是 List 接口的子类。AbstractList 是个抽象类,适配器设计模式。

范例:增加及取得元素

package org.listdemo.arraylistdemo; 

import java.util.ArrayList; 

import java.util.List; 

public class ArrayListDemo01 { 

 public static void main(String[] args) { 

  List<String> all = new ArrayList<String>(); // 实例化List对象,并指定泛型类型 

  all.add("hello "); // 增加内容,此方法从Collection接口继承而来 

  all.add(0, "LAMP ");// 增加内容,此方法是List接口单独定义的 

  all.add("world"); // 增加内容,此方法从Collection接口继承而来 

  System.out.println(all); // 打印all对象调用toString()方法 

 } 

}

以上的操作向集合中增加了三个元素,其中在指定位置增加的操作是 List 接口单独定义的。随后进行输出的时候,

实际上调用的是 toString()方法完成输出的。

可以发现,此时的对象数组并没有长度的限制,长度可以任意长,只要是内存够大就行。

范例:进一步操作

使用 remove()方法删除若干个元素,并且使用循环的方式输出。

根据指定位置取的内容的方法,只有 List 接口才有定义,其他的任何接口都没有任何的定义。

package org.listdemo.arraylistdemo; 
import java.util.ArrayList; 
import java.util.List; 
public class ArrayListDemo02 { 
public static void main(String[] args) { 
 List<String> all = new ArrayList<String>(); // 实例化List对象,并指定泛型类型 
 all.add("hello "); // 增加内容,此方法从Collection接口继承而来 
 all.add(0, "LAMP ");// 增加内容,此方法是List接口单独定义的 
 all.add("world"); // 增加内容,此方法从Collection接口继承而来 
 all.remove(1); // 根据索引删除内容,此方法是List接口单独定义的 
 all.remove("world");// 删除指定的对象 
 System.out.print("集合中的内容是:"); 
 for (int x = 0; x < all.size(); x++) { // size()方法从Collection接口继承而来 
 System.out.print(all.get(x) \+ "、"); // 此方法是List接口单独定义的 
  } 
 } 
}

但是,这里需要注意的是,对于删除元素的操作,后面还会有更加清楚的讲解,此处只是简单的理解一下元素删除

的基本操作即可。具体的原理可以暂时不进行深入掌握。

Vector

与 ArrayList 一样,Vector 本身也属于 List 接口的子类,此类的定义如下:

public class Vector<E> extends AbstractList<E> 

implements List<E>, RandomAccess, Cloneable, Serializable

此类与 ArrayList 类一样,都是 AbstractList 的子类。所以,此时的操作只要是 List 接口的子类就都按照 List 进行操作。

62709705945
62709705945

以上的操作结果与使用 ArrayList 本身并没有任何的区别。因为操作的时候是以接口为操作的标准。

但是 Vector 属于 Java 元老级的操作类,是最早的提供了动态对象数组的操作类,在 JDK 1.0 的时候就已经推出了此类的使用,只是后来在 JDK 1.2 之后引入了 Java 类集合框架。但是为了照顾很多已经习惯于使用 Vector 的用户,所以在 JDK 1.2 之后将 Vector 类进行了升级了,让其多实现了一个 List 接口,这样才将这个类继续保留了下来。

Vector 类和 ArrayList 类的区别

这两个类虽然都是 List 接口的子类,但是使用起来有如下的区别,为了方便大家笔试,列出此内容:

62709750138
62709750138

LinkedList集合

实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,而LinkedList提供了大量首尾操作的方

法。这些方法我们作为了解即可:

  • public void addFirst(E e) :将指定元素插入此列表的开头。

  • public void addLast(E e) :将指定元素添加到此列表的结尾。

  • public E getFirst() :返回此列表的第一个元素。

  • public E getLast() :返回此列表的最后一个元素。

  • public E removeFirst() :移除并返回此列表的第一个元素。

  • public E removeLast() :移除并返回此列表的最后一个元素。

  • public E pop() :从此列表所表示的堆栈处弹出一个元素。

  • public void push(E e) :将元素推入此列表所表示的堆栈。

  • public boolean isEmpty() :如果列表不包含元素,则返回true。

    LinkedList是List的子类,List中的方法LinkedList都是可以使用,这里就不做详细介绍,我们只需要了

    解LinkedList的特有方法即可。在开发时,LinkedList集合也可以作为堆栈,队列的结构使用。(了解

    即可)

    范例:验证 LinkedList 子类

    62709762807
    62709762807

    Iterator迭代器

    Iterator接口

    ​ 在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,JDK专门提供了一个接口

    java.util.Iterator 。 Iterator 接口也是Java集合中的一员,但它与 Collection 、 Map 接口有所

    不同, Collection 接口与 Map 接口主要用于存储元素,而 Iterator 主要用于迭代访问(即遍历)

    Collection 中的元素,因此 Iterator 对象也被称为迭代器。

    ​ 想要遍历Collection集合,那么就要获取该集合迭代器完成迭代操作,下面介绍一下获取迭代器的方

    法:

    public Iterator iterator() : 获取集合对应的迭代器,用来遍历集合中的元素的。

    下面介绍一下迭代的概念:

    迭代:即Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果

    有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有元素全部取

    出。这种取出方式专业术语称为迭代。

    Iterator接口的常用方法如下:

    ​ public E next() :返回迭代的下一个元素。

    ​ public boolean hasNext() :如果仍有元素可以迭代,则返回 true。

    迭代器的实现原理

    我们在之前案例已经完成了Iterator遍历集合的整个过程。当遍历集合时,首先通过调用t集合的

    iterator()方法获得迭代器对象,然后使用hashNext()方法判断集合中是否存在下一个元素,如果存在,

    则调用next()方法将元素取出,否则说明已到达了集合末尾,停止遍历元素。

    增强for

    增强for循环(也称for each循环)是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合的。它

    的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作。

    格式:

    62709797636
    62709797636

    它用于遍历Collection和数组。通常只进行遍历元素,不要在遍历的过程中对集合元素进行增删操作。

    练习1:遍历数组

    62709801799
    62709801799

    练习2:遍历集合

    62709803656
    62709803656

    tips: 新for循环必须有被遍历的目标。目标只能是Collection或者是数组。新式for仅仅作为遍历操

    作出现。

Set接口

​ Set 接口也是 Collection 的子接口,与 List 接口最大的不同在于,Set 接口里面的内容是不允许重复的。

​ Set 接口并没有对 Collection 接口进行扩充,基本上还是与 Collection 接口保持一致。因为此接口没有 List 接口中定义 的 get(int index)方法,所以无法使用循环进行输出。

​ 那么在此接口中有两个常用的子类:HashSet、TreeSet

​ java.util.Set 接口和 java.util.List 接口一样,同样继承自 Collection 接口,它与Collection 接口中的方法基本一致,并没有对 Collection 接口进行功能上的扩充,只是比Collection 接口更加严格了。与 List 接口不同的是, Set 接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。

Set 集合有多个子类,这里我们介绍其中的 java.util.HashSet 、 java.util.LinkedHashSet 这两个集合。

tips:Set集合取出元素的方式可以采用:迭代器、增强for。

HashSet集合介绍

​ java.util.HashSet 是 Set 接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的

(即存取顺序不一致)。 java.util.HashSet 底层的实现其实是一个 java.util.HashMap 支持,由于我

们暂时还未学习,先做了解。

​ HashSet 是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证

元素唯一性的方式依赖于: hashCode 与 equals 方法。

​ 我们先来使用一下Set集合存储,看下现象,再进行原理的讲解:

62709825850
62709825850

​ 既然 Set 接口并没有扩充任何的 Collection 接口中的内容,所以使用的方法全部都是 Collection 接口定义而来的。

​ HashSet 属于散列的存放类集,里面的内容是无序存放的。

62709831581
62709831581
62709833698
62709833698
62709834770
62709834770

HashSet集合存储数据的结构(哈希表)

​ 什么是哈希表呢?

​ 在JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一

个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率

较低。而JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转

换为红黑树,这样大大减少了查找时间。

​ 简单的来说,哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下图所示。

​ 总而言之,JDK1.8引入红黑树大程度优化了HashMap的性能,那么对于我们来讲保证HashSet集合元素的唯一,其实就是根据对象的hashCode和equals方法来决定的。如果我们往集合中存放自定义的对象,那么保证其唯一,就必须复写hashCode和equals方法建立属于当前对象的比较方式。

HashSet存储自定义类型元素

​ 给HashSet中存放自定义类型元素时,需要重写对象中的hashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一

LinkedHashSet

​ 我们知道HashSet保证元素唯一,可是元素存放进去是没有顺序的,那么我们要保证有序,怎么办呢?在HashSet下面有一个子类 java.util.LinkedHashSet ,它是链表和哈希表组合的一个数据存储结

构。

小结

​ 关于 TreeSet 的排序实现,如果是集合中对象是自定义的或者说其他系统定义的类没有实现 Comparable 接口,则不能实现 TreeSet 的排序,会报类型转换(转向 Comparable 接口)错误。 换句话说要添加到 TreeSet 集合中的对象的类型必须实现了 Comparable 接口。

​ 不过 TreeSet 的集合因为借用了 Comparable 接口,同时可以去除重复值,而 HashSet 虽然是 Set 接口子类,但是对于没有复写 Object 的 equals 和 hashCode 方法的对象,加入了 HashSet 集合中也是不能去掉重复值的。

集合输出(重点)

​ 已经学习过了基本的集合操作,那么对于集合的输出本身也是有多种形式的,可以使用如下的几种方式:

· Iterator 迭代输出(90%)、ListIterator(5%)、Enumeration(1%)、foreach(4%)

​ 但是在讲解输出的时候一定要记住以下的原则:“只要是碰到了集合,则输出的时候想都不想就使用 Iterator 进行输出。”

Iterator(绝对重点)

​ Iterator 属于迭代输出,基本的操作原理:是不断的判断是否有下一个元素,有的话,则直接输出。 此接口定义如下:

public interface Iterator<E>

​ 要想使用此接口,则必须使用 Collection 接口,在 Collection 接口中规定了一个 iterator()方法,可以用于为 Iterator 接 口进行实例化操作。

Map 接口(重点)

​ 以上的 Collection 中,每次操作的都是一个对象,如果现在假设要操作一对对象,则就必须使用 Map 了,类似于以 下一种情况:

· 张三 123456

· 李四 234567

​ 那么保存以上信息的时候使用 Collection 就不那么方便,所以要使用 Map 接口。里面的所有内容都按照 keyvalue 的形式保存,也称为二元偶对象。

此接口定义如下:

public interface Map<K,V>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值