2020/4/7
List
void add(E e)
void add(int index, E e)
int remove(int index)
int remove(Object e)
E get(int index)
int size()
-1:
int indexOf(Object o)创建List:List.of()方法不接受null值,如果传入null,会抛出NullPointerException异常
List list = List.of(1, 2, 5);
遍历List:用Iterator迭代器
Iterator
对象有两个方法:boolean hasNext()
判断是否有下一个元素,E next()
返回下一个元素
public class Main {
public static void main(String[] args) {
List list = List.of("apple", "pear", "banana");
for (Iterator it = list.iterator(); it.hasNext(); ) {
String s = it.next();
System.out.println(s);
}
//也可以简写为这样
for (String s : list) {
System.out.println(s);
}
}
}
List和Array转换:
给toArray(T[])
传入一个类型相同的Array
,List
内部自动把元素复制到传入的Array
中:
public class Main {
public static void main(String[] args) {
List list = List.of(12, 34, 56);
Integer[] array = list.toArray(new Integer[3]);
for (Integer n : array) {
System.out.println(n);
}
}
}
把Array变为List,通过List.of(T...)方法:
Integer[] array = { 1, 2, 3 };
List list = List.of(array);
List.of()方法返回的是一个只读List,不能调用add(),remove()方法。
要正确使用List
的contains()
、indexOf()
这些方法,放入的实例必须正确覆写equals()
方法
public class Person {
public String name;
public int age;
//覆写equals()方法
public boolean equals(Object o) {
if (o instanceof Person) {
Person p = (Person) o;
return Objects.equals(this.name, p.name) && this.age == p.age;
}
return false;
}
}
引用类型的比较,使用Objects.equals()静态方法 ,两个引用类型都是null
时它们也是相等的。
Map
是一种键-值映射表。和List
类似,Map
也是一个接口,最常用的实现类是HashMap
。
常用方法:
put(K key, V value):
把key
和value
做了映射并放入Map
。V get(K key):
以通过key
获取到对应的value
。如果key
不存在,则返回null
。boolean containsKey(K key)
方法:查询某个key
是否存在。key-value集合可以调用getKey(),getValue()方法得到key和value。
public class Main {
public static void main(String[] args) {
Map map = new HashMap<>();
map.put("apple", 123);
map.put("pear", 456);
map.put("banana", 789);
for (Map.Entry entry : map.entrySet()) {
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key + " = " + value);
}
}
}
正确使用Map
必须保证:
作为key
的对象必须正确覆写equals()
方法,相等的两个key
实例调用equals()
必须返回true
;
作为key
的对象还必须正确覆写hashCode()
方法,且hashCode()
方法要严格遵循以下规范:
hashCode()
必须相等;hashCode()
尽量不要相等。计算hashCode()的时候,经常借助Objects.hash()来计算:
int hashCode() {
return Objects.hash(firstName, lastName, age);
}
编写equals()
和hashCode()
遵循的原则是:
equals()
用到的用于比较的每一个字段,都必须在hashCode()
中用于计算;equals()
中没有使用到的字段,绝不可放在hashCode()
中计算。对于放入HashMap
的value
对象,没有任何要求。
如果作为key的对象是enum
类型,那么,还可以使用Java集合库提供的一种EnumMap
,它在内部以一个非常紧凑的数组存储value,并且根据enum
类型的key直接定位到内部数组的索引,并不需要计算hashCode()
,不但效率最高,而且没有额外的空间浪费。
public class Main {
public static void main(String[] args) {
Map 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));
}
}
有一种Map
,它在内部会对Key进行排序,这种Map
就是SortedMap
。注意到SortedMap
是接口,它的实现类是TreeMap
。
使用TreeMap时,放入的Key必须实现Comparable接口,如果作为Key的class没有实现Comparable
接口,那么,必须在创建TreeMap
时同时指定一个自定义排序算法:
public class Main {
public static void main(String[] args) {
Map map = new TreeMap<>(new Comparator() {
public int compare(Person p1, Person p2) {
return p1.name.compareTo(p2.name);
}
});
map.put(new Person("Tom"), 1);
map.put(new Person("Bob"), 2);
map.put(new Person("Lily"), 3);
for (Person key : map.keySet()) {
System.out.println(key);
}
// {Person: Bob}, {Person: Lily}, {Person: Tom}
System.out.println(map.get(new Person("Bob"))); // 2
}
}
class Person {
public String name;
Person(String name) {
this.name = name;
}
public String toString() {
return "{Person: " + name + "}";
}
}
Set
用于存储不重复的元素集合,它主要提供以下几个方法:
Set
:boolean add(E e)
Set
删除:boolean remove(Object e)
boolean contains(Object e)
集合中元素个数:int size()
Set
实际上相当于只存储key、不存储value的Map
。我们经常用Set
用于去除重复元素。因为放入Set
的元素和Map
的key类似,都要正确实现equals()
和hashCode()
方法,否则该元素无法正确地放入Set
。
最常用的Set
实现类是HashSet
,实际上,HashSet
仅仅是对HashMap
的一个简单封装。使用TreeSet
和使用TreeMap
的要求一样,添加的元素必须正确实现Comparable
接口,如果没有实现Comparable
接口,那么创建TreeSet
时必须传入一个Comparator
对象。
2020/4/8
Java的标准库中,队列接口Queue
定义了以下几个方法:
int size()
:获取队列长度;boolean add(E)
/boolean offer(E)
:添加元素到队尾;E remove()
/E poll()
:获取队首元素并从队列中删除;E element()
/E peek()
:获取队首元素但并不从队列中删除。抛出异常 | 返回true/false或null | |
入队 | boolean add(E e) | boolean offer(E e) |
出队,获取队首元素 | E remove() | E poll() |
获取队首元素 | E element() | E peek() |
注意:不要把null
添加到队列中,否则poll()
方法返回null
时,很难确定是取到了null
元素还是队列为空。
LinkedList
即实现了List
接口,又实现了Queue
接口:
// 这是一个List:
List list = new LinkedList<>();
// 这是一个Queue:
Queue queue = new LinkedList<>();
调用add()方法,当添加失败时(可能超过了队列的容量),它会抛出异常:
Queue q = ...
try {
q.add("Apple");
System.out.println("添加成功");
} catch(IllegalStateException e) {
System.out.println("添加失败");
}
调用offer():
Queue q = ...
if (q.offer("Apple")) {
System.out.println("添加成功");
} else {
System.out.println("添加失败");
}
优先级队列PriorityQueue:
PriorityQueue
实现了一个优先队列:从队首获取元素时,总是获取优先级最高的元素。
PriorityQueue
默认按元素比较的顺序排序(必须实现Comparable
接口),也可以通过Comparator
自定义排序算法(元素就不必实现Comparable
接口)。
public class Main {
public static void main(String[] args) {
Queue q = new PriorityQueue<>(new UserComparator());
// 添加3个元素到队列:
q.offer(new User("Bob", "A1"));
q.offer(new User("Alice", "A2"));
q.offer(new User("Boss", "V1"));
System.out.println(q.poll()); // Boss/V1
System.out.println(q.poll()); // Bob/A1
System.out.println(q.poll()); // Alice/A2
System.out.println(q.poll()); // null,因为队列为空
}
}
class UserComparator implements Comparator {
public int compare(User u1, User u2) {
if (u1.number.charAt(0) == u2.number.charAt(0)) {
// 如果两人的号都是A开头或者都是V开头,比较号的大小:
int num1 = Integer.parseInt(u1.number.substring(1, u1.number.length()));
int num2 = Integer.parseInt(u2.number.substring(1, u2.number.length()));
return Integer.compare(num1, num2);
}
if (u1.number.charAt(0) == 'V') {
// u1的号码是V开头,优先级高:
return -1;
} else {
return 1;
}
}
}
class User {
public final String name;
public final String number;
public User(String name, String number) {
this.name = name;
this.number = number;
}
public String toString() {
return name + "/" + number;
}
}
双端队列Deque:
实现类有ArrayDeque和LinkedList;
Deque d2 = new LinkedList<>();
d2.offerLast("z");
addLast()
/offerLast()
/addFirst()
/offerFirst()
;removeFirst()
/pollFirst()
/removeLast()
/pollLast()
;getFirst()
/peekFirst()
/getLast()
/peekLast()
;栈(Stack)是一种后进先出(LIFO)的数据结构,操作栈的元素的方法有:
push(E)
;pop()
;peek()
。在Java中,我们用Deque
可以实现Stack
的功能,注意只调用push()
/pop()
/peek()
方法。
如果我们自己编写了一个集合类,想要使用for each
循环,只需满足以下条件:
Iterable
接口,该接口要求返回一个Iterator
对象;Iterator
对象迭代集合内部数据。一个简单的Iterator示例如下,它总是以倒序遍历集合:
import java.util.*;
public class Main {
public static void main(String[] args) {
ReverseList rlist = new ReverseList<>();
rlist.add("Apple");
rlist.add("Orange");
rlist.add("Pear");
for (String s : rlist) {
System.out.println(s);
}
}
}
class ReverseList implements Iterable {
private List list = new ArrayList<>();
public void add(T t) {
list.add(t);
}
@Override
public Iterator iterator() {
return new ReverseIterator(list.size());
}
class ReverseIterator implements Iterator {
int index;
ReverseIterator(int index) {
this.index = index;
}
@Override
public boolean hasNext() {
return index > 0;
}
@Override
public T next() {
index--;
return ReverseList.this.list.get(index);
}
}
}
在编写Iterator
的时候,我们通常可以用一个内部类来实现Iterator
接口,这个内部类可以直接访问对应的外部类的所有字段和方法。例如,上述代码中,内部类ReverseIterator
可以用ReverseList.this
获得当前外部类的this
引用,然后,通过这个this
引用就可以访问ReverseList
的所有字段和方法。
Collections
类提供了一组工具方法来方便使用集合类:
addAll()方法可以给一个Collection类型的集合添加若干元素:
public static boolean addAll(Collection super T> c, T... elements) { ... }
创建空集合:
List emptyList()
Map emptyMap()
Set emptySet()
返回的空集合是不可变集合,无法向其中添加或删除元素。
创建单元素集合:
List singletonList(T o)
Map singletonMap(K key, V value)
Set singleton(T o)
返回的单元素集合也是不可变集合,无法向其中添加或删除元素。
排序:
Collections.sort(list);
洗牌:
Collections.shuffle(list);
不可变集合
List unmodifiableList(List extends T> list)
Set unmodifiableSet(Set extends T> set)
Map unmodifiableMap(Map extends K, ? extends V> m)
public class Main {
public static void main(String[] args) {
List mutable = new ArrayList<>();
mutable.add("apple");
mutable.add("pear");
// 变为不可变集合:
List immutable = Collections.unmodifiableList(mutable);
// 立刻扔掉mutable的引用:
mutable = null;
System.out.println(immutable);
}
}