集合(重点,常用类库里面最重要的一部分)
类集设置的目的(重点)
普通的对象数组的最大问题在于数组中的元素个数是固定的,不能动态的扩充大小,所以最早的时候可以通过链表实现一个动态对象数组。但是这样做毕竟太复杂了,所以在 Java 中为了方便用户操作各个数据结构,所以引入了类集的概念,有时候就可以把类集称为 java 对数据结构的实现。
在整个类集中的,这个概念是从 JDK 1.2(Java 2)之后才正式引入的,最早也提供了很多的操作类,但是并没有完整的提出类集的完整概念。
类集中最大的几个操作接口:Collection、Map、Iterator,这三个接口为以后要使用的最重点的接口。
所有的类集操作的接口或类都在 java.util
Collection集合
1.1 集合概述
集合:集合是java中提供的一种容器,可以用来存储多个数据。 集合和数组既然都是容器,它们有啥区别呢?
-
数组的长度是固定的。
集合的长度是可变的。
-
数组中存储的是同一类型的元素,可以存储基本数据类型值。
集合存储的都是对象。而且对象的类型可以不一致。在开发中一般当对象多的时候,使用集合进行存储。
1.2 Collection 常用功能
Collection是所有单列集合的父接口,因此在Collection中定义了单列集合(List和Set)通用的一些方法, 这些方法可用于操作所有的单列集合。方法如下:
public boolean add(E e) : 把给定的对象添加到当前集合中 。
public void clear() :清空集合中所有的元素。
public boolean remove(E e) : 把给定的对象在当前集合中删除。
public boolean contains(E e) : 判断当前集合中是否包含给定的对象。
public boolean isEmpty() : 判断当前集合是否为空。
public int size() : 返回集合中元素的个数。
public Object[] toArray() : 把集合中的元素,存储到数组中。
Collection:单列集合类的根接口,用于存储一系列符合某种规则的元素,它有两个重要的子接口,分别是 java.util.List 和 java.util.Set 。其中,
List 的特点是:元素有序、元素可重复,允许多个Null元素对象,。
Set 的特点是:元素无序,而且不可重复,无索引,最多允许有一个Null元素对象。
List 接口的主要实现类有 java.util.ArrayList 和 java.util.LinkedList ,
Set 接口的主要实现类有 java.util.HashSet 和 java.util.TreeSet 。
List集合
List接口介绍
java.util.List 接口继承自 Collection 接口,是单列集合的一个重要分支,习惯性地会将实现了 List 接口的对象称为List集合。在List集合中允许出现重复的元素,所有的元素是以一种线性方式进行存储的,在程序中可以通过索引来访问集合中的指定元素。另外,List集合还有一个特点就是元素有序,即元素的存入顺序和取出顺序一致。
List接口特点:
- 它是一个元素存取有序的集合。例如,存元素的顺序是11、22、33。那么集合中,元素的存储就 是按照11、22、33的顺序完成的)。
- 它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道 理)。
- 集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。
List 子接口的定义:
public interface List<E> extends Collection<E>
List接口中常用方法
List作为Collection集合的子接口,不但继承了Collection接口中的全部方法,而且还增加了一些根据元 素索引来操作集合的特有方法,如下:
public void add(int index, E element) : 将指定的元素,添加到该集合中的指定位置上。
public E get(int index) :返回集合中指定位置的元素。
public E remove(int index) : 移除列表中指定位置的元素, 返回的是被移除的元素。
public E set(int index, E element) :用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
List的子类
ArrayList集合
java.util.ArrayList 集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以 ArrayList 是最常用的集合。
许多程序员开发时非常随意地使用ArrayList完成任何需求,并不严谨,这种用法是不提倡的。
ArrayList 是 List 接口的子类,此类的定义如下:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable
例:增加及取得元素
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()方法完成输出的。
可以发现,此时的对象数组并没有长度的限制,长度可以任意长,只要是内存够大就行。
例:1、使用 remove()方法删除若干个元素,并且使用循环的方式输出。
2、根据指定位置取的内容的方法,只有 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 进行操作。
public class VectorDemo01 {
public static void main(String[] args) {
List<String> all = new Vector<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接口单独定义的
}
}
}
以上的操作结果与使用 ArrayList 本身并没有任何的区别。因为操作的时候是以接口为操作的标准。
但是 Vector 属于 Java 元老级的操作类,是最早的提供了动态对象数组的操作类,在 JDK 1.0 的时候就已经推出了此类的使用,只是后来在 JDK 1.2 之后引入了 Java 类集合框架。但是为了照顾很多已经习惯于使用 Vector 的用户,所以在 JDK 1.2 之后将 Vector 类进行了升级了,让其多实现了一个 List 接口,这样才将这个类继续保留了下来。
Vector 类和 ArrayList 类的区别(重点)
这两个类虽然都是 List 接口的子类,但是使用起来有如下的区别,为了方便大家笔试,列出此内容:
No. | 区别点 | ArrayList | Vector |
---|---|---|---|
1 | 时间 | 是新的类,是在 JDK 1.2 之后推出的 | 是旧的类是在 JDK 1.0 的时候就定义的 |
2 | 性能 | 性能较高,是采用了异步处理 | 性能较低,是采用了同步处理 |
3 | 输出 | 支持 Iterator、ListIterator 输出 | 除了支持 Iterator、ListIterator 输出,还支持 Enumeration 输出 |
LinkedList集合
java.util.LinkedList 集合数据存储的结构是链表结构。方便元素添加、删除的集合。
LinkedList是一个双向链表
此类的使用几率是非常低的,但是此类的定义如下:
public class LinkedList<E> extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, Serializable
此类继承了 AbstractList,所以是 List 的子类。但是此类也是 Queue 接口的子类,Queue 接口定义了如下的方法:
No. | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public boolean add(E e) | 普通 | 增加元素,如果有容量限制,并且已满,则抛出异常 |
2 | public E element() | 普通 | 取得,但是不删除当前元素,如果对列为空则抛出异常 |
3 | boolean offer(E e) | 普通 | 添加,如果有容量限制,并且已满,只是无法添加, 但不抛出异常,返回 false |
4 | E peek() | 普通 | 取得头元素,但是不删除,如果队列为空,则返回 null |
5 | E poll() | 普通 | 取得头元素,但是删除, 如果队列为空,则返回 null |
6 | E remove() | 普通 | 删除当前元素, 如果对列为空则抛出异常 |
实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,而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 子类
public class TestDemo {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<String>();
queue.add("A");
queue.add("B");
queue.add("C");
int len=queue.size();//把queue的大小先取出来,否则每循环一次,移除一个元素,就少一个元素,那么queue.size()在变小,就不能循环queue.size()次了。
for (int x = 0; x <len; x++) {
System.out.println(queue.poll());
}
System.out.println(queue);
}
}
Iterator迭代器(重点)
Iterator 属于迭代输出,基本的操作原理:是不断的判断是否有下一个元素,有的话,则直接输出。
此接口定义如下:
public interface Iterator<E>
要想使用此接口,则必须使用 Collection 接口,在 Collection 接口中规定了一个 iterator()方法,可以用于为 Iterator 接口进行实例化操作。
通过 Collection 接口为其进行实例化之后,一定要记住,Iterator 中的操作指针是在第一条元素之上,当调用 next()方 法的时候,获取当前指针指向的值并向下移动,使用
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。
void remove() : 删除当前内容
Iterator 输出
public class IteratorDemo01 {
public static void main(String[] args) {
Collection<String> all = new ArrayList<String>();
all.add("A");
all.add("B");
all.add("C");
all.add("D");
all.add("E");
Iterator<String> iter = all.iterator();
while (iter.hasNext()) {// 判断是否有下一个元素
String str = iter.next(); // 取出当前元素
System.out.print(str + "、");
}
}
}
以上的操作是 Iterator 接口使用最多的形式,也是一个标准的输出形式。
但是在使用 Iterator 输出的时候有一点必须注意,在进行迭代输出的时候如果要想删除当前元素,则只能使用 Iterator 接口中的 remove()方法,而不能使用集合中的 remove()方法。否则将出现未知的错误。
public class IteratorDemo02 {
public static void main(String[] args) {
Collection<String> all = new ArrayList<String>();
all.add("A");
all.add("B");
all.add("C");
all.add("D");
all.add("E");
Iterator<String> iter = all.iterator();
while (iter.hasNext()) {// 判断是否有下一个元素
String str = iter.next(); // 取出当前元素
if (str.equals("C")) {
iter.remove(str); // all.remove(str)是错误的,调用了集合中的删除
} else {
System.out.print(str + "、");
}
}
}
}
/**
*iter.next();必须先指向这个元素才能删除
*iter.remove();
*/
从实际的开发角度看,元素的删除操作出现的几率是很小的,基本上可以忽略,即:集合中很少有删除元素 的操作。
Iterator 接口本身可以完成输出的功能,但是此接口只能进行由前向后的单向输出。如果要想进行双向输出,则必须 使用其子接口 —— ListIterator。
迭代器的实现原理
我们在之前案例已经完成了Iterator遍历集合的整个过程。当遍历集合时,首先通过调用t集合的 iterator()方法获得迭代器对象,然后使用hashNext()方法判断集合中是否存在下一个元素,如果存在, 则调用next()方法将元素取出,否则说明已到达了集合末尾,停止遍历元素。
ListIterator(理解)
ListIterator 是可以进行双向输出的迭代接口,此接口定义如下:
public interface ListIterator<E> extends Iterator<E>
此接口是 Iterator 的子接口,此接口中定义了以下的操作方法:
No. | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | void add(E e) | 普通 | 增加元素 |
2 | boolean hasPrevious() | 普通 | 判断是否有前一个元素 |
3 | E previous() | 普通 | 取出前一个元素 |
4 | void set(E e) | 普通 | 修改元素的内容 |
5 | int previousIndex() | 普通 | 前一个索引位置 |
6 | int nextIndex() | 普通 | 下一个索引位置 |
但是如果要想使用 ListIterator 接口,则必须依靠 List 接口进行实例化。
List 接口中定义了以下的方法:ListIterator listIterator()
使用 ListIterator 输出
public class ListIteratorDemo01 {
public static void main(String[] args) {
List<String> all = new ArrayList<String>();
all.add("A");
all.add("B");
all.add("C");
all.add("D");
all.add("E");
ListIterator<String> iter = all.listIterator();
System.out.print("从前向后输出:");
while (iter.hasNext()) {
System.out.print(iter.next() + "、");
}
System.out.print("\n从后向前输出:");
while (iter.hasPrevious()) {
System.out.print(iter.previous() + "、");
}
}
}
此处有一点需要注意的是,如果要想进行由后向前的输出,则首先必须先进行由前向后的输出。
但是,此接口一般使用较少
废弃的接口:Enumeration(了解)
Enumeration 是一个非常古老的输出接口,其也是一个元老级的输出接口,最早的动态数组使用 Vector 完成,那么只 要是使用了 Vector 则就必须使用 Enumeration 进行输出
此接口定义如下:
public interface Enumeration<E>
在 JDK 1.5 之后,此接口实际上也已经加入了泛型操作。此接口常用方法如下:
No. | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | boolean hasMoreElements() | 普通 | 判断是否有下一个元素 |
2 | E nextElement() | 普通 | 取出当前元素 |
但是,与 Iterator 不同的是,如果要想使用 Enumeration 输出的话,则还必须使用 Vector 类完成,在类中定义了如下 方法:public Enumeration elements()
验证 Enumeration 接口
public class EnumerationDemo01 {
public static void main(String[] args) {
Vector<String> v = new Vector<String>();
v.add("A");
v.add("B");
v.add("C");
Enumeration<String> enu = v.elements();
while (enu.hasMoreElements()) {
System.out.println(enu.nextElement());
}
}
}
需要注意的是,在大部分的情况下,此接口都不再使用了,但是对于一些古老的类库,本身依然要使用此接口进行 操作,所以此接口一定要掌握。
增强for循环(forEach)
增强for循环(也称for each循环)是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作。
格式:
for(元素的数据类型 变量名 : Collection集合or数组名){
//写操作代码
}
==*==它用于遍历Collection和数组。通常只进行遍历元素,不要在遍历的过程中对集合元素进行增删操作。
遍历数组
public class Demo {
public static void main(String[] args) {
int[] arr = {3,5,6,87};
//使用增强for遍历数组
for(int a : arr){//a代表数组中的每个元素
System.out.println(a);
}
}
}
遍历集合
public class NBFor {
public static void main(String[] args) {
Collection<String> coll = new ArrayList<String>();
coll.add("锄禾日当午");
coll.add("汗滴禾下土");
coll.add("谁知盘中餐");
coll.add("粒粒皆辛苦");
//使用增强for遍历
for(String s :coll){//接收变量s代表 代表被遍历到的集合元素
System.out.println(s);
}
}
}
增强for循环必须有被遍历的目标。目标只能是Collection或者是数组。增强for仅仅作为遍历操作出现。内部使用了迭代器。
在使用 foreach 输出的时候一定要注意的是,里面的操作泛型要指定具体的类型,这样在输出的时候才会更加有针对性