Collection

主要接口方法:

  • int size()
  • boolean inEmpty()
  • boolean contains(Object element)
  • boolean add(E element)
  • boolean remove(Object element)
  • Interator<E> iterator()
  • boolean containsAll(Collection<?> c)
  • boolean addAll(Collection<? extends E> c)
  • boolean removeAll(Collection<?> c)
  • boolean retainAll(Collection<?> c)
  • void clear()
  • Object[] toArray()
  • <T> T[] toArray(T[] a)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package org.example;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

public class CollectionDemo {
public static void main(String[] args) {
PrintStream out = System.out;

Collection<Integer> collection = new ArrayList<Integer>();
List<Integer> list = List.of(1,2,3,4,5,6,7,8,9,10);
collection.addAll(list);
out.println(collection);
out.println(collection.containsAll(list));
}
}

Collection类是除了Map外所有其他集合类的根接口。

Java的java.util包主要提供了以下三种类型的集合:

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

java集合的特点:

  • 接口和实现类相分离,如:有序表的接口是List,实现类是ArrayListLinkedList等。
  • 支持泛型

List

主要接口方法:

  • boolen add(E e):在末尾添加一个元素
  • boolen add(int index,E e):在指定索引添加一个元素
  • E remove(int index):删除指定索引的元素
  • boolean remove(Object e):删除某个元素
  • E get(int index):获取指定索引的元素
  • int size():获取链表大小(包含元素的个数)
  • List.of():返回一个不可变的list

Iterator类

使用Iterator类来遍历List

  • iterator():获取一个迭代器
  • next():返回迭代器的下一个元素,并将迭代器的指针移到下一个元素
  • hasNext():用于判断集合中是否还有下一个元素可以访问
  • remove():从集合中删除迭代器最后访问的元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package one;

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

public class collection {
public static void main(String[] ages){
try{
List<String> list=new ArrayList<>();
list.add("one");
list.add("two");
for(Iterator<String> it=list.iterator();it.hasNext();){
String item=it.next();
System.out.println(item);
}
}catch(IndexOutOfBoundsException e){
throw new ListNumAdd();
}
}
}
class ListNumAdd extends RuntimeException{
public ListNumAdd(){
super("不能添加超过集合长度的索引");
}
}

List和Array转换

如果传入的数组大小比List元素个数小时,List内部会创建一个新的刚好够大的数组填充后返回;如果传入的数组大小比List元素个数大时,填充完元素后,剩余的元素一律填充null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package one;

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

public class collection {
public static void main(String[] ages){
try{
List<Integer> list=List.of(11,13,22);
Number[] array=list.toArray(new Number[list.size()]);
for (Number integer : array) {
System.out.println(integer);
}
}catch(ArrayStoreException e){
throw new ListNumAdd();
}
}
}
class ListNumAdd extends RuntimeException{
public ListNumAdd(){
super("类型不正确");
}
}

Array变为List时,返回的List只是一个只读List,无法进行增删元素操作。

覆写equals()方法

  • boolean contains(Object o):判断List中是否包含某个指定元素
  • int indexOf(Object o):返回某个元素的索引,如果元素不存在就返回-1

containsindexOf等方法是通过equals方法来判断两个元素是否相等,所以放入的实例必须正确覆写equals方法。StringInteger这些对象是已经正确实现了equals方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package one;

import java.util.List;

public class collection {
public static void main(String[] ages){
List<Person> list=List.of(new Person("xiaodi"),new Person("xiaobai"));
System.out.println(list.contains(new Person("xiaodi"))); //false
System.out.println(list.contains(list.get(0))); //true
}
}
class Person{
String name;
public Person(String name){
this.name = name;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package one;

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

public class collection {
public static void main(String[] ages){
List<Person> list=List.of(new Person("xiaodi"),new Person("xiaobai"));
System.out.println(list.contains(new Person("xiaodi")));//true
System.out.println(list.contains(list.get(0)));//true
}
}
class Person{
String name;
public Person(String name){
this.name = name;
}
public boolean equals(Object o){
if (!(o instanceof Person p)) return false;
return Objects.equals(this.name, p.name);

/*jdk8版本
if (!(o instanceof Person)) return false;
Person p = (Person)o;
return Objects.equals(name, p.name);
*/
}
}

1. list.contains(new Person("xiaodi")) 开始执行
2. List内部遍历每个元素,调用每个元素的equals方法
3. 第一次调用:list.get(0).equals(new Person("xiaodi"))
- this → list.get(0)对象(name="xiaodi"
- o → new Person("xiaodi")对象
4. 执行equals方法:
if (!(o instanceof Person p)) return false; // o是Person类型,通过
return Objects.equals(this.name, p.name);
- this.name = "xiaodi"(第一个对象的name)
- p.name = "xiaodi"(参数对象的name)
5. Objects.equals("xiaodi", "xiaodi") 返回 true
6. contains()立即返回true

Map

实现类:HashMap

boolean containsKey(K key):检查key是否存在。

Map中不存在重复的key(因为放入相同的key会把原有的key-value对应的value替换掉),但可以存在相同的value

遍历Map

遍历key,可以使用Map实例的keySet()方法返回的不包含重复keySet集合。

同时遍历keyvalue,可以使用Map对象的entrySet()集合,它包含了每一个key-value映射。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package one;

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

public class collection {
public static void main(String[] ages){
Map<String,Integer> map = new HashMap<>();
map.put("one",1);
map.put("two",2);
for(Map.Entry<String,Integer> entry : map.entrySet()){
System.out.println(entry.getKey() +":"+ entry.getValue());
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package one;

import java.util.*;

public class collection {
public static void main(String[] ages){
List<Student> list = List.of(
new Student("Bob", 78),
new Student("Alice", 85),
new Student("Brush", 66),
new Student("Newton", 99));
var holder = new Students(list);
System.out.println(holder.getScore("Bob") == 78 ? "测试成功!" : "测试失败!");
System.out.println(holder.getScore("Alice") == 85 ? "测试成功!" : "测试失败!");
System.out.println(holder.getScore("Tom") == -1 ? "测试成功!" : "测试失败!");
}
}
class Students {
List<Student> list;
Map<String, Integer> cache;
Students(List<Student> list) {
this.list = list;
cache = new HashMap<>();
}
/**
* 根据name查找score,找到返回score,未找到返回-1
*/
int getScore(String name) {
// 先在Map中查找:
Integer score = this.cache.get(name);
if (score == null) {
return findInList(name);
}
return score == null ? -1 : score.intValue();
}
Integer findInList(String name) {
for (var ss : this.list) {
if (ss.name.equals(name)) {
return ss.score;
}
}
return null;
}
}
class Student {
String name;
int score;

Student(String name, int score) {
this.name = name;
this.score = score;
}
}

覆写HashCode()方法

HashMap是用一个大数组存储所有的value,再根据key计算出value应该存储在哪个索引,所以Map也需要覆写equals()方法来保证两个相同类型相同参数的key实例相等。

通过key计算索引的方式就是调用key对象的hashCode()方法,它返回一个int整数。也需要覆写来确保key实例相同时对应的索引也相同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package one;

import java.util.*;

public class collection {
public static void main(String[] ages){
Map<Person,Integer> map = new HashMap<>();
Person key=new Person("John",20);
map.put(key,1);
System.out.println(map.get(key)); //1
System.out.println(map.get(new Person("John",20))); //null
}
}
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}

正确使用Map必须保证

  1. 作为key的对象必须正确覆写equals()方法,相等的两个key实例调用equals()必须返回true
  2. 作为key的对象还必须正确覆写hashCode()方法,且hashCode()方法要严格遵循以下规范:
    • 如果两个对象相等,则两个对象的hashCode()必须相等;
    • 如果两个对象不相等,则两个对象的hashCode()尽量不要相等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package one;

import java.util.*;

public class collection {
public static void main(String[] ages){
Map<Person,Integer> map = new HashMap<>();
Person key=new Person("John",20);
map.put(key,1);
System.out.println(map.get(key));
System.out.println(map.get(new Person("John",20)));
}
}
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if(o instanceof Person p) return this.age=p.age && Objects.equals(this.name, p.name);
return false;
}
@Override
public int hashCode() {
return Objects.hash(this.name);
}
}

拓展

1、hashCode()返回的int类型范围为+2147483647~-2147483647,而HashMap内部使用的数组默认大小只有16。当超过一定数量的键值对时,HashMap会自动扩容一倍并重新通过hashCode()计算索引位置。频繁扩容对HashMap的性能影响很大,所以创建HashMap时可以指定内存空间大小。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package one;

import java.util.*;
import java.io.*;


public class collection {
public static void main(String[] ages){
Map<String,Integer> map = new HashMap<>(100);
map.put("one",1);
map.put("two",2);
map.put("three",3);
for(Map.Entry<String,Integer> entry : map.entrySet()){
System.out.println(entry.getKey() + " " + entry.getValue());
}
}
}

2、不同的key具有相同的hashCode()的情况称之为哈希冲突。在冲突的时候,一种最简单的解决办法是用List存储hashCode()相同的key-value。显然,如果冲突的概率越大,这个List就越长,Mapget()方法效率就越低。

EnumMap

当key的对象是enum类型时可以使用。内部以一个非常紧凑的数组存储value,并且根据枚举类型的key直接定位到内部数组的索引,并不需要计算hashCode()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package org.example;

import java.time.DayOfWeek;
import java.util.*;

public class CollectionDemo {
public static void main(String[] args) {
Map<DayOfWeek, String> map = new EnumMap<>(DayOfWeek.class);
map.put(DayOfWeek.MONDAY, "星期一");
map.put(DayOfWeek.TUESDAY, "星期二");
map.put(DayOfWeek.WEDNESDAY, "星期三");
map.put(DayOfWeek.THURSDAY, "星期四");
map.put(DayOfWeek.FRIDAY, "星期五");
map.put(DayOfWeek.SATURDAY, "星期六");
map.put(DayOfWeek.SUNDAY, "星期日");
System.out.println(map);
System.out.println(map.get(DayOfWeek.MONDAY));
}
}

SortedMap会自动对key进行排序。它的实现类是TreeMap。使用TreeMap时,放入的key必须实现Comparable接口。否则在创建TreeMap时就得同时指定一个自定义排序算法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package org.example;

import java.time.DayOfWeek;
import java.util.*;

public class CollectionDemo {
public static void main(String[] args) {
Map<Person, Integer> map = new TreeMap<>(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.name.compareTo(o2.name);
}
});
map.put(new Person("老白"),1);
map.put(new Person("小白"),2);
System.out.println(map);
}
}
class Person{
String name;
public Person(String name){
this.name = name;
}
public String toString(){
return name;
}
}

Properties

用来表示一组配置的集合,内部本质是一个Hashtable

java默认配置文件以.properties为拓展名,每行以key=value表示,以#开头为注释。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package org.example;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.DayOfWeek;
import java.util.*;

public class CollectionDemo {
public static void main(String[] args)throws IOException {
Path peizhi= Paths.get(".\\src\\main\\java\\demo\\one.properties");
Path parent= peizhi.getParent();
if(parent!=null&& Files.notExists(parent)) Files.createDirectories(parent);
if(Files.notExists(peizhi)) Files.createFile(peizhi);
try(InputStream input=new FileInputStream(peizhi.toFile())) {
Properties properties = new Properties();
properties.load(input);
System.out.println(properties.getProperty("open_file"));
}
}
}
class Person{
String name;
public Person(String name){
this.name = name;
}
public String toString(){
return name;
}
}

Set