集合
一个Java对象可以在内部持有若干个其他Java对象,并对外提供访问接口,我们把这种Java对象称为集合。显然,数组也是一种集合
数组存在限制:
- 数组初始化后大小不可变
- 数组只能按索引顺序存取
因此需要各种不同类型的集合类来处理不同数据,例:
- 可变大小的顺序链表
- 保证无重复元素的集合
- ……
Collection
Java自带的java.util包提供了集合类:Collection,
它是除了Map外所有其他集合类的根接口。
主要提供了三种类型的集合:
- List:一种有序列表的集合
- Set:一种保证没有重复元素的集合
- Map:一种通过键值(key-value)查找的映射表集合
特点:
- 实现了接口和实现类相分离
- 支持泛型
Java访问集合总是通过统一的方式:迭代器,好处就是无需知道集合内部元素是按什么方式存储
List
list是一种有序列表,和数组几乎完全相同
ArrayList把添加和删除的操作封装起来,我们操作List类似于操作数组,不用关心内部元素如何移动。
主要接口方法:
- 在末尾添加一个元素:boolean add(E e)
- 在指定索引位置添加一个元素:boolean add(int index, E e)
- 删除指定索引的元素:E remove(int index)
- 删除某个元素:boolean remove(Object e)
- 获取指定索引的元素:E get(int index)
- 获取链表大小:int size()
特点:
- 允许元素重复
- 允许添加null
创建List
List<String> list = new ArrayList<>();//使用ArrayList
List<Integer> list = List.of(1, 2, 5);//使用List接口提供的of()方法,不接受null值
遍历List
for循环结合get(int)可行,但不推荐。
应使用迭代器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);
}
}
}
//由于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);
}
}
}
List 和 Array转换
- 第一种调用toArray()方法直接返回一个Object[]数组(不推荐,会丢失类型信息)
- 第二种调toArray(T[])传入一个类型相同的Array,List内部自动复制元素到Array中
编写equals()方法
List提供 boolean contains(Object o)方法判断List是否包含某个指定元素
此外,int indexOf(Object o)方法可以返回某个元素索引
注意:因为contains()方法是通过equals()进行判断是否相等。
所以,List放入的实例,必须正确重写equals()。
如何正确编写equals()?
- 自反性
- 对称性
- 传递性
- 一致性
- 对null比较,只返回false
1.先确定实例"相等"的逻辑
2.用instanceof判断传入的待比较的Object是不是当前类型
3.对引用类型用Objects.equals()比较,对基本类型用==比较
Map
key-value映射表的数据结构,不保证顺序
Map是一个接口,最常用实现类是HashMap。
Map中不存在重复的key,value可以重复
- 遍历key,可以使用for-each循环Map实例的keySet()方法返回Set集合,包含不重复的key集合 ,遍历key和value,可以使用entrySet()集合,包含每一个key-value映射
- 在Map内部,对key作比较是通过equals()方法,所以作为key的对象必须正确覆写equals()方法
- 如果不同的key映射的hashCode()相同,那么在HashMap的数组中会存储List,包含两个Entry。查找时,会找到这个List,然后遍历List,找到Entry,它的key字段为"a",然后返回对应实例
EnumMap
作为key的对象是enum类型,就可以使用EnumMap。
非常紧凑的数组存储value,并且根据enum类型的key直接定位到内部数组的索引,效率最高
TreeMap
内部会对Key进行排序,SortedMap是接口,实现类是TreeMap
SortedMap保证遍历时以Key的顺序进行排序。String 默认按字母排序。
使用TreeMap时,放入的Key必须实现Comparable接口,作为Value的对象无要求
如果作为Key的class没有实现Comparable接口,
那么必须在创建TreeMap时同时指定一个自定义排序算法。
Map<String,Integer> map = new TreeMap<>()
Properties
编写应用程序时,经常需要读写配置文件。例如,用户的设置:
# 上次最后打开的文件:
last_open_file = /data/hello.txt
# 自动保存文件的时间间隔
auto_save_interval = 60
配置文件的特点,key-value一般是String-String类型,
因此完全可以用Map<String,String>来表示
因为配置文件非常常用,所以Java提供Properties来表示一组配置
内部本质是Hashtable
读取配置文件
用Properties读取配置文件非常简单。默认配置文件以.properties为扩展名
典型配置文件:
# setting.properties
last_open_file=/data/hello.txt
auto_save_interval=60
可以从文件系统读取这个.properties文件
一共三步:
- 创建Properties实例;
- 调用load()读取文件
- 调用getProperty()获取配置。
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");
load(InputStream)方法接收一个InputStream实例,表示一个字节流,既可以是文件流,也可以是从jar包中读取的资源流
写入配置文件
通过setProperty()修改Properties实例,可以把配置写入文件,写入配置文件使用store()方法
Set
Map用于存储key-value的映射,对于充当key的对象,是不能重复的,并且不但需要正确覆写equals(),还要覆写hashCode()方法。
如果我们只需要存储不重复的key,并不需要存储映射的value,那就可以使用Set。
Set用于存储不重复的元素集合,主要方法:
- 将元素加入Set< E>: boolean add(E e)
- 将元素从Set
删除:boolean remove(Object e) - 判断是否包含元素:boolean contains(Object e)
最常用Set实现类是HashSet,实际上HashSet仅仅是对HashMap的一个简单包装
Set接口不保证有序,而SortedSet接口则保证元素有序
- HashSet是无序的,它实现了Set接口,并没有实现SortedSet接口
- TreeSet是有序的,它实现了SortedSet接口
Queue
队列,先进先出的有序表。它和List区别在于,List可以在任意位置添加和删除元素,而Queue只有两个操作:
- 把元素添加到队列末尾
- 从队列头部取出元素
方法:
- int size()
- boolean add(E) / boolean offer(E)
- E remove() / E poll()
- E element() / E peak()
LinkedList既实现了List接口,又实现了Queue接口。
尽量避免把null添加到队列。
PriorityQueue
优先队列,出队顺序与元素优先级有关。
调用remove()或poll()方法,返回的总是优先级最高的元素。
必须给每个元素定义优先级
因此放入PriorityQueue的元素必须实现Comparable接口。
Deque
双端队列,允许两头都进,两头都出。
Deque接口实际扩展自Queue。
所以Deque可以调用Queue的方法,offer()/poll()
但是推荐直接明确调用方法
Stack
栈,先进后出的数据结构,只有入栈和出栈的操作。
- push(E) ,压栈
- pop(); 弹出
- peek(); 取栈顶元素但不弹出
Java中用Deque实现Stack功能:
- 压栈:push(E) \ addFirst(E);
- 弹出:pop() \ removeFirst();
- 取栈顶元素但不弹出:peek() \ peekFirst().
作用:
- JVM在处理Java方法调用的时候就会通过栈这种数据结构维护方法调用的层次
Collections
是JDK提供的工具类,更方便操作各种集合
创建集合
直接使用 .of()方法
排序
排序会修改List元素的位置,因此必须传入可变List
洗牌
随机打乱List内部元素的顺序
不可变集合
可以将可变集合封装成不可变集合:.unmodifiableList(), ……
浙公网安备 33010602011771号