1 集合框架
1.1 集合框架的概述
1.1.1 集合简介
首先我们考虑一下,为什么我们要使用集合。根据我自己做的练习题,我们假设这么一个场景。在一个公司里面,有一个Hr,他的手里有20个雇员的信息(分别是姓名、工号、年薪)。那么我们可以根据工号来顺序存储每个雇员,但是在计算机的内存中是怎么实现的呢?我们是不是要一开始就划分出固定的内存去存储这20名雇员,然后将这20名雇员逐一放到内存里面?如果现在公司来了一名新员工,要怎么办呢?我们在内存中将20条记录全部下移后,再从开头插入新的记录?还是创建一个映射记住每个对象的位置?当决定如何存储对象的集合时,我们必须考虑考虑以下几个问题:
(1)对于对象集合,必须执行的操作主要是下面的三种:
- 添加对象
- 删除对象
- 查找对象
添加对象:我们要确认如何将新的对象添加到集合中。可以将对象添加到集合的开头、末尾或者中间的某个逻辑位置。
删除对象:从一个集合中删除对象后,对象集合中现有对象会有什么影响?可能必须将内存移来移去,或者在现有对象留驻的内存位置留下一个“洞”。
查找对象:在内存中建立对象集合后,必须确定如何定位特定对象。可以通过建立一种机制,利用这个机制可以根据索引号,或者某种搜索条件(例如工号),直接定位到目标对象。
开始学习集合的时候 我有一个疑问就是:我们之前学习的数组不就是拿来存取一组同类型的对象吗?为什么我们要用集合,甚至说,在之后吧我们用集合的次数远远大于用数组的次数。
但是数组存在一些不可改变的缺点,所以没有办法使用数组比较方便、快捷地完成如上的场景。
在多数情况下,我们需要能够存取一组数据的容器,这一点虽然数组可以实现,但是如果我们需要存储的数据个数并不确定,或者说容器里面的数据个数是随时在变化的。我们需要一种存储数据的容器,它可以自动改变自己所能存放数据量的大小。这一点,如果用数组来存储,就十分笨拙(因为一个数组的大小是不可变的,那么我们在上述情景中,如果使用数组,就要不停的创建新的大小的数组)。
我们再假设这样一种场景:假定一个购物网站,经过一段时间的运行,我们已经存储了一系列的购物清单了,购物清单中有商品信息。如果我们想要知道这段时间里面有多少种商品被销售出去了。那么我们就需要一个容器能够自动的过滤掉购物清单中的关于商品的重复信息。如果使用数组,这也是很难实现的。
最后再想想,我们经常会遇到这种情况,我知道某个人的帐号名称,希望能够进一步了解这个人的其他的一些信息。也就是说,我们在一个地方存放一些用户信息,我们希望能够通过用户的帐号来查找到对应的该用户的其他的一些信息。再举个查字典例子:假设我们希望使用一个容器来存放单词以及对于这个单词的解释,而当我们想要查找某个单词的意思的时候,能够根据提供的单词在这个容器中找到对应的单词的解释。如果使用数组来实现的话,就更加的困难了。
正是为了解决这些问题,Java里面设计了集合,不同的集合用来保存不同类型的对象。
1.1.2 集合框架的定义
集合框架是由一组用来操作对象的接口组成。不同的接口描述不同类型的集合。
1.1.3集合的分类
Java集合类类库的用途是"保存对象",我们将其分为两个不同的概念:
(1) Collection: Collection接口是所有集合接口的根接口。集合管理的单个对象被称为元素(element)(这里说Collection是所有集合接口的根接口可以说对,也可以说不对。因为Collection是Iterator的子接口)。
(2) Map:一组成对的"键值对"对象。可能刚初起来这应该是一个Collection,其元素是成对的对象,但是这样的设计并没有很直观,所以我们将Map提取出来形成一个独立的概念。 Map可以扩展成多维Map,不需要增加新的概念,只要让Map中的键值对的每个值也是一个Map就可以。
Collection和Map的区别在于集合中每个位置保存的元素个数。Collection每个位置只能保存一个元素。Map保存的是"键值对",就像是一个小型的数据库。我们可以通过相应的"键"找到该键对应的"值"。
1.2 Collection
1.2.1 常用方法
Collection接口用于表示任何元素(对象)组。在上面的图中,我们也看到了,Collection是List和Set的父接口,并且它自身也是一个接口。它定义了作为集合所应该拥有的一些方法。集合的元素必须是引用数据类型,不能有基本数据类型。
(1) Collection接口支持添加和删除元素的基本操作。
- boolean add(Object element);
- boolean remove(Object element);
(2) 查询操作
- int size();
- boolean isEmpty();
- boolean contains(Object element);
- Iterator iterator();
(3)组操作:作用于元素组的任务,或者同时作用在整个集合的任务。
- boolean containsAll(Collection> collection);
- boolean addAll(Collection> collection);
- void clear();
- void removeAll(Collection> collection);
- void retainAll(Collection> collection);
containsAll()方法允许查找当前集合是否包含了指定集合的所有元素(可以理解为指定集合是否为当前集合的子集)。addAll()方法是把指定的集合所有元素添加到当前集合。clear()方法是清除当前集合的所有元素。removeall()方法除去当前集合的子集(传入的参数子集合)。retainall()方法是取当前集合和参数集合的交集。
1.2.2 Iterator 迭代器
任何的集合,都必须有某种方式可以把东西放进去,然后以某种方式取出来。对于List,add()是插入对象的方法,而get()是取出元素的方法之一。List接口很灵活,可以随时选取任意的元素,或者使用不同的索引号一次取多个元素。
但是,我们把眼光放在更高的层面上的时候,就会发现有一个缺点:要使用一个集合的时候,我们就要知道里面的元素的确切类型。我们原来是用List接口,但是后来我们想换用Set接口,那怎么做呢?Set是不能按照索引号取元素。我想到的是,我们可以写一个通用的代码,只是使用集合,但是不关心集合的类型(无论是List还是Set),那么这个通用的代码要怎么写呢?
这时候Java就给出了迭代器的概念,也是一种设计模式,为了达到上述目的。所以Collection没有提供get()方法。如果需要遍历Collection集合中的元素,就必须使用Iterator 迭代器。
Iterator 就是一个对象,它的工作就是遍历并选择集合中的所有元素。我们不需要关注迭代器的底层代码。
Collection接口的iterator()方法返回一个Iterator。我们可以从头至尾遍历集合,并安全的从集合中删除元素。
下面是迭代器使用的代码:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.function.Consumer;
/*
* Created by qingke07 on 2016/7/27.
*/
public class IteratorTest {
public static void main(String[] args){
Collection collection = new ArrayList();
collection.add("a");
collection.add("b");
collection.add("c");
Iterator it = collection.iterator(); //得到一个迭代器
while (it.hasNext()){
System.out.println("iterator = " + it.next());
}
if (collection.isEmpty()){
System.out.println("cllection is empty");
}else {
System.out.println("cllection is not empty! size = " + collection.size());
}
Iterator it2 = collection.iterator();
while (it2.hasNext()){
System.out.println("remove: " + it2.next());
it2.remove();
}
Iterator it3 = collection.iterator();
if ( ! it3.hasNext()) {
System.out.println("还有元素");
}
if (collection.isEmpty()){
System.out.println("collection is empty");
}
}
}
程序的运行结果为:
iterator = a
iterator = b
iterator = c
cllection is not empty! size = 3
remove: a
remove: b
remove: c
还有元素
collection is empty
通过以上程序,我们可以看到Iterator可以用来:
(1) 使用iterrator()方法,要求集合返回一个Iterator。
(2) 使用hasNext()方法,如果仍有元素可以迭代,则返回 true。(换句话说,如果 next 返回了元素而不是抛出异常,则返回 true)。
(3) 使用next()方法,返回迭代的下一个元素。
(4)使用remove()方法将迭代器新返回的元素删除。
- 在每次调用next()方法时,remove()方法只能调用一次。
Java里面的Iterator使用是十分的简单的。还有就是,针对List还有一个更加高级的ListIterator。
1.3 List
1.3.1简介
List是集合的一种,是列表的意思。它里面的元素按照一定的次序排列。List集合里的元素可以通过位置存取,并且List集合里的元素可以重复。
1.3.2常用的方法
List集合是按照对象的进入顺序进行保存对象,而不做其他的排序操作。
- void add(int index, Object element) :添加对象element到位置index上
- boolean addAll(int index, Collection collection) :在index位置后添加集合collection中所有的元素
- Object get(int index) :取出下标为index的位置的元素
- int indexOf(Object element) :查找对象element 在List中第一次出现的位置
- int lastIndexOf(Object element) :查找对象element 在List中最后出现的位置
- Object remove(int index) :删除index位置上的元素
- Object set(int index, Object element) :将index位置上的对象替换为element 并返回老的元素。
List接口的两种实现类
实现类 | 操作特性 | 元素要求 |
---|---|---|
ArrayList | 提供了快速的基于索引的元素访问,对尾部元素的添加和删除支持较好,但是对集合中间元素的添加和支持较差 | 集合中的元素可以为任意Object子类的对象 |
LinkedList | 对集合中任意位置元素的添加和删除支持较好,但对基于索引的元素访问支持较差 | 集合中的元素可以为任意Object子类的对象 |
1.4 Set
1.5 Map
1.5.1 简介
Map并不是Collection接口的子接口,而是从自己的用于维护"键"-"值"关联的接口层次结构入手。也就是,Map接口描述了从不重复的键到值得映射。Map集合保存键-值对元素,基于键找值操作,使用compareTo或compare方法对键进行排序
1.5.2 常用方法
这里我们可以把Map接口方法分成三组认识:改变、查询、提供可选视图。
(1) 改变操作可以从映射中添加或者删除键-值对。键和值都可以为null。但是,不能把Map作为一个键或者是值添加给本身
- Object put(Object key,Object value):用来存放一个键-值对
- Object remove(Object key):根据key(键),移除一个键-值对,并将值返回
- void putAll(Map mapping) :将另外一个Map中的元素存入当前的Map中
- void clear() :清空当前Map中的元素
(2) 查询操作允许检查映射内容
- Object get(Object key) :根据key(键)取得对应的值
- boolean containsKey(Object key) :判断Map中是否存在某键(key)
- boolean containsValue(Object value):判断Map中是否存在某值(value)
- int size():返回Map中键-值对的个数
- boolean isEmpty() :判断当前Map是否为空
(3) 允许我们把键或者值得组作为集合来处理
- public Set keySet() :返回所有的键(key),并使用Set容器存放
- public Collection values() :返回所有的值(Value),并使用Collection存放
- publicSet entrySet() :返回一个实现Map.Entry接口的元素Set
- 因为Map中键是不能相同的,所以我们用Set集合存放键。因为Map中的值可能不唯一,所以我们用Collection集合来存放值。
1.5.3 Map接口的三种实现类
实现类 | 操作特性 | 成员要求 |
---|---|---|
HashMap | 能满足用户对Map的通用需求 | 每个元素中的键成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法 |
TreeMap | 支持对键有序地遍历,使用时建议先用HashMap增加和删除成员,最后从HashMap生成TreeMap;附加实现了SortedMap接口,支持子Map等要求顺序的操作 | 键成员要求实现Comparable接口,或者使用Comparator构造TreeMap键成员一般为同一类型 |
LinkedHashMap | 保留键的插入顺序,用equals 方法检查键和值的相等性 | 元素可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法 |
1.6 集合框架常用类的比较
Collection/Map | Interface | 元素重复性 | 元素存放顺序(Ordered/Sorted) | 元素中被调用的方法 | 基于哪种数据结构来实现 |
---|---|---|---|---|---|
HashSet | Set | Unique element | No order(随机存放) | equals(),hashCode() | Hash表 |
LinkedHashSet | Set | Unique element | Insertion order(输入顺序) | equals(),hashCode() | Hash表和双向链表 |
TreeSet | SortedSet | Unique element | Sorted(字典顺序) | equals(),compareTo() | 平衡树(Balanced tree) |
ArrayList | List | Allowed | Insertion order | equals() | 数组 |
LinkedList | List | Allowed | Insertion order | equals() | 数组 |
Vector | List | Allowed | Insertion order | equals() | 数组 |
HashMap | Map | Unique key | No order | equals(),hashCode() | Hash表和双向链表 |
LinkedHashMap | Map | Unique key | Key insertion order/Access order of entries | equals(),hashCode() | Hash表和双向链表 |
Hashtable | Map | Unique key | No order | equals(),hashCode() | Hash表 |
TreeMap | SortedMap | Unique key | Sorted in key order | equals(),compareTo() | 平衡树(Balanced tree) |