Java的容器
定义好Post
类之后,现在需要对博客文章进行管理。我们可以定义一个PostRepository
类,通过PostRepository
可以做以下操作:
- 创建博客
- 删除博客
- 获取一篇博客的内容
- 获取博客列表
我们已经有面向对象编程的经验了,我们可以给Repository
增加四个方法:
package com.tianmaying.repository;
import com.tianmaying.domain.Post;
public class PostRepository {
public static void add(Post post) {
}
public static Post getPostById(long id) {
return null;
}
public static void remove(long id) {
}
public static ArrayList<Post> getAll() {
return null;
}
}
上面我们定义了四个静态成员方法,我们刚刚讲过static
修饰方法的含义,现在马上就用到了。你现在可能对getAll()
方法返回的ArrayList<Post>
还有点疑惑,没关系,我们马上就要讲到。
先来考虑一个问题,这个PostRepository
里面应该包含什么变量呢? 我们很容易想到就是一组博客。在Java中,我们可以通过数组将一组对象组织在一起,因此我们马上可以想到定义一个Post
的数组。
但是数组的尺寸是固定的,通常情况下程序总是在运行时根据条件来创建对象,我们可能无法预知将要创建对象的个数,甚至是具体的类型。比如PostRepository
中保存多少Blog
对象我们事先是不知道的。
这时我们就需要Java的集合(Collection)类了。我们通常也称集合为容器,因为它们可以帮我们方便地组织和管理一组对象。
所以我们可以给PostRepository
类增加一个静态成员变量:
public class PostRepository {
private static posts = new ArrayList<Post>();
...
}
这里ArrayList
就是Java提供给我们使用的一个集合类,用以保存一个元素序列,并且可以进行元素的访问、插入和删除等操作。
ArrayList<Post>
是一种泛型的写法。泛型就是参数化类型,即ArrayList
所操作的数据类型通过一个类型参数指定。ArrayList
这个容器中可以保存任何类型的变量,而且这些对象访问、插入和删除的逻辑也是相同的。比如,如果我们希望ArrayList
中存放一组整数的话,我们就可以声明List<Integer>
类型的变量。
在Java 7和Java 8中,编译器能够根据变量声明时的泛型类型自动推断出实例化所用的泛型类型。
ArrayList<Post> post1 = new ArrayList<Post>();
ArrayList<Post> post2 = new ArrayList<>();
post2
的初始化就没有指定ArrayList
中的泛型类型,编译器可以推导出来。
List的使用
ArrayList
提供了一系列操作元素序列的方法。下面我们使用ArrayList
来实现PostRepository
的四个方法。
package com.tianmaying.repository;
import java.util.ArrayList;
import java.util.List;
import com.tianmaying.domain.Post;
public class PostRepository {
private static ArrayList<Post> posts = new ArrayList<Post>();
public static void add(Post post) {
posts.add(post);
}
public static Post getPostById(long id) {
for (Post post : posts) {
if (post.getId() == id) {
return post;
}
}
return null;
}
public static void remove(long id) {
for (Post post: posts) {
if (post.getId() == id) {
posts.remove(post);
return;
}
}
}
public static ArrayList<Post> getAll() {
return posts;
}
}
我们看到ArrayList
的用法很简单,可以通过add
方法增加元素,通过remove
方法删除元素。
遍历ArrayList
的方法是一种特殊的for
循环,遍历Java容器中的元素都可以使用这种方法。
我们也可以使用迭代器Iterator
类来完成遍历。Iterator主要有两个方法,基于这两个方法就能进行遍历操作:
next()
方法来获取序列的下一个元素hasNext()
检查序列中是否还有元素
使用Iterator
实现的代码如下:
public static void remove(long id) {
Iterator<Post> iterator = posts.iterator();
while (iterator.hasNext()) {
Post post = iterator.next();
if (post.getId() == id) {
posts.remove(post);
return;
}
}
}
与ArrayList
类似的一种容器是LinkedList
,它们都是List
接口的实现。接口的概念我们后面会讲,现在你只需要知道操作ArrayList
和操作LinkedList
的方式是完全一样的,只不过它们内部的实现机制不一样。
这两者的主要不同在于:
ArrayList
:通过下标随机访问元素快,但是插入、删除元素较慢LinkedList
:插入、删除和移动元素快,但是通过下标随机访问元素性能较低
如果你学过数据结构,就是知道ArrayList
是基于数组实现的,而LinkedList
是基于链表实现的。这两种数据结构的特点决定了这两个容器的不同之处。
Map
Map
故名思议,就是映射,可以将一个对象映射到另一个对象。每一组映射作为一个<键,值>对保存在Map
容器中。Map
和List
一样是一种接口,它的实现HashMap
类,是我们最常使用的一种容器。
我们回顾getPostById
这个方法的实现:
public static Post getPostById(long id) {
for (Post post : posts) {
if (post.getId() == id) {
return post;
}
}
return null;
}
每次都要经过一次遍历,经过比较才能找到id对应的博客。如果能够把作为一个键值对,那么通过id取出一篇博客就更加快速和方便。现在基于Map
来实现博客管理功能,我们新建另外一个类PostRepositoryByMap
:
package com.tianmaying.repository;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.tianmaying.domain.Post;
public class PostRepositoryByMap {
private static Map<Long, Post> postsMap = new HashMap<Long, Post>();
public static void add(Post post) {
postsMap.put(post.getId(), post);
}
public static Post getPostById(long id) {
return postsMap.get(id);
}
public static void remove(long id) {
postsMap.remove(id);
}
public static List<Post> `addAll`方法() {
List<Post> posts = new ArrayList<>();
posts.addAll(postsMap.values());
return posts;
}
}
`
Map
具有两个泛型参数,第一个是键的类型,第二个是值的类型。类型不能是原生类型,必须是引用类型,因此这里第一个类型是long
的包装类Long
。
使用Map
之后,查找和删除博客的实现就非常简单了:
put
方法可以增加一个键值对get
方法就能根据键获取到值remove
方法可以删除键对应的元素
Java编译器会在需要时自动帮我们进行原生数据类型和包装类之间进行转换,比如我们postMap.remove(id);
这条语句传入的是long
原生数据类型,Java编译器知道此时应该传入的是Long
,会自动帮我们做这个转换。
我们可以获取键、值或键值对的集合,分别使用keySet
, values
以及entrySet
。getAll
方法就通过postsMap.values()
获取所有的值,这里就是所有的Blog
对象。
注意这里调用了一个addAll
方法,这个方法是所有容器都有的一个方法,可以把另外一个容器中的元素加入其中。
我们可以这样遍历一个Map:
for (Map.Entry<Long, Post> postEntry : postMap.entrySet()) {
Long id = postEntry.getKey();
Post post = postEntry.getValue();
// 在遍历中操作每一个post
...
}
如果PostRepositoryByMap
的getAll()
方法不使用addAll
方法,而通过遍历的方式逐个将博客对象加入到一个List
中,如何实现呢? 你可以尝试一下。