Java集合的使用:List与Map




David发表在天码营

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中,如何实现呢? 你可以尝试一下。


作者:David
链接:https://zhuanlan.zhihu.com/p/20676879
来源:知乎

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值