Java集合框架入门

这篇博客文章是在Qiita上投稿的。

0.开场白

无论是Java还是其他编程语言,在编程中,仅仅使用数组来存储数据往往效果不佳,因此经常会出现各种问题。因此,数据结构变得非常重要,而这方面的表达方法因语言而异。

如果用C++,就可以使用vector、list等标准库的容器。(虽然我没有太多经验)用C#的话,好像要用到LINQ这个东西。如果用PHP,则可以用数组来做很多事情(但使用方法要谨慎)。用Python的话,根据需要可以使用列表、元组、字典等来选择不同的。

1. 什么是集合框架?

说到Java。在Java中,我们使用集合框架。集合框架提供了需要表示数据的各种数据结构,包括列表(值按照顺序排列的集合),集合(值不一定按照顺序排列,但只有一个相同值的集合),映射(根据键值对表示的集合)等(还有双端队列Deque,只能从两端插入和提取值,但由于用途有限,这次省略)。

2. 列表

首先是列表。列表是一种按照从0开始的索引存储数据的对象。它是基于List接口实现的,常用的有ArrayList(可变长数组)和LinkedList(链表)两个类。
ArrayList类类似于C++中的vector。注意,在Java中也有Vector类,但在Java8及以后版本中已被弃用。
它们的定义如下:

List<Integer> arrayList = new ArrayList<>();
List<Integer> linkedList = new LinkedList<>();

在调用构造函数时将决定在哪个类中定义。顺便提一下,类和尖括号(<>)之间的是称为”钻石操作符”,用于省略元素类型的记法。以下是常用操作的示例。尽管下列代码是针对ArrayList编写的,但在LinkedList中执行的处理相同。

List<String> arrayList = new ArrayList<>();
List<String> linkedList = new LinkedList<>();
arrayList.add("Apple");  // リストの最後に要素"Apple"を追加
arrayList.get(5);  // リストの5 + 1番目の要素を取得する
arrayList.set(3, "Orange");  // リストの3 + 1番目の要素の値を"Orange"に設定する
arrayList.remove(4); // リストの4 + 1番目の要素を削除し、後続のインデックスを1つ減らす
arrayList.size();  // リストの要素数を返す
arrayList.clear();  // リストを空にする
// arrayListの要素一覧を出力する
for (String data : arrayList) {
    System.out.println(data);
}

然而,在上面我写道“执行的过程是相同的”,但是ArrayList和LinkedList在实现方法上是不同的,它们各自都有自己的优势和劣势。
诸如get和set之类的操作,在ArrayList中,每个元素在内存中都被索引,因此速度较快;而诸如add和remove之类的操作,LinkedList中的元素通过彼此的链接信息来连接(类似于C语言的指针),因此速度较快。
因此,如果从数据库或其他地方获取一定数量的大型数据,并且对其中的元素进行访问是主要目的的话,那么ArrayList更适合;而如果列表中频繁添加或删除数据的情况下,LinkedList更适合。

3. 套装

集合和列表一样,是一个将数据组合在一起的概念,但与列表不同的是,集合没有索引,并且不能包含重复的值。如果你学过数学,你可以把集合想象成集合和拓扑学的概念。这里的集合是基于Set接口实现的,常用的有HashSet、TreeSet和LinkedHashSet三种。它们分别定义如下。

Set<String> hashSet = new HashSet<>();
Set<String> treeList = new TreeList<>();
Set<String> linkedHashSet = new LinkedHashSet<>();

基本操作如下所示。虽然这里是针对HashSet写的,但其他两个也是一样的。请注意,没有get和set这样的操作。

Set<String> hashSet = new HashSet<>();
Set<String> treeSet = new TreeSet<>();
Set<String> linkedHashSet = new LinkedHashSet<>();
hashSet.add("Banana");  // 集合の要素に"Banana"が含まれていなければ追加する
hashSet.remove("Lemon"); // 集合に"Lemon"が含まれていたら削除する
hashSet.size();  // 集合の要素数を返す
// hashSetの要素一覧を出力する
for (String data : hashSet) {
    System.out.println(data);
}

HashSet、TreeSet和LinkedHashSet的区别在于,HashSet的处理速度很快,但顺序不被保证;TreeSet以排序的状态保存元素;LinkedHashSet则按添加顺序保持元素。

treeSet.add("Banana");
treeSet.add("Orange");
treeSet.add("Apple");
for (String data : treeSet) {
    System.out.println(data);
}

如果是这样,输出会按照“苹果” -> “香蕉” -> “橙子”的顺序进行。

linkedHashSet.add("Banana"); 
linkedHashSet.add("Orange");
linkedHashSet.add("Apple"); 
for (String data : linkedHashSet) {
    System.out.println(data);
}

如果是这样的话,“Banana” -> “Orange” -> “Apple”会依次输出。

4. 地图

地图是一种数据结构,它不仅保存值,还与指定的键一起组成数据。地图中的键是唯一的(不存在相同的键),但是值可以有多个相同的。
地图是根据Map接口实现的,常用的有HashMap、TreeMap和LinkedHashMap。每个地图的定义如下。

Map<String, Integer> hashMap = new HashMap<>();
Map<String, Integer> treeMap = new TreeMap<>();
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();

基本操作如下:使用put方法将键值对添加到映射中。

Map<String, Integer> hashMap = new HashMap<>();
Map<String, Integer> treeMap = new TreeMap<>();
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
hashMap.put("Tomato", 300);  // マップのキー"Tomato"の値を300に設定する
hashMap.get("Tomato");  // マップのキーが"Tomato"のものの値を返す(なければnull)
hashMap.remove("Lemon"); // マップにキーが"Lemon"のものが含まれていたら削除する
hashMap.size();  // マップの要素数を返す
// マップの要素ごとにキー:値という形で出力する
for (Map.Entry<String, Integer> data : hashSet.entrySet()) {
    System.out.println(data.getKey() + ":" + data.getValue());
}

输出所有数据稍微麻烦一些,对吧。
这里,与集合一样,HashMap是快速的但不保证顺序,TreeMap按键保持数据排序,LinkedHashMap按添加数据的顺序保持。

5. 最后

我在Java中以快速的方式介绍了常用的集合。虽然还有其他一些集合可供选择,但如果能根据实际需求灵活运用我所介绍的集合,我认为在Java中处理数据时不会遇到太多问题。
之前我使用HashMap定义了一个包含名称和某个对象组合的数据,但后来意识到需要按名称的升序进行输出,于是匆忙将其改为TreeMap。大家在使用集合时也应考虑自己的目的,并选择最合适的集合。