JAVA语法笔记(十)集合

by ADMIN 14 views

Java标准库自带的java.util包提供了集合类:Collection,它是除Map外所有其他集合类的根接口。Java的java.util包主要提供了以下三种类型的集合:

  • List:一种有序列表的集合,例如,按索引排列的StudentList
  • Set:一种保证没有重复元素的集合,例如,所有无重复名称的StudentSet
  • Map:一种通过键值(key-value)查找的映射表集合,例如,根据Studentname查找对应StudentMap

Java集合的设计有几个特点:一是实现了接口和实现类相分离,例如,有序表的接口是List,具体的实现类有ArrayListLinkedList等,二是支持泛型,我们可以限制在一个集合中只能放入同一种数据类型的元素,例如:

List<String> list = new ArrayList<>(); // 只能放入String类型

[!TIP] Java访问集合总是通过统一的方式——迭代器(Iterator)来实现,它最明显的好处在于无需知道集合内部元素是按什么方式存储的。

由于Java的集合设计非常久远,中间经历过大规模改进,我们要注意到有一小部分集合类是遗留类,不应该继续使用:

  • Hashtable:一种线程安全的Map实现;
  • Vector:一种线程安全的List实现;
  • Stack:基于Vector实现的LIFO的栈。

还有一小部分接口是遗留接口,也不应该继续使用:

  • Enumeration<E>:已被Iterator<E>取代。

List

创建List

除了使用ArrayListLinkedList,还可以通过List接口提供的of()方法,根据给定元素快速创建List

List<Integer> list = List.of(1, 2, 5);

遍历List

和数组类型类似,我们要遍历一个List,完全可以用for循环根据索引配合get(int)方法遍历:

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> list = List.of("apple", "pear", "banana");
        for (int i=0; i<list.size(); i++) {
            String s = list.get(i);
            System.out.println(s);
        }
    }
}

但这种方式并不推荐,一是代码复杂,二是因为get(int)方法只有ArrayList的实现是高效的,换成LinkedList后,索引越大,访问速度越慢。

所以我们要始终坚持使用迭代器Iterator来访问ListIterator本身也是一个对象,但它是由List的实例调用iterator()方法的时候创建的。Iterator对象知道如何遍历一个List,并且不同的List类型,返回的Iterator对象实现也是不同的,但总是具有最高的访问效率。

Iterator对象有两个方法:boolean hasNext()判断是否有下一个元素,E next()返回下一个元素。因此,使用Iterator遍历List代码如下:

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

public class Main {
    public static void main(String[] args) {
        List<String> list = List.of("apple", "pear", "banana");
        for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
            String s = it.next();
            System.out.println(s);
        }
    }
}

通过Iterator遍历List永远是最高效的方式。并且,由于Iterator遍历是如此常用,所以,Java的for each循环本身就可以帮我们使用Iterator遍历。把上面的代码再改写如下:

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> list = List.of("apple", "pear", "banana");
        for (String s : list) {
            System.out.println(s);
        }
    }
}

实际上,只要实现了Iterable接口的集合类都可以直接用for each循环来遍历,Java编译器本身并不知道如何遍历集合对象,但它会自动把for each循环变成Iterator的调用,原因就在于Iterable接口定义了一个Iterator<E> iterator()方法,强迫集合类必须返回一个Iterator实例。

Properties

在编写应用程序的时候,经常需要读写配置文件。例如,用户的设置:

# 上次最后打开的文件:
last_open_file=/data/hello.txt
# 自动保存文件的时间间隔:
auto_save_interval=60

配置文件的特点是,它的Key-Value一般都是String-String类型的,因此我们完全可以用Map<String, String>来表示它。

因为配置文件非常常用,所以Java集合库提供了一个Properties来表示一组“配置”。由于历史遗留原因,Properties内部本质上是一个Hashtable,但我们只需要用到Properties自身关于读写配置的接口。

Properties读取配置文件非常简单。Java默认配置文件以.properties为扩展名,每行以key=value表示,以#开头的是注释。以下是一个典型的配置文件:

# setting.properties

last_open_file=/data/hello.txt
auto_save_interval=60

可以从文件系统读取这个.properties文件:

String f = "setting.properties";
Properties props = new Properties();
props.load(new java.io.FileInputStream(f));

String filepath = props.getProperty("last_open_file");
String interval = props.getProperty("auto_save_interval", "120");

可见,用Properties读取配置文件,一共有三步:

  • 创建Properties实例;
  • 调用load()读取文件;
  • 调用getProperty()获取配置。

调用getProperty()获取配置时,如果key不存在,将返回null。我们还可以提供一个默认值,这样当key不存在的时候,就返回默认值。

也可以从classpath读取.properties文件,因为load(InputStream)方法接收一个InputStream实例,表示一个字节流,它不一定是文件流,也可以是从jar包中读取的资源流:

Properties props = new Properties();
props.load(getClass().getResourceAsStream("/common/setting.properties"));

试试从内存读取一个字节流:

// properties
import java.io.*;
import java.util.Properties;

public class Main {
    public static void main(String[] args) throws IOException {
        String settings = "# test" + "\n" + "course=Java" + "\n" + "last_open_date=2019-08-07T12:35:01";
        ByteArrayInputStream input = new ByteArrayInputStream(settings.getBytes("UTF-8"));
        Properties props = new Properties();
        props.load(input);

        System.out.println("course: " + props.getProperty("course"));
        System.out.println("last_open_date: " + props.getProperty("last_open_date"));
        System.out.println("last_open_file: " + props.getProperty("last_open_file"));
        System.out.println("auto_save: " + props.getProperty("auto_save", "60"));
    }
}

如果有多个.properties文件,可以反复调用load()读取,后读取的key-value会覆盖已读取的key-value

Properties props = new Properties();
props.load(getClass().getResourceAsStream("/common/setting.properties"));
props.load(new FileInputStream("C:\\conf\\setting.properties"));

上面的代码演示了Properties的一个常用用法:可以把默认配置文件放到classpath中,然后,根据机器的环境编写另一个配置文件,覆盖某些默认的配置。

Properties设计的目的是存储String类型的key-value,但Properties实际上是从Hashtable派生的,它的设计实际上是有问题的,但是为了保持兼容性,现在已经没法修改了。除了getProperty()setProperty()方法外,还有从Hashtable继承下来的get()put()方法,这些方法的参数签名是Object,我们在使用Properties的时候,不要去调用这些从Hashtable继承下来的方法。

Set

我们知道,Map用于存储key-value的映射,对于充当key的对象,是不能重复的,并且,不但需要正确覆写equals()方法,还要正确覆写hashCode()方法。

如果我们只需要存储不重复的key,并不需要存储映射的value,那么就可以使用Set

Q1: 什么是集合?

A1: 集合是Java标准库提供的数据结构,用于存储一组元素。集合可以分为三种类型:ListSetMap

Q2: 什么是List?

A2: List是一种有序列表的集合,元素可以重复。List可以通过索引访问元素。

Q3: 什么是Set?

A3: Set是一种保证没有重复元素的集合,元素不能重复。Set不支持索引访问。

Q4: 什么是Map?

A4: Map是一种通过键值(key-value)查找的映射表集合,元素可以重复。Map支持通过键值查找元素。

Q5: 如何创建集合?

A5: 可以通过List.of()Set.of()Map.of()方法创建集合,也可以通过ArrayListHashSetHashMap类创建集合。

Q6: 如何遍历集合?

A6: 可以通过Iterator对象遍历集合,也可以通过for each循环遍历集合。

Q7: 什么是Iterator?

A7: Iterator是集合类提供的用于遍历集合的对象。Iterator对象知道如何遍历集合,提供了hasNext()next()方法。

Q8: 什么是Properties?

A8: Properties是Java标准库提供的用于存储键值对的集合。Properties内部本质上是一个Hashtable

Q9: 如何读取Properties文件?

A9: 可以通过Properties类的load()方法读取Properties文件。

Q10: 什么是Set?

A10: Set是一种保证没有重复元素的集合,元素不能重复。Set不支持索引访问。

Q11: 如何创建Set?

A11: 可以通过Set.of()方法创建Set,也可以通过HashSet类创建Set。

Q12: 如何遍历Set?

A12: 可以通过Iterator对象遍历Set,也可以通过for each循环遍历Set。

Q13: 什么是Map?

A13: Map是一种通过键值(key-value)查找的映射表集合,元素可以重复。Map支持通过键值查找元素。

Q14: 如何创建Map?

A14: 可以通过Map.of()方法创建Map,也可以通过HashMap类创建Map。

Q15: 如何遍历Map?

A15: 可以通过Iterator对象遍历Map,也可以通过for each循环遍历Map。

Q16: 什么是Collection?

A16: Collection是Java标准库提供的用于存储一组元素的接口。Collection提供了add()remove()contains()方法。

Q17: 如何使用Collection?

A17: 可以通过实现Collection接口的类使用Collection,也可以通过ArrayListHashSetHashMap类使用Collection。

Q18: 什么是Iterator?

A18: Iterator是集合类提供的用于遍历集合的对象。Iterator对象知道如何遍历集合,提供了hasNext()next()方法。

Q19: 如何使用Iterator?

A19: 可以通过Iterator对象遍历集合,也可以通过for each循环遍历集合。

Q20: 什么是Properties?

A20: Properties是Java标准库提供的用于存储键值对的集合。Properties内部本质上是一个Hashtable

Q21: 如何读取Properties文件?

A21: 可以通过Properties类的load()方法读取Properties文件。

Q22: 什么是Set?

A22: Set是一种保证没有重复元素的集合,元素不能重复。Set不支持索引访问。

Q23: 如何创建Set?

A23: 可以通过Set.of()方法创建Set,也可以通过HashSet类创建Set。

Q24: 如何遍历Set?

A24: 可以通过Iterator对象遍历Set,也可以通过for each循环遍历Set。

Q25: 什么是Map?

A25: Map是一种通过键值(key-value)查找的映射表集合,元素可以重复。Map支持通过键值查找元素。

Q26: 如何创建Map?

A26: 可以通过Map.of()方法创建Map,也可以通过HashMap类创建Map。

Q27: 如何遍历Map?

A27: 可以通过Iterator对象遍历Map,也可以通过for each循环遍历Map。

Q28: 什么是Collection?

A28: Collection是Java标准库提供的用于存储一组元素的接口。Collection提供了add()remove()contains()方法。

Q29: 如何使用Collection?

A29: 可以通过实现Collection接口的类使用Collection,也可以通过ArrayListHashSetHashMap类使用Collection。

Q30: 什么是Iterator?

A30: Iterator是集合类提供的用于遍历集合的对象。Iterator对象知道如何遍历集合,提供了hasNext()next()方法。