「Java」容器
常见的各个容器的继承关系:
Iterable 接口
Iterable
是一个超级接口,被Collection所继承。它只有一个方法:Iterator<T> iterator()
,返回一个迭代器
1 | public static void main(String args[]) { |
Collection 接口
JDK 不提供此接口的任何直接实现:它提供更具体的子接口(如 Set , List , Map)实现。不同的Collection子类对于有序性、重复性、null、线程同步都有不同的策略。
List 接口
List是有序的 collection(也称为序列)。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。与Set不同,List允许插入重复的值。
List 接口提供了特殊的迭代器,称为 ListIterator
,该迭代器还允许元素插入和替换以及双向访问。
排序
List接口的排序可以通过Collections.sort()
来进行定制排序。只需要继承Comparable接口后,重写compareTo()
方法。
1 | public class Student extends Thread implements Comparable { |
1. ArrayList
- 基于数组实现的List类,它封装了一个动态的、增长的、允许再分配的
Object[ ]
数组,允许对元素进行快速随机访问 - 当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此它适合随机查找和遍历,不适合插入和删除。
2. LinkedList
- LinkedList是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。
- 实现了Deque接口,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。
3. Vector
- 与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,避免多线程同时写而引起的不一致性,但要慢很多,现在块被淘汰了。
Set 接口
集合,是无序、不可重复的。
Set判断两个对象相同不用"=="
而是根据equals
方法。在使用Set集合的时候,应该注意:
- 为Set集合里的元素的实现类实现一个有效的
equals(Object)
方法; - 对Set的构造函数,传入的Collection参数不能包含重复的元素。
1. HashSet
- 使用HASH算法来存储集合中的元素,集合判断两个元素相等的标准是两个对象通过
equals()
方法比较相等,并且两个对象的hashCode()
方法的返回值相等
1.1 LinkedHashSet
- 也是根据元素的hashCode值来决定元素的存储位置,但同时使用链表维护元素的次序,当遍历集合里的元素时,LinkedHashSet将会按元素的添加顺序来访问集合里的元素
2. SortedSet
此接口主要用于排序操作,实现了此接口的子类都属于排序的子类
2.1TreeSet
- TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态
3. EnumSet
- EnumSet是一个专门为枚举类设计的集合类,EnumSet中所有元素都必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet时显式、或隐式地指定。EnumSet的集合元素也是有序的,
Queue 接口
用于模拟队列
1. PriorityQueue
- 按照队列中某个属性的大小来排列的,因此称作优先队列。
2. Deque
- 双端队列
2.1 ArrayDeque
- 基于数组的双端队列。
2.2 LinkedList
- 如上
Map 接口
Map用于保存具有“映射关系”的数据。每个Entry都持有键-值
两个对象。其中,Value可能重复,但Key不可重复
操作:
merge
:将新的值赋值到key
(如果不存在)或更新给定的key
值对应的value
1 | idCount.merge(id, 1, Integer::sum); |
HashMap
通过对key计算hashCode()
,通过空间换时间的方式,直接定位到value所在的内部数组的索引,因此,查找效率非常高。
LinkedHashMap
LinkedHashMap也使用双向链表来维护key-value对的次序,该链表负责维护Map的迭代顺序,与key-value对的插入顺序一致(注意和TreeMap对所有的key-value进行排序区分)
EnumMap
key的对象是enum
类型,在内部以一个非常紧凑的数组存储value,并且根据enum
类型的key直接定位到内部数组的索引,并不需要计算hashCode()
,不但效率最高,而且没有额外的空间浪费。
1 | Map<DayOfWeek, String> map = new EnumMap<>(DayOfWeek.class); |
SortedMap
在内部对key进行排序,实现类是TreeMap
。
TreeMap
- 是一个红黑树结构,每个键值对都作为红黑树的一个节点。TreeMap存储键值对时,需要根据key对节点进行排序,TreeMap可以保证所有的
key-value
对处于有序状态。 - TreeMap也有两种排序方式:自然排序(放入的Key必须实现
Comparable
接口)、定制排序(指定一个自定义排序算法)
1 | Map<Person, Integer> map = new TreeMap<>(new Comparator<Person>() { |
Properties
Java默认配置文件以.properties
为扩展名,每行以key=value
表示
read
- 文件
1 | # setting.properties |
想要读取上述.properties
文件:
1 | Properties props = new Properties(); |
- 从jar包中读取
也可以从classpath读取.properties
文件
1 | props.load(getClass().getResourceAsStream("/common/setting.properties")); |
Properties
的一个常用用法:可以把默认配置文件放到classpath中,然后,根据机器的环境编写另一个配置文件,覆盖某些默认的配置。
1 | props.load(getClass().getResourceAsStream("/common/setting.properties")); |
- 也可以从内存中读取
编码问题:
由于load(InputStream)
默认总是以ASCII编码读取字节流,所以会导致读到乱码。我们需要用另一个重载方法load(Reader)
读取:
1 | Properties props = new Properties(); |
就可以正常读取中文。InputStream
和Reader
的区别是一个是字节流,一个是字符流。字符流在内存中已经以char
类型表示了,不涉及编码问题。
write
1 | props.setProperty("url", "http://www.liaoxuefeng.com"); |
Collections
提供了一系列静态方法,能更方便地操作各种集合。
- 创建不可变集合;
1 | List<String> immutable = Collections.unmodifiableList(mutable); |
这种封装实际上是通过创建一个代理对象,拦截掉所有修改方法实现的。然而,继续对原始的可变List
进行增删是可以的,并且,会直接影响到封装后的“不可变”List
,所以最好扔掉可变List
的引用:
1 | mutable = null; |
- 排序/洗牌等操作。
1 | Collections.sort(list); |