什么是集合?
- 集合,或者叫容器,是一个包含多个元素的对象;
- 集合可以对数据进行存储,检索,操作;
- 它们可以把许多个体组织成一个整体:
- 比如一副扑克牌(许多牌组成的集合);
- 比如一个电话本(许多姓名和号码的映射)。
什么是集合框架?
集合框架是一个代表、操作集合的统一架构。所有的集合框架都包含以下几点:
接口:表示集合的抽象数据类型。接口允许我们操作集合时不必关注具体实现,从而达到“多态”。在面向对象编程语言中,接口通常用来形成规范。
实现类:集合接口的具体实现,是重用性很高的数据结构。
算法:用来根据需要对实体类中的对象进行计算,比如查找,排序。
同一种算法可以对不同的集合实现类进行计算,这是利用了“多态”。
重用性很高。
不仅 Java,其他语言也有一些集合框架,比如 C艹 的 STL(标准模板库),Smalltalk 的集合层次结构。不同于他们陡峭的学习曲线,Java 集合框架设计的更加合理,学习起来更加轻松。
使用 Java 集合框架能有以下几点好处:
编码更轻松:Java 集合框架为我们提供了方便使用的数据结构和算法,让我们不用从头造轮子,直接操心上层业务就好了。
代码质量更上一层楼:Java 集合框架经过几次升级迭代,数据结构和算法的性能已经优化地很棒了。由于是针对接口编程,不同实现类可以轻易互相替换。这么优雅的设计,省下你自己磨练多少工夫,恩?!
减少学习新 API 的成本:过去每个集合 API 下还有子 API 来对 API 进行操作,你得学好几层才能知道怎么使用,而且还容易出错。现在好了!有了标准的 Java 集合框架,每个 API 都继承自己顶层 API,只负责具体实现,一口气学 5 个集合,不费劲!
照猫画虎也容易多了:由于顶层接口已经把基础方法都定义好了,你只要实现接口,把具体实现方法填好,再也不用操心架构设计。
Java 集合框架主要结构图
如上图所示,Java 的集合主要按两种接口分类:Collection, Map。
Collection 接口
Collection 作为集合的一个根接口,定义了一组对象和它的子类需要实现的 15 个方法:
对集合的基础操作,比如 :
int size()
获取元素个数
boolean isEmpty()
是否个数为 0
boolean contains(Object element)
是否包含指定元素
boolean add(E element)
添加元素,成功时返回 true
boolean remove(Object element)
删除元素,成功时返回 true
Iterator<E> iterator()
获取迭代器
还有一些操作整个集合的方法,比如 :
boolean containsAll(Collection<?> c)
是否包含指定集合 c 的全部元素
boolean addAll(Collection<? extends E> c)
添加集合 c 中所有的元素到本集合中,如果集合有改变就返回 true
boolean removeAll(Collection<?> c)
删除本集合中和 c 集合中一致的元素,如果集合有改变就返回 true
boolean retainAll(Collection<?> c)
保留本集合中 c 集合中两者共有的,如果集合有改变就返回 true
void clear()
删除所有元素
还有对数组操作的方法:
Object[] toArray()
返回一个包含集合中所有元素的数组
<T> T[] toArray(T[] a)
返回一个包含集合中所有元素的数组,运行时根据集合元素的类型指定数组的类型
在 JDK 8 以后,Collection 接口还提供了从集合获取连续的或者并行流:
Stream<E> stream()
Stream<E> parallelStream()
遍历 Collection 的几种方式:
1.for-each语法
Collection<Person> persons = new ArrayList<Person>();
for (Person person : persons) {
System.out.println(person.name);
}
2.使用 Iterator 迭代器
Collection<Person> persons = new ArrayList<Person>();
Iterator iterator = persons.iterator();
while (iterator.hasNext) {
System.out.println(iterator.next);
}
3.使用 aggregate operations 聚合操作
Collection<Person> persons = new ArrayList<Person>();
persons
.stream()
.forEach(new Consumer<Person>() {
@Override
public void accept(Person person) {
System.out.println(person.name);
}
});
Aggregate Operations 聚合操作
在 JDK 8 以后,推荐使用聚合操作对一个集合进行操作。聚合操作通常和 lambda 表达式结合使用,让代码看起来更简洁(因此可能更难理解)。下面举几个简单的栗子:
1.使用流来遍历一个 ShapesCollection,然后输出红色的元素:
myShapesCollection.stream()
.filter(e -> e.getColor() == Color.RED)
.forEach(e -> System.out.println(e.getName()));
2.你还可以获取一个并行流(parallelStream),当集合元素很多时使用并发可以提高效率:
myShapesCollection.parallelStream()
.filter(e -> e.getColor() == Color.RED)
.forEach(e -> System.out.println(e.getName()));
3.聚合操作还有很多操作集合的方法,比如说你想把 Collection 中的元素都转成 String 对象,然后把它们 连起来:
String joined = elements.stream()
.map(Object::toString)
.collect(Collectors.joining(", "));
看起来是不是非常简洁呢!
聚合操作还有很多功能,这里仅做介绍,想要了解更多可以查看Aggregate Operations 官方指引。
Iterator 迭代器
在Java 集合解析:Iterator 和 Java 集合解析:ListIterator 我介绍了 Collection 的迭代器 Iterator 以及用于 List 的迭代器 ListIterator。
结合 Collection 和 Iterator 可以实现一些复用性很强的方法,比如这样:
public static void filter(Collection<?> c) {
for (Iterator<?> it = c.iterator(); it.hasNext(); )
if (!condition(it.next()))
it.remove();
}
这个 filter 方法是多态的,可以用于所有 Collection 的子类、实现类。 这个例子说明了使用 Java 集合框架我们可以很随便就写出 “优雅,可拓展,复用性强” 的代码~
总结
Collection 接口是类集框架的基础之一。
它创建所有类集都将拥有的 15 个核心方法。
因为几乎所有集合类集都实现了 Collection接口,所以熟悉它对于清楚地理解框架是必要的。
接下来将逐步了解集合框架的各个子接口及实现类。
感谢 密哥 提醒,parallel 应该是并行,而不是并发。
并发与并行的概念区别还是挺大的。
并行”是指无论从微观还是宏观,二者都是一起执行的,就好像两个人各拿一把铁锨在挖坑,一小时后,每人一个大坑。
而“并发”在微观上不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行,从宏观外来看,好像是这些进程都在执行,这就好像两个人用同一把铁锨,轮流挖坑,一小时后,两个人各挖一个小一点的坑,要想挖两个大一点得坑,一定会用两个小时。
从以上本质不难看出,“并发”执行,在多个进程存在资源冲突时,并没有从根本提高执行效率。
{
学习多线程的时候会遇到一个名词:并发。这是属于操作系统中的词汇,需要了解并发和并行的区别,从网上搜集了几种说法帮助理解。
一:并发
并发是指一个处理器同时处理多个任务。
并行是指多个处理器或者是多核的处理器同时处理多个不同的任务。并发是逻辑上的同时发生(simultaneous),而并行是物理上的同时发生。来个比喻:并发是一个人同时吃三个馒头,而并行是三个人同时吃三个馒头。
二:并行
并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行。
就好像两个人各拿一把铁锨在挖坑,一小时后,每人一个大坑。所以无论从微观还是从宏观来看,二者都是一起执行的。
并发(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。
这就好像两个人用同一把铁锨,轮流挖坑,一小时后,两个人各挖一个小一点的坑,要想挖两个大一点得坑,一定会用两个小时。
并行在多处理器系统中存在,而并发可以在单处理器和多处理器系统中都存在,并发能够在单处理器系统中存在是因为并发是并行的假象,并行要求程序能够同时执行多个操作,而并发只是要求程序假装同时执行多个操作(每个小时间片执行一个操作,多个操作快速切换执行)。
三:当有多个线程在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,再将时间段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状态.这种方式我们称之为并发(Concurrent)。
当系统有一个以上CPU时,则线程的操作有可能非并发.当一个CPU执行一个线程时,另一个CPU可以执行另一个线程,两个线程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。
并发是在同一时段发生,并行是在同一时刻发生
}
这里 看到的一幅生动形象图:
Thanks