常见的各个容器的继承关系:

屏幕截图 2021-03-01 103049.jpg

Iterable 接口

Iterable是一个超级接口,被Collection所继承。它只有一个方法:Iterator<T> iterator(),返回一个迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String args[]) {		
List<String> l = new ArrayList<String>();
l.add("aa");
l.add("bb");
Iterator iter = l.iterator();
while(iter.hasNext()){
System.out.println((String)iter.next());
}
// for循环的版本
// for(Iterator<String> iter=l.iterator();iter.hasNext();){
// String str = (String)iter.next();
// System.out.println(str);
// }
}

Collection 接口

JDK 不提供此接口的任何直接实现:它提供更具体的子接口(如 Set , List , Map)实现。不同的Collection子类对于有序性、重复性、null、线程同步都有不同的策略。

List 接口

List是有序的 collection(也称为序列)。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。与Set不同,List允许插入重复的值。

List 接口提供了特殊的迭代器,称为 ListIterator,该迭代器还允许元素插入和替换以及双向访问。

排序

List接口的排序可以通过Collections.sort()来进行定制排序。只需要继承Comparable接口后,重写compareTo()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Student extends Thread implements Comparable {	   
...
public static void main(String args[]) {
List<Student> list = new ArrayList<Student>();
list.add(stu3);
...
Collections.sort(list);
}
@Override
public int compareTo(Object o) {
//使用当前对象的年龄和其他对象的年龄比较,如果<0返回负数,>0返回正数,=0返回0
int z = this.age - ((Student)o).getAge();
if(z<0)
return -1;
else if(z == 0)
return 0;
else
return 1;
}
}

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
2
3
4
5
6
7
8
idCount.merge(id, 1, Integer::sum);
// <--- 等价于 --->
Integer prev = idCount.get(id);
if (prev == null) {
idCount.put(id, 1);
} else {
idCount.put(id, prev + 1);
}

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
2
3
4
5
Map<Person, Integer> map = new TreeMap<>(new Comparator<Person>() {
public int compare(Person p1, Person p2) {
return p1.name.compareTo(p2.name);
}
});

Properties

Java默认配置文件以.properties为扩展名,每行以key=value表示

read

  1. 文件
1
2
3
4
# setting.properties

last_open_file=/data/hello.txt
auto_save_interval=60

想要读取上述.properties文件:

1
2
3
4
5
6
Properties props = new Properties();
props.load(new FileInputStream("setting.properties"));

String filepath = props.getProperty("last_open_file");
String interval = props.getProperty("auto_save_interval", "120");
// 第二个参数为默认值,key不存在的时候,就返回默认值。
  1. 从jar包中读取

也可以从classpath读取.properties文件

1
props.load(getClass().getResourceAsStream("/common/setting.properties"));

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

1
2
props.load(getClass().getResourceAsStream("/common/setting.properties"));
props.load(new FileInputStream("C:\\conf\\setting.properties"));
  1. 也可以从内存中读取

编码问题:

由于load(InputStream)默认总是以ASCII编码读取字节流,所以会导致读到乱码。我们需要用另一个重载方法load(Reader)读取:

1
2
Properties props = new Properties();
props.load(new FileReader("settings.properties", StandardCharsets.UTF_8));

就可以正常读取中文。InputStreamReader的区别是一个是字节流,一个是字符流。字符流在内存中已经以char类型表示了,不涉及编码问题。

write

1
2
3
props.setProperty("url", "http://www.liaoxuefeng.com");
props.setProperty("language", "Java");
props.store(new FileOutputStream("C:\\conf\\setting.properties"), "这是写入的properties注释");

Collections

提供了一系列静态方法,能更方便地操作各种集合。

  • 创建不可变集合;
1
List<String> immutable = Collections.unmodifiableList(mutable);

这种封装实际上是通过创建一个代理对象,拦截掉所有修改方法实现的。然而,继续对原始的可变List进行增删是可以的,并且,会直接影响到封装后的“不可变”List,所以最好扔掉可变List的引用:

1
mutable = null;
  • 排序/洗牌等操作。
1
2
Collections.sort(list);
Collections.shuffle(list);