# Java 集合

原文：http://zetcode.com/lang/java/collections/

在本章中，我们将处理集合。 Java 提供了用于数据存储和检索的专门类。 在前面的章节中，我们描述了数组。 集合是对数组的增强。

Java 5 引入了通用集合。 通用集合更灵活，它们是使用数据的首选方式。 通用集合可增强代码重用性，类型安全性和性能。

收集框架中有许多类。 其中一些，例如`ArrayBlockingQueue`或`IdentityHashMap`，是在特定情况下使用的专用容器。 我们将提到一些通用容器。

## Java `ArrayList`

`ArrayList`是可调整大小的动态数组。 它提供对元素的随机访问。 随机访问意味着我们可以在恒定时间内获取任何元素。 `ArrayList`随着添加数据而自动扩展。 与`array`不同，`ArrayList`可以保存多种数据类型的数据。 `ArrayList`中的元素通过整数索引访问。 索引从零开始。 在`ArrayList`的末尾元素的索引以及插入和删除需要固定的时间。

在动态数组的中间插入或删除元素的成本更高。 它要求将所有后面的元素转移过来。 该过程需要线性时间。

您可以在 [Java `ArrayList`教程](/articles/javaarraylist/)中找到有关 Java `ArrayList`的更多信息。

`ArrayListSimpleEx.java`

```java
package com.zetcode;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class ArrayListSimpleEx {

    public static void main(String[] args) {

        List<String> distros = new ArrayList<String>();
        distros.add("Manjaro");
        distros.add("Xubuntu");
        distros.add("Fedora");
        distros.add("elementary");

        for (String distro : distros) {

            System.out.println(distro);
        }

        List<String> capitals = Arrays.asList("Prague", "Bratislava", "Warsaw", 
                "Budapest", "Washington");

        for (String capital : capitals) {

            System.out.println(capital);
        }        
    }
}

```

该示例创建两个列表并打印其内容。

```java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

```

必要的类位于`java.util`包中。

```java
List<String> distros = new ArrayList<String>();

```

创建一个新的`ArrayList`。 该列表可以包含字符串。 列表可以包含的类型在菱形括号之间给出。

```java
distros.add("Manjaro");
distros.add("Xubuntu");
distros.add("Fedora");
distros.add("elementary");

```

使用`add()`方法，我们将四个条目添加到列表中。

```java
for (String distro : distros) {

    System.out.println(distro);
}

```
我们使用`for-each`循环遍历列表。

```java
List<String> capitals = Arrays.asList("Prague", "Bratislava", "Warsaw", 
        "Budapest", "Washington");

```

我们可以使用`Arrays.asList()`方法来初始化列表。

`ArrayList`可以包含多种数据类型。

`ArrayListMultipleEx.java`

```java
package com.zetcode;

import java.util.ArrayList;
import java.util.List;

class Base { }

public class ArrayListMultipleEx {

    public static void main(String[] args) {

        List da = new ArrayList();

        da.add("Java");
        da.add(3.5);
        da.add(55);
        da.add(new Base());

        for (Object el : da) {

            System.out.println(el);
        }
    }
}

```

该示例创建一个`ArrayList`集合。 它包含各种数据类型。

```java
import java.util.ArrayList;

```

从`java.util`包中，导入`ArrayList`类。

```java
List da = new ArrayList();

```

创建一个`ArrayList`集合。

```java
da.add("Java");
da.add(3.5);
da.add(55);
da.add(new Base());

```

我们使用`add()`方法向数组添加四个元素。

```java
for (Object el : da) {

    System.out.println(el);
}

```

我们遍历数组列表，并将其元素打印到控制台。

```java
$ java com.zetcode.ArrayListMultipleEx 
Java
3.5
55
com.zetcode.Base@1535ac

```

在这里，我们可以看到`com.zetcode.ArrayListMultipleEx`的输出。

下一个示例将介绍一些`ArrayList`方法。

`ArrayListMethodsEx.java`

```java
package com.zetcode;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class ArrayListMethodsEx {

    public static void main(String[] args) {

        List<String> names = new ArrayList<String>();
        names.add("Jane");
        names.add("Thomas");
        names.add("Robin");
        names.add("David");
        names.add("Becky");

        System.out.println(names);    
        names.set(1, "Tom");
        System.out.println(names);         

        System.out.format("There are %d elements in the collection%n",
                names.size());

        names.remove(1);
        System.out.format("There are %d elements in the collection%n",
                names.size());            

        System.out.println(names.get(3));

        System.out.println("************");

        Iterator<String> it = names.iterator();

        while (it.hasNext()) {

            System.out.println(it.next());
        }        
    }
}

```

在示例中，我们介绍了`ArrayList`容器的一些有用方法。

```java
List<String> names = new ArrayList<String>();

```

将创建一个通用的`ArrayList`。 我们将元素的数据类型限制为`String`数据类型。 这是通过在`<>`字符之间写入数据类型来完成的。

```java
names.add("Jane");
names.add("Thomas");
names.add("Robin");
names.add("David");
names.add("Becky");

```

我们将五个字符串元素添加到数组列表中。

```java
System.out.println(names); 

```

将容器作为`println()`方法的参数将调用容器的`toString()`方法。 它将集合转换为字符串。

```java
names.set(1, "Tom");

```

`set()`方法用给定元素替换指定索引处的元素。 “托马斯”替换为“汤姆”。

```java
System.out.format("There are %d elements in the collection%n",
        names.size());

```

`ArrayList`的大小由`size()`方法确定。

```java
names.remove(1);

```

我们从集合中删除第二个元素。 参数是集合的索引。

```java
System.out.println(names.get(3));

```

`get()`方法检索容器的第四个元素。

```java
Iterator<String> it = names.iterator();

while (it.hasNext()) {

    System.out.println(it.next());
}   

```

我们使用`Iterator`对象浏览容器。 `hasNext()`方法检查是否还剩下一些元素，`next()`方法检索迭代中的下一个元素。

```java
$ java com.zetcode.ArrayListMethodsEx 
[Jane, Thomas, Robin, David, Becky]
[Jane, Tom, Robin, David, Becky]
There are 5 elements in the collection
There are 4 elements in the collection
Becky
************
Jane
Robin
David
Becky

```

这是`com.zetcode.ArrayListMethodsEx`示例的示例输出。

在下一个示例中，我们继续介绍`ArrayList`的方法。

`ArrayListMethodsEx2.java`

```java
package com.zetcode;

import java.util.ArrayList;
import java.util.List;

public class ArrayListMethodsEx2 {

    public static void main(String[] args) {

        List<String> names = new ArrayList<>();

        names.add("Jane");
        names.add(0, "Thomas");
        names.add(1, "Robin");
        names.add("David");
        names.add("Becky");

        System.out.println(names);

        System.out.println(names.isEmpty());
        System.out.println(names.contains("Jane"));
        System.out.println(names.contains("Robert"));

        System.out.println(names.indexOf("Jane"));

        System.out.println(names.subList(1, 4));

        names.clear();
        System.out.println(names.isEmpty());
        System.out.println(names);
    }
}

```

我们展示了可以用于`ArrayList`的另外五种方法。

```java
List<String> names = new ArrayList<>();

```

从 Java 7 开始，可以在对通用类的构造器调用中省略显式类型参数。 编译器为通用类的构造器推断参数类型。

```java
names.add("Jane");
names.add(0, "Thomas");

```

`add()`方法将新项目添加到容器。 重载的第二个选项指定将放置项目的索引。 最后，“ Thomas”字符串位于“ Jane”字符串之前。

```java
System.out.println(names.isEmpty());

```

`empty()`方法检查容器是否为空。 该行返回`false`。 目前，容器中有五个字符串。

```java
System.out.println(names.contains("Jane"));

```

`contains()`方法确定容器中是否存在指定的元素。

```java
System.out.println(names.indexOf("Jane"));

```

`indexOf()`方法返回指定元素首次出现的索引，如果列表不包含该元素，则返回 -1。

```java
System.out.println(names.subList(1, 4));

```

`subList()`方法返回指定索引之间的列表切片。 切片中包括第一个索引处的元素，不包括第二个索引处的元素。

```java
names.clear();

```

`clear()`方法从容器中删除所有元素。

```java
$ java com.zetcode.ArrayListMethodsEx2 
[Thomas, Robin, Jane, David, Becky]
false
true
false
2
[Robin, Jane, David]
true
[]

```

这是`com.zetcode.ArrayListMethodsEx2`的输出。

我们可以将其他列表添加到列表中。

`ListOfLists.java`

```java
package com.zetcode;

import java.util.ArrayList;
import java.util.List;

public class ListOfLists {

    public static void main(String[] args) {

        List<Integer> l1 = new ArrayList<>();
        l1.add(1);
        l1.add(2);
        l1.add(3);

        List<Integer> l2 = new ArrayList<>();
        l2.add(4);
        l2.add(5);
        l2.add(6);

        List<Integer> l3 = new ArrayList<>();
        l3.add(7);
        l3.add(8);
        l3.add(9);

        List<List<Integer>> nums = new ArrayList<>();
        nums.add(l1);
        nums.add(l2);
        nums.add(l3);

        System.out.println(nums);

        for (List<Integer> list : nums) {

            for (Integer n : list) {

                System.out.printf("%d ", n);
            }

            System.out.println();
        }
    }
}

```

该示例创建三个整数列表。 以后，这些列表将添加到另一个第四列表中。

```java
List<Integer> l1 = new ArrayList<>();
l1.add(1);
l1.add(2);
l1.add(3);

```

将创建一个整数列表。

```java
List<List> nums = new ArrayList<>();
nums.add(l1);
nums.add(l2);
nums.add(l3);

```

创建列表列表。

```java
for (List<Integer> list : nums) {

    for (Integer n : list) {

        System.out.printf("%d ", n);
    }

    System.out.println();
} 	

```

我们使用两个 for 循环遍历所有元素。

```java
$ java com.zetcode.ListOfListsEx 
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
1 2 3 
4 5 6 
7 8 9 

```

这是`com.zetcode.ListOfListsEx`的输出。

## Java 遍历列表

在下一节中，我们将展示如何遍历 Java 中的列表。

`TraversingList.java`

```java
package com.zetcode;

import java.util.ArrayList;
import java.util.List;

public class TraversingList {

    public static void main(String[] args) {

        List<String> martialArts = new ArrayList<>();
        martialArts.add("Silat");
        martialArts.add("Wing chun");
        martialArts.add("Karate");
        martialArts.add("Judo");
        martialArts.add("Aikido");

        for (int i=0; i < martialArts.size(); i++) {

            System.out.printf("%s ", martialArts.get(i));
        }        

        System.out.print("\n");

        for (String e: martialArts) {

            System.out.printf("%s ", e);
        }

        System.out.print("\n");

        martialArts.forEach((e) -> System.out.printf("%s ", e));

        System.out.print("\n");
    }
}

```

我们有一个字符串列表； 我们展示了三种遍历 Java 列表的方法。

```java
for (int i=0; i < martialArts.size(); i++) {

    System.out.printf("%s ", martialArts.get(i));
}  

```

使用传统的`for`循环遍历列表。

```java
for (String e: martialArts) {

    System.out.printf("%s ", e);
}

```

在这里，列表通过`for-each`循环进行遍历。

```java
martialArts.forEach((e) -> System.out.printf("%s ", e));

```

第三种方法使用`forEach`方法和 lambda 表达式。

```java
$ java com.zetcode.TraversingList
Silat Wing chun Karate Judo Aikido 
Silat Wing chun Karate Judo Aikido 
Silat Wing chun Karate Judo Aikido 

```

这是输出。

## Java `LinkedList`

`LinkedList`是 Java 中的双链表。 元素的插入和移除需要固定的时间。 链接列表提供对元素的顺序访问，这意味着抓取元素需要花费线性时间。 由于链接列表需要额外的存储空间以供参考，因此对于诸如字符之类的小型数据项列表来说，它们是不切实际的。

将`ArrayList`与`LinkedList`进行比较时，`ArrayList`用于访问特定元素的速度很快，但是添加到任一端的速度可能很慢，尤其是在中间删除时，速度特别慢。 `LinkedList`快速添加和删除元素，但是访问特定元素却很慢。

`LinkedListEx.java`

```java
package com.zetcode;

import java.util.LinkedList;

public class LinkedListEx {

    public static void main(String[] args) {

        LinkedList<Integer> nums = new LinkedList<>();

        nums.add(5);
        nums.add(10);
        nums.add(13);
        nums.add(12);
        nums.add(15);
        nums.add(23);

        System.out.println(nums);

        nums.removeFirst();
        nums.removeLast();
        nums.addFirst(17);
        nums.addLast(77);

        System.out.println(nums);            
    }
}

```

这是一个`LinkedList`示例，其中包含一些方法。

```java
LinkedList<Integer> nums = new LinkedList<>();

```

该`LinkedList`具有整数。

```java
nums.add(5);
nums.add(10);

```

我们将数字添加到列表中。 自动装箱将原始`int`类型包装到`Integer`对象。

```java
nums.removeFirst();
nums.removeLast();

```

这两种方法从容器中删除第一个和最后一个元素。

```java
nums.addFirst(17);
nums.addLast(77);

```

我们在列表的开头和结尾添加一个元素。

```java
$ java com.zetcode.LinkedListEx
[5, 10, 13, 12, 15, 23]
[17, 10, 13, 12, 15, 77]

```

链接列表中包含的元素将两次打印到控制台。

## Java `HashMap`

`HashMap`是一个存储键/值对的容器。 每个键与一个值关联。 键必须是唯一的。 这种容器类型在其他编程语言中称为关联数组或字典。 `HashMap`占用更多内存，因为对于每个值还有一个键。

删除和插入操作需要固定的时间。 `HashMap`可以存储空值。

`HashMapEx.java`

```java
package com.zetcode;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class HashMapEx {

    public static void main(String[] args) {

        Map<String, String> domains = new HashMap<>();

        domains.put("de", "Germany");
        domains.put("sk", "Slovakia");
        domains.put("us", "United States");
        domains.put("ru", "Russia");
        domains.put("hu", "Hungary");
        domains.put("pl", "Poland");    

        System.out.println(domains.get("pl"));

        for (String item : domains.values()) {

            System.out.println(item);
        }        

        Set keys = domains.keySet();

        System.out.println(keys);
    }
}

```

我们有一个`HashMap`，将域名映射到其国家名称。

```java
Map<String, String> domains = new HashMap<>();

```

我们用字符串键和值创建一个`HashMap`。

```java
domains.put("de", "Germany");
domains.put("sk", "Slovakia");
domains.put("us", "United States");
...

```

我们将一些数据放入`HashMap`。 第一个字符串是键。 第二是价值。

```java
System.out.println(domains.get("pl"));

```

我们通过其键检索特定值。 对于检索操作，我们使用`get`方法。

```java
for (String item : domains.values()) {

    System.out.println(item);
}    

```

`values()`方法返回域`HashMap`中包含的值的集合。 我们使用`for`循环遍历这些值，并将它们打印到控制台。

```java
Set keys = domains.keySet();

```

`keySet()`方法返回`Set`集合中`HashMap`的键。 `Set`是唯一元素的集合。

```java
System.out.println(keys);

```

该集合的元素将打印到控制台。

```java
$ java com.zetcode.HashMapEx
Poland
Germany
Slovakia
Hungary
Poland
United States
Russia
[de, sk, hu, pl, us, ru]

```

这是示例的输出。

在下一个示例中，我们创建一个自定义颜色对象的映射。

`HashMapEx2.java`

```java
package com.zetcode;

import java.util.HashMap;
import java.util.Map;

class Colour {

    private String name;
    private String code;

    public Colour(String name, String code) {
        this.name = name;
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }
}

public class HashMapEx2 {

    public static void main(String[] args) {

        Map<Integer, Colour> cols = new HashMap<>();

        cols.put(1, new Colour("AliceBlue", "#f0f8ff"));
        cols.put(2, new Colour("GreenYellow", "#adff2f"));
        cols.put(3, new Colour("IndianRed", "#cd5c5c"));
        cols.put(4, new Colour("khaki", "#f0e68c"));

        System.out.printf("The size of the map is %d%n", cols.size());

        int key = 4;

        if (cols.containsKey(key)) {

            System.out.printf("The map contains key %d%n", key);
        }

        cols.remove(1);

        System.out.printf("The size of the map is %d%n", cols.size());

        cols.replace(3, new Colour("VioletRed", "#d02090"));

        Colour col = cols.get(3);

        System.out.printf("Colour name:%s colour code:%s %n", 
                col.getName(), col.getCode());
    }
}

```

在此示例中，我们提出以下三种方法：`containsKey()`，`remove()`和`replace()`。

```java
class Colour {

    private String name;
    private String code;

    public Colour(String name, String code) {
        this.name = name;
        this.code = code;
    }
...
}

```

自定义颜色对象包含颜色名称和颜色代码属性。

```java
Map<Integer, Colour> cols = new HashMap<>();

```

创建一个映射，其中键是整数，值是`Colour`对象。

```java
if (cols.containsKey(key)) {

    System.out.printf("The map contains key %d%n", key);
}

```

`containsKey()`方法确定键是否在映射中。

```java
cols.remove(1);

```

`remove()`方法从映射中删除具有指定键的对象。

```java
cols.replace(3, new Colour("VioletRed", "#d02090"));

```

`replace()`方法替换指定键的条目。

```java
$ java com.zetcode.HashMapEx2
The size of the map is 4
The map contains key 4
The size of the map is 3
Colour name:VioletRed colour code:#d02090 

```

This is the output of the example.

## 单词计数

在下面的示例中，我们计算文本文件中单词的出现次数。 我们使用`HashMap`存储单词及其出现。

`thermopylae.txt`

```java
The Battle of Thermopylae was fought between an alliance of Greek city-states, 
led by King Leonidas of Sparta, and the Persian Empire of Xerxes I over the 
course of three days, during the second Persian invasion of Greece.
It took place simultaneously with the naval battle at Artemisium, in August 
or September 480 BC, at the narrow coastal pass of Thermopylae.
The Persian invasion was a delayed response to the defeat of the first Persian 
invasion of Greece, which had been ended by the Athenian victory at the Battle 
of Marathon in 490 BC. Xerxes had amassed a huge army and navy, and set out to 
conquer all of Greece.

```

我们从`thermopylae.txt`文件中读取内容。 该文件位于`src/resources/`目录中。

`CountingWordsEx.java`

```java
package com.zetcode;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CountingWordsEx {

    public static void main(String[] args) throws IOException {

        Map<String, Integer> wordCount = new HashMap<>();

        String fileName = "src/resources/thermopylae.txt";

        List<String> lines = Files.readAllLines(Paths.get(fileName),
                StandardCharsets.UTF_8);

        for (String line : lines) {

            String[] words = line.split("\\s+");

            for (String word : words) {

                if (word.endsWith(".") || word.endsWith(",")) {
                    word = word.substring(0, word.length()-1);
                }

                if (wordCount.containsKey(word)) {
                    wordCount.put(word, wordCount.get(word) + 1);

                } else {
                    wordCount.put(word, 1);
                }
            }
        }

        for (String key : wordCount.keySet()) {
            System.out.println(key + ": " + wordCount.get(key));
        }
    }
}

```

该示例从文件中读取文本，将句子拆分为单词并计算其在文本中的出现频率。

```java
Map<String, Integer> wordCount = new HashMap<>();

```

`wordCount`是一个映射，其中键是单词，频率是整数。

```java
String fileName = "src/resources/thermopylae.txt";

List<String> lines = Files.readAllLines(Paths.get(fileName),
        StandardCharsets.UTF_8);

```

我们使用`Files.readAllLines()`方法一次读取所有内容。

```java
for (String line : lines) {

    String[] words = line.split("\\s+");
...    

```

我们遍历这些行，将它们分成单词； 单词之间用空格隔开。

```java
if (word.endsWith(".") || word.endsWith(",")) {
    word = word.substring(0, word.length()-1);
}

```

我们删除尾随点和逗号。

```java
if (wordCount.containsKey(word)) {
    wordCount.put(word, wordCount.get(word) + 1);

} else {
    wordCount.put(word, 1);
}

```

如果单词已经在映射中，则增加其频率； 否则，我们将其插入映射并将其频率设置为 1。

```java
for (String key : wordCount.keySet()) {
    System.out.println(key + ": " + wordCount.get(key));
}

```

我们遍历映射并打印其键/值对。

```java
$ java com.zetcode.CountingWordsEx 
been: 1
Athenian: 1
alliance: 1
navy: 1
fought: 1
led: 1
delayed: 1
had: 2
during: 1
three: 1
second: 1
Greece: 3
Leonidas: 1
...

```

这是示例的部分输出。

## Java `TreeMap`

`TreeMap`是根据其键的自然顺序排序的映射。 尽管`HashMap`更省时，但`TreeMap`更节省空间。

`TreeMapEx.java`

```java
package com.zetcode;

import java.util.TreeMap;

public class TreeMapEx {

    public static void main(String[] args) {

        TreeMap<String, String> domains = new TreeMap<>();

        domains.put("de", "Germany");
        domains.put("sk", "Slovakia");
        domains.put("us", "United States");
        domains.put("ru", "Russia");
        domains.put("hu", "Hungary");
        domains.put("pl", "Poland");    

        System.out.println(domains);        
        System.out.println(domains.descendingMap());
    }
}

```

在示例中，我们创建一个`TreeMap`并将域名及其国家/地区放入其中。

```java
TreeMap<String, String> domains = new TreeMap<>();

```

创建了`TreeMap`。

```java
System.out.println(domains);        

```

这将按键/值的自然排序顺序（升序）打印。

```java
System.out.println(domains.descendingMap());

```

`descendingMap()`方法返回此映射中包含的映射的逆序视图。

```java
$ java com.zetcode.TreeMapEx 
{de=Germany, hu=Hungary, pl=Poland, ru=Russia, sk=Slovakia, us=United States}
{us=United States, sk=Slovakia, ru=Russia, pl=Poland, hu=Hungary, de=Germany}

```

`com.zetcode.TreeMapEx`程序按升序和降序打印键值。

## Java `HashSet`

`HashSet`是一个不包含重复元素的集合。 此类为基本操作（添加，删除，包含和调整大小）提供恒定的时间性能。 `HashSet`不提供元素的排序。

`HashSetEx.java`

```java
package com.zetcode;

import java.util.HashSet;
import java.util.Set;

public class HashSetEx {

    public static void main(String[] args) {

        Set<String> brands = new HashSet<>();

        brands.add("Pepsi");
        brands.add("Amazon");
        brands.add("Volvo");
        brands.add("IBM");
        brands.add("IBM");

        System.out.println(brands);   

        System.out.println(brands.isEmpty());
        System.out.println(brands.contains("Volvo"));
        brands.remove("Volvo");
        System.out.println(brands.contains("Volvo"));

        brands.clear();
        System.out.println(brands);   
    }
}

```

名称下只能注册一个品牌。 因此，品牌名称是`HashSet`的一个很好的例子。

```java
Set<String> brands = new HashSet<>();

brands.add("Pepsi");
brands.add("Amazon");
brands.add("Volvo");
brands.add("IBM");
brands.add("IBM");

```

我们创建一个`HashSet`并添加新元素。 IBM 品牌被添加两次。 但是，IBM 在容器中仅存在一次。

```java
System.out.println(brands);   

```

一键打印所有元素。

```java
System.out.println(brands.isEmpty());

```

`isEmpty()`方法检查容器是否为空。

```java
System.out.println(brands.contains("Volvo"));

```

使用`contains()`方法，我们检查品牌容器中是否存在沃尔沃品牌。 该行打印`true`。

```java
brands.remove("Volvo");
System.out.println(brands.contains("Volvo"));

```

我们从品牌容器中删除了沃尔沃品牌。 第二行打印`false`。

```java
brands.clear();

```

`clear()`方法从集合中删除所有元素。

```java
$ java com.zetcode.HashSetEx
[IBM, Pepsi, Volvo, Amazon]
false
true
false
[]

```

这是`com.zetcode.HashSetEx`程序的输出。

## Java `TreeSet`

`TreeSet`是具有使用自然顺序排序的元素的集合。 `TreeSet`比`HashSet`慢。 `HashSet`可以包含空值，而`TreeSet`不能包含空值。

`TreeSetEx.java`

```java
package com.zetcode;

import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;

public class TreeSetEx {

    public static void main(String[] args) {

        List<String> brands = new ArrayList<>();

        brands.add("Pepsi");
        brands.add("Amazon");
        brands.add("Volvo");
        brands.add("IBM");    
        brands.add("HP");
        brands.add("Apple");
        brands.add("Starbucks");

        TreeSet<String> brands2 = new TreeSet<>();
        brands2.addAll(brands);

        System.out.println(brands2);        
        System.out.println(brands2.descendingSet());

        System.out.println(brands2.first());
        System.out.println(brands2.last());

        System.out.println(brands2.headSet("IBM", true));
        System.out.println(brands2.tailSet("IBM", false));
        System.out.println(brands2.subSet("Apple", true, "Starbucks", true));        
    }
}

```

在此示例中，我们使用`TreeSet`。

```java
List<String> brands = new ArrayList<>();

brands.add("Pepsi");
brands.add("Amazon");
brands.add("Volvo");
brands.add("IBM");    
brands.add("HP");
brands.add("Apple");
brands.add("Starbucks");

```

创建了各种品牌的`ArrayList`。

```java
TreeSet<String> brands2 = new TreeSet<>();
brands2.addAll(brands);

```

借助`addAll()`方法，从`ArrayList`容器中创建了一个新的`TreeSet`。

```java
System.out.println(brands2);        
System.out.println(brands2.descendingSet());

```

容器的元素按升序和降序打印到控制台。

```java
System.out.println(brands2.first());
System.out.println(brands2.last());

```

我们打印容器的第一个和最后一个元素。

```java
System.out.println(brands2.headSet("IBM", true));

```

`headSet()`方法返回其元素小于指定元素的集合的一个切片。 第二个参数控制是否包含指定的元素。

```java
System.out.println(brands2.tailSet("IBM", false));

```

`tailSet()`方法返回其元素大于指定元素的集合的一个切片。

```java
System.out.println(brands2.subSet("Apple", true, "Starbucks", true));  

```

`subSet()`方法返回容器的一部分，其元素范围从第一个指定元素到第二个指定元素。

```java
$ java com.zetcode.TreeSetEx
[Amazon, Apple, HP, IBM, Pepsi, Starbucks, Volvo]
[Volvo, Starbucks, Pepsi, IBM, HP, Apple, Amazon]
Amazon
Volvo
[Amazon, Apple, HP, IBM]
[Pepsi, Starbucks, Volvo]
[Apple, HP, IBM, Pepsi, Starbucks]

```

这是`com.zetcode.TreeSetEx`示例的输出。

## Java `Collections`类

`Collections`是一个工具类，提供了许多使用容器的有用方法。 它仅由静态方法组成。 某些方法不适用于所有集合类型。 例如，不可能在`HashSet`上使用`sort()`方法，因为此容器不支持有序元素。

`CollectionsEx.java`

```java
package com.zetcode;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class CollectionsEx {

    public static void main(String[] args) {

        Integer[] nums = { 4, 3, 2, 4, 5, 6, 4, 2, 7, 8, 9, 0, 1 };

        List<Integer> ns = new ArrayList<>(Arrays.asList(nums));
        System.out.println("Default order:");
        System.out.println(ns);

        System.out.println("Ascending order:");
        Collections.sort(ns);
        System.out.println(ns);

        System.out.println("Descending order:");
        Collections.reverse(ns);
        System.out.println(ns);

        System.out.println("Swapping the first and the last elements:");
        Collections.swap(ns, 0, ns.size()-1);
        System.out.println(ns);

        System.out.println("Replacing all 4s with 0s:");
        Collections.replaceAll(ns, 4, 0);
        System.out.println(ns);

        System.out.println("Random order:");
        Collections.shuffle(ns);
        System.out.println(ns);

        System.out.println(Collections.max(ns));
        System.out.println(Collections.min(ns));
    }
}

```

在示例中，我们使用`Collections`类的几种方法。

```java
Integer[] nums = { 4, 3, 2, 4, 5, 6, 4, 2, 7, 8, 9, 0, 1 };

ArrayList<Integer> ns = new ArrayList<>(Arrays.asList(nums));

```

`ArrayList`由整数数组创建。 `Arrays`类的`asList()`方法用于将数组转换为列表，然后将其传递给构造器。

```java
Collections.sort(ns); 

```

`sort()`方法以升序对元素进行排序。

```java
Collections.reverse(ns);

```

`reverse()`方法反转列表中元素的顺序。

```java
Collections.swap(ns, 0, ns.size()-1);

```

`swap()`方法交换两个元素。 在我们的案例中，第一个元素与最后一个元素。

```java
Collections.replaceAll(ns, 4, 0);

```

该行用 0 替换所有出现的数字 4。

```java
Collections.shuffle(ns);

```

`shuffle()`方法随机重新排序容器中的元素。

```java
System.out.println(Collections.max(ns));
System.out.println(Collections.min(ns));

```

在这里，我们打印列表的最大值和最小值。

```java
$ java com.zetcode.CollectionsEx 
Default order:
[4, 3, 2, 4, 5, 6, 4, 2, 7, 8, 9, 0, 1]
Ascending order:
[0, 1, 2, 2, 3, 4, 4, 4, 5, 6, 7, 8, 9]
Descending order:
[9, 8, 7, 6, 5, 4, 4, 4, 3, 2, 2, 1, 0]
Swapping the first and the last elements:
[0, 8, 7, 6, 5, 4, 4, 4, 3, 2, 2, 1, 9]
Replacing all 4s with 0s:
[0, 8, 7, 6, 5, 0, 0, 0, 3, 2, 2, 1, 9]
Random order:
[1, 6, 2, 8, 0, 2, 0, 9, 5, 0, 7, 3, 0]
9
0

```

这是`com.zetcode.CollectionsEx`程序的示例输出。

Java 教程的这一部分专门用于 Java 集合。