第9章:集合
最近都没怎么学习,我很愧疚
9.1 Java集合框架
9.1.1 集合接口与实现分离
例如队列。队列Queue一般有两种实现方式,一种是循环数组CircularArrayQueue,另一种是链表LinkedListQueue
接口回调 :可以把使用实现了某一接口的类创建的对象的引用赋给该接口声明的接口变量,那么该接口变量就可以调用被类实现的接口的方法
实现与接口分类,接口回调已经实现的接口。
Queue … = new CircularArrayQueue()… 而使用的时候,Queue的方法便足矣
在类库中会有一些以Abstract开头的类,这是为类库实现者而设计的。例如想要实现自己队列类,扩展AbstactQueue类要比实现Queue接口中的所有方法轻松得多.
9.1.2 Collection接口
public interface Collection<E>{
boolean add(E elements);
//如果添加元素影响了集合就返回true(集合中不允许存在重复的对象)
Iterator<E> iterator();
...
}
9.1.3 迭代器
Iterator包含4个方法
public interface Iterator<E>{
E next();//如果到达了末尾,会抛出来一个NoSuchElementException异常
boolean hasNext();//所以需要先调用这个方法判断一下
void remove();
default void forEachRemaining(Consumer<? super E> action);
}
Collection<String> c = ...;
Iterator<String> iter = c.iterator();
while(iter.hasNext(){
String element = iter.next();
dosomething
}
//或者使用foreach
for(String element : iter){
dosomething
}
对于任何实现了Iterable接口的对象都可以使用foreach循环。
也可以不写循环,而是对forEachRemaining传入一个lambda表达式,他将会对迭代器中的每一个元素调用这个lambda表达式,直到没有元素为止
在Java中查找与位置变更紧密耦合。查找一个元素的唯一方法是调用next,而在执行查找操作的同时,迭代器的位置就会向前移动
remove方法删除的是上次调用next方法返回的元素,这很合理,在删除某一个元素之前,应该至少看一看这个元素。
package com.package1;
import java.util.*;
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("wuhu");
list.add("qifei");
Iterator<String> iterator = list.iterator();
System.out.println(iterator.next());
iterator.remove();
System.out.println(list);
}
}
//result:
//wuhu
//[qifei]
更重要的是next与remove方法之间存在依赖性,如果在remove之前没有调用next,就会抛出一个IllegalStateExceptionyic
iter.remove();
iter.next();//如果注释掉,就为非法。
iter.remove();
9.1.4 泛型使用方法
例如对于Collection接口,包括了很多抽象方法,这是有用的,但是对于程序员使用类库,扩展Collection接口,需要一一去实现,这无疑是冗杂的。
所以,在类库中提供了Abstract–的抽象类,除了size和Iterator方法为抽象的,其他的方法都 提供了实现。
使用默认方法default关键字可能会更好,但是并没有这样实现。
9.2 集合框架中的接口
其中集合框架的接口的继承树如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NKR4C5kD-1626595498349)(Java核心技术·卷一·六到九章.assets/草稿.svg)]
List是一个有序集合,元素会增加到容器中的特定位置,可以采用两种方式来访问
-
顺序访问,亦称迭代器访问
-
随机访问,使用一个整数值来访问,list接口有多个随机访问的接口
void add(int index j, E element); void remove(int index); E get(int index); E set(int index, E element);
ListIterator接口是Iterator的一个子接口,它定义了一个方法用于在迭代器位置前面增加元素
void add(E element)
实际上有两种有序集合
- 数组支持的有序集合,使用整数索引来访问的效率较高
- 链表,用迭代器来访问的效率更高
在Java1.4中引入了RandomAccess这个标记接口,来标志一个特定的集合是否支持快速随机访问
Set:集,set的add方法不允许增加重复的元素,而且set的equals方法是只要两个集包含同样的元素就认为它们是相等的(而不考虑顺序)(为此hashCode方法也需要进行重载)
9.3 具体集合
集合类型 | 描述 |
---|---|
ArrayList | 可以动态增长和缩减的一个索引序列 |
LinkedList | 可以在任何位置高效插入和删除的一个有序序列 |
ArrayDeque | 实现为循环数组的一个双端队列 |
HashSet | 没有重复元素的一个无序集合 |
TreeSet | 一个有序集 |
EnumSet | 一个包含枚举类型值的集 |
LinkedHashSet | 一个可以记住元素插入次序的集 |
PriorityQueue | 运行高效删除最小元素的一个集合 |
HashMap | 存储键/值关联的一个数据结构 |
TreeMap | 键有序的一个映射 |
EnumMap | 键属于枚举类型的一个映射 |
LinkedHashMap | 可以记住键/值下项添加次序的一个映射 |
WeakHashMap | 值不会再别处使用时就可以被垃圾回收的一个映射 |
IdentityHashMap | 用 == 而不是equals比较键的一个映射 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YIfb3POc-1626595498358)(Java核心技术·卷一·六到九章.assets/草稿 (1)].svg)
9.3.1 链表
对于数组以及动态的ArrayList,删除或新增一个元素的代价是较大的,需要去移动其他的元素。但是对于链表(Link),这种代价是较小的
在Java中的所有link全为双向link
链表是一个有序的集合,LinkedList的add方法是将元素加到尾部,所以需要使用迭代器来实现在中间加入元素。
由于在非自然有序的集合使用迭代器添加元素的没意义的,又为了足够抽象Iterator,Iterator并没有add方法。实际上集合类库提供了一个子接口ListIterator,其中包括了add方法
interface ListIterator<E> extends Iterator<E>{
void add(E elements);
//与Collection的add不同,这个方法不返回布尔值,它假定修改是一定会是链表的结构发生改变的
E previous();
boolean hasPrevivous();
//这俩方法可以实现对link的反遍历
}
add方法只依赖与迭代器的位置,而不像remove方法依赖于状态(即与next紧密耦合)
package com.package1;
import java.util.*;
public class Test {
public static void main(String[] args) {
LinkedList<String> staff = new LinkedList<>();
staff.add("A");
staff.add("B");
staff.add("C");
ListIterator<String> iterator = staff.listIterator();
iterator.next();
iterator.add("D");
//add方法在刚返回的元素的后面加一个新元素
System.out.println(staff);
System.out.println(iterator.next());
//可见add方法之后迭代器移到add的元素之后了
iterator.set("E");
//set方法将刚返回的元素set为新的值
System.out.println(staff);
}
}
//result:
//[A, D, B, C]
使用多重迭代器的时候,如果一个迭代器在遍历,而另一个迭代器或者这集合本身进行了修改,就有可能抛出ConcurrentModificationException异常
这个机制的简单检测是:集合会记录自己被修改的次数(例如添加或者删除元素),而迭代器也会,在每次迭代器的方法被调用的时候,就会检测两个次数是否相等,如果不相等,就证明有其他的迭代器修改了数据,就会抛出ConcurrentModificationException异常
不过,有个奇怪的例外。链表只追踪对列表的结构性修改,于是set方法不视为结构性修改。所以,多个迭代器,随便调用set方法都是不会抛出异常的
按理来讲,由于对link进行随机访问的效率是低下的,不应该提供随机访问的方法,但是LinkedList还是定义了一个get方法。这玩意儿应该避免使用
for(int i = 0; i < list.size(); i++){
do something with list.get(i);
//这段代码的效率可谓低下得不能再低下
//完全违反了数据结构,使用了一种虚假的遍历。
}
package com.package1;
import java.util.*;
public class Test {
public static void main(String[] args) {
LinkedList<String> staff = new LinkedList<>();
staff.add("A");
staff.add("B");
staff.add("C");
ListIterator<String> iterator = staff.listIterator(2);
System.out.println(iterator.next());
//result: C
//这一方面说明了索引是从0开始的,另一方面说明了,返回的是给定的数的前面的位置的迭代器
}
}
总之,不应该违反数据结构与使用link的初衷(减少增删元素的开销)使用那些随机访问的方法。
package com.package1;
import java.util.*;
public class Test {
public static void main(String[] args) {
var a = new LinkedList<String>();
a.add("Amy");
a.add("Carl");
a.add("Erica");
var b = new LinkedList<String>();
b.add("Bob");
b.add("Doug");
b.add("Frances");
b.add("Gloria");
ListIterator<String> aIter = a.listIterator();
Iterator<String> bIter = b.iterator();
while(bIter.hasNext()){
if(aIter.hasNext()){
aIter.next();
}
aIter.add(bIter.next());
}
System.out.println(a);
bIter = b.iterator();
while(bIter.hasNext()){
bIter.next();
if(bIter.hasNext()){
bIter.next();
bIter.remove();
}
}
System.out.println(b);
a.removeAll(b);
System.out.println(a);
}
}
//rsult:
//[Amy, Bob, Carl, Doug, Erica, Frances, Gloria]
//[Bob, Frances]
//[Amy, Carl, Doug, Erica, Gloria]
9.3.2 数组列表
List接口用于描述一个有序集合,而去集合中的每一个元素的位置都很重要。
ArrayList封装了一个动态再分配的对象数组。
注意:vector是同步的,而ArrayList是不同步的.在单线的情况下,vector由于同步,会损失效率;在多线情况下,ArrayList由于不是同步的,是不安全的。
9.3.3 散列集
相对于链表和数组,散列表(hash table)可以快速的进行随机索引。它为每一个对象计算一个整数——散列码(hash code)。散列码是由对象的实例字段计算得来的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PGmE1j5c-1626595498360)(Java核心技术·卷一·六到九章.assets/image-20210712110816513.png)]
在Java中,散列表用链表数组实现。每一个列表称之为桶(bucket)。假设有m个桶,对于一个散列码为n的对象,查看n%m的桶里面的元素,如果没有,那么就插入。如果有,这种情况称之为散列冲突,需要将原有的对象与这个对象比较查看是否重复。
如果散列表太满,就会影响索引速度,就需要 再散列。 填充因子可以确定何时对散列表进行再散列。默认为0.75.
在桶满的时候,如果不进行在散列,会变成平衡二叉树。
HashSet实现了基于散列表的集。
//Hash的构造方法
HashSet();
HashSet(Collection<? extends E>);
//将集合中的所有元素添加到新构建的散列集中
HashSet(int initialCapacity);
//initialCapacity为桶量
HashSet(int initialCapacity, float loadFactor);
//loadFactor为填充因子
下列程序利用HashSet散列集完成了统计输入的不重复单词数目的功能。还显示了运行时间
package com.package1;
import java.util.*;
public class Test {
public static void main(String[] args) {
var words = new HashSet<String>();
long totalTime = 0;
try(var in = new Scanner(System.in)){
while(in.hasNext()){
String word = in.next();
long callTime = System.currentTimeMillis();
words.add(word);
callTime = System.currentTimeMillis() - callTime;
totalTime += callTime;
}
}
Iterator<String> iter = words.iterator();
for(int i = 0; i <= 100 && iter.hasNext(); i++){
System.out.println(iter.next());
}
System.out.println(".....");
System.out.println(words.size()+" distinct words "+ totalTime + " milliseconds");
}
}
9.3.4 树集
TreeSet。与散列集类似,但是它是一个有序集合。可任意顺序插入,但是在遍历的时候会自动按照排序后的顺序呈现
package com.package1;
import java.util.*;
public class Test {
public static void main(String[] args) {
var sorter = new TreeSet<String>();
sorter.add("Amy");
sorter.add("Carl");
sorter.add("Bob");
for(String s : sorter){
System.out.println(s);
}
}
}
//result:
//Amy
//Bob
//Carl
其实现是用一个树数据结构实现的(当前所使用的是红黑树)
将元素添加到树中要比添加到散列表中慢,但是查找显著快。如果树中包含n个元素,查找新元素的正确位置平均需要log2n次比较就行了。
注意:由于需要比较,元素必须实现Comparable接口
相对于散列集,树集需要是全排列的,这意味着每一个元素都能够比较(例如,元素是一个矩形的时候,比较什么?比较面积嘛?这行不通,可能面积一样坐标不一样,比较面积和坐标?也有可能长宽不一样,这就造成了一些复杂的后果。),所以说,如果不需要元素具有有序性,就没必要使用TreeSet来造成排序的开销和额外的麻烦。
package com.package1;
import com.sun.source.tree.Tree;
import java.util.*;
public class Test {
public static void main(String[] args) {
var parts = new TreeSet<Item>();
parts.add(new Item("Toaster",1234));
parts.add(new Item("Widget",4562));
parts.add(new Item("Modem",9912));
System.out.println(parts);
var sortByDescription = new TreeSet<Item>(Comparator.comparing(Item::getDescription));
//也可以传入一个Comrarator
sortByDescription.addAll(parts);
System.out.println(sortByDescription);
}
}
class Item implements Comparable<Item>{
private String description;
private int partNumber;
public Item(String description, int partNumber) {
this.description = description;
this.partNumber = partNumber;
}
@Override
public int compareTo(Item o) {
int diff = Integer.compare(partNumber,o.partNumber);
return diff != 0 ? diff :description.compareTo(o.description);
}
@Override
public boolean equals(Object obj) {
if(this == obj){
return true;
}
if(obj == null){
return false;
}
if(getClass() != obj.getClass()){
return false;
}
var other = (Item) obj;
return Objects.equals(description,other.description) && partNumber == other.partNumber;
}
//重写equals方法必须重写hashCode方法
@Override
public int hashCode() {
return Objects.hash(description, partNumber);
}
public String getDescription() {
return description;
}
@Override
public String toString() {
return "Item{" +
"description='" + description + '\'' +
", partNumber=" + partNumber +
'}';
}
}
TreeSet是implements NavigableSet,NavigableSet implements SortedSet,SortedSet implements Set。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bpx4vQGD-1626595498361)(Java核心技术·卷一·六到九章.assets/2020090818320921.png)]
//SortedSet<E>
Comparator<? super E> comparator();
E first();
E last();
//返回有序集中的最小元素或最大元素
//NavigableSet<E>
E higer(E value);
E lower(E value);
//返回大于alue的最小元素或者小于value的最大元素。如果没有,返回null
E ceilling(E value);
E floor(E value);
//返回大于等于alue的最小元素或者小于等于value的最大元素。如果没有,返回null
E pollFirst();
E pollLast();
//删除并返回这个集中的最大元素或者最小元素,如果集为空就返回null
Iterator<E> descendingIterator();
//返回一个按照递顺序排列遍历集中的元素的迭代器
9.3.5 队列与双端队列
队列(Queue),双端队列(deuqe)。双端队列运行在队列头和队列尾增删元素,队列只能在队头删,队尾加。
Java 6 引入了Deque接口,ArrayDeque和LinkedDeque都实现了这个接口。
//Queue<E>
boolean add(E element);
boolean offer(E element);
//都是添加到队尾,如果队列满了,add方法返回IllegalStateException,offer方法返回false
E remove();
E poll();
//删除队头并返回,如果是空的,第一个返回NoSuchElementException,第二个返回null
E element();
E peek();
//如果队列不为空,返回这个队列列头的元素,如果为空,第一个返回NoSuchElementException,第二个返回null\
//Deque<E>与Quene类似,只是都添加了First表示队头,Last表示队尾
void addFirst(E element);
void addLast(E element);
...详见API文档
9.3.6 优先队列
优先队列(priority queue)与队列不同的是,它的remove方法会自动查找最小的元素,而非队头的元素。
这是由堆来实现的,堆是一个可以自组织的二叉树,其添加和删除操作可以让最小的元素移到到根,而不必花费时间进行排序。
它的经典用法就是实现优先级,不同的任务随机插入,一般用1表示最高优先级,数字递增优先级递减,实现按照优先级一次执行任务。
package com.package1;
import com.sun.source.tree.Tree;
import java.util.*;
public class Test {
public static void main(String[] args) {
var parts = new PriorityQueue<Item>();
parts.add(new Item(4));
parts.add(new Item(1));
parts.add(new Item(2));
parts.add(new Item(3));
for(Item i : parts){
System.out.println(i.getNumber());
}
while(!parts.isEmpty()){
System.out.println(parts.remove());
}
}
}
class Item implements Comparable<Item>{
private int number;
@Override
public int compareTo(Item o) {
return Integer.compare(number,o.number);
}
public Item(int number) {
this.number = number;
}
public int getNumber() {
return number;
}
@Override
public boolean equals(Object obj) {
if(this == obj){
return true;
}
if(obj == null){
return false;
}
if(getClass() != obj.getClass()){
return false;
}
var other = (Item) obj;
return number == other.number;
}
//重写equals方法必须重写hashCode方法
@Override
public int hashCode() {
return Objects.hash(number);
}
@Override
public String toString() {
return "Item{" +
"number=" +number +
'}';
}
}
//result:
1
3
2
4
Item{number=1}
Item{number=2}
Item{number=3}
Item{number=4}
9.4 映射
Map
9.4.1 基本映射操作
HashMap,TreeMap。散列映射或树映射都是只应用与键Key。
Key值唯一
public static void main(String[] args) {
HashMap<String,Item> hashMap = new HashMap<>();
hashMap.getOrDefault(0,new Item(0));
//get方法如果没有键,就会返回null,
//可以使用getOrDefault方法来实现为null的时候给一个默认值
}
package com.package1;
import java.util.*;
public class Test {
public static void main(String[] args) {
HashMap<Integer,String> hashMap = new HashMap<>();
hashMap.put(1,"Tom");
hashMap.put(2,"Amy");
hashMap.put(3,"Jerry");
hashMap.forEach(
(k,v)->{
System.out.println("k:"+k+" v:"+v);
}
);
}
}
result:
k:1 v:Tom
k:2 v:Amy
k:3 v:Jerry
9.4.2 更改映射条目
例如,对于一个String->Integer的映射,Integer记录了String出现的次数。
counts.put(word,count.get(word)+1);
但是对于第一次看到word的时候,get会返回null,就会出现一个NullPointerException异常
1.使用getOrDefault方法
counts.put(word,counts.getorDefault(word,0)+1);
2.使用putIfAbsent方法
counts.putIfAbsent(word,0);
counts.put(word,counts.get(word)+1);
3.使用merge简化2的操作
counts.merge(word,1,Integer::sum);
如果键原来不存在,会将word与1关联,如果存在,就会调用sum方法组合原值与1(即+1)
9.4.3 映射视图
映射不被认为本身是一个集合,不过可以得到映射的视图
Set<K> keySey();
Collection<V> values;
Set<Map.Entry<K,V>> entrySet();
keySet与HashSet和TreeSet是并列关系而非子类关系,它是实现了Set接口的另一个对象
HashMap<Integer,String> hashMap = new HashMap<>();
hashMap.put(1,"Tom");
hashMap.put(2,"Amy");
hashMap.put(3,"Jerry");
Set<Integer> keySet = hashMap.keySet();
keySet.forEach(i->{
System.out.println(i);
});
package com.package1;
import java.util.*;
public class Test {
public static void main(String[] args) {
HashMap<Integer,String> hashMap = new HashMap<>();
hashMap.put(1,"Tom");
hashMap.put(2,"Amy");
hashMap.put(3,"Jerry");
Set<Integer> keySet = hashMap.keySet();
Iterator<Integer> iterator = keySet.iterator();
iterator.next();
iterator.remove();
System.out.println(keySet);
//如果在键集视图上调用迭代器的remove方法,那么原来的映射也会被修改。但是不能添加,哪怕是键值视图映射
//result:
//[2,3]
}
}
9.4.4 弱散列映射
WeakHashMap使用弱引用(weak reference)保存键,当一个对象没有任何对象引用,就会进行垃圾回收
9.4.5 链接散列集与映射
链接散列集(LinkedHashSet)与映射(LinkedHashMap)是有序的,它们会记住插入元素的顺序(采用链表的形式)
可以使用removeEldestEntry(Map.Entry<K,V> eldest)方法来实现将不常用的元素从链表的前面移动到尾部(在桶的顺序是不会变的),实现高效索引,或者控制容量。
9.4.6 枚举集与映射
E extends Enum 这个类型参数的意思是E是一个枚举类型,所有的枚举类型都扩展了泛型Enum类。
EnumSet内部用位序列来实现,如果对于的值在集中,就用1来表示
package com.package1;
import java.util.*;
enum WeekDay{MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY,SUNDAY};
public class Test {
public static void main(String[] args) {
EnumSet<WeekDay> e = EnumSet.allOf(WeekDay.class);
e.forEach(i->{
System.out.println(i);
});
}
}
package com.package1;
import java.util.*;
enum WeekDay{MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY,SUNDAY};
public class Test {
public static void main(String[] args) {
EnumSet<WeekDay> e = EnumSet.allOf(WeekDay.class);
e.forEach(i->{
System.out.println(i);
});
}
}
var personInCharge = new EnumMap<Weekday,Employee>(Weekday.class)
9.4.7 标识散列映射
IdentityHashMap中,键的散列值不是用hansCode函数所计算的,而是用System.identityHashCode方法计算的,它们比较的是对象的地址,而不是内容。而且,在对两个对象进行比较时,IdentityHashMap类使用 ==
9.5 视图与包装器
例如对于KeySet,它返回的并不是一个由原来的集的键所创建的新集,而是返回了一个实现了Set接口的对象,由这个对象直接操作原映射。
9.5.1 小集合
在Java 9 中引入了of…的静态方法,它们可以创建不可修改的一些小集合,这些小集合的开销很低。
package com.package1;
import java.util.*;
public class Test {
public static void main(String[] args) {
List<String> names = List.of("Peter","Paul","Mary");
Set<Integer> numbers = Set.of(1,2,3);
Map<String,Integer> scores = Map.of("Peter",2,"Tom",2,"Mary",4);
//Map.Entry(first,last)可作为一个对
Map<String,Integer> map = Map.ofEntries(Map.entry("QinLi",1),Map.entry("MuRuo",2));
List<String> strings = Collections.nCopies(10,"QinLi");
strings.forEach(i->{
System.out.println(i);
});
//result: QinLi *10
}
}
9.5.2 子范围
对于Set,Map,等等有很多sub方法,可以实现截取全映射的子集,一般为左闭右开的截取,Navigable接口的方法可以自行控制是否包含接口
9.5.3 不可修改的视图
Collections.unmodifiableCollection;
Collections.unmodifiableList;
Collections.unmodifiableSet;
Collections.unmodifiableSortedSet;
Collections.unmodifiableNavigableSet;
Collections.unmodifiableMap;
Collections.unmodifiableSortedMap;
Collections.unmodifiableNavigableMap;
注意:比较不可修改的视图是否相等的时候,unmodifiableCollection是比较的两个对象是不是同一个对象,而unmodifiableSet与unmodifiableList会比较内容。
9.5.4 同步视图
有同步的视图,例如Collections类的静态synchronizedMap方法可以将任何一个映射转换成有同步访问方法的Map
9.5.5 检查型视图
例如对于下列代码,只会有一个警告,而在运行的时候才会出现问题
var Strings = new ArrayList<String>();
ArrayList rawList = Strings;//这一步会出现一个警告
rawList(new Date());
但是检查型视图在插入的时候会检查插入的类似是否相符,如果不符就会立即抛出ClassCastException,虽然说也是在运行的时候才能发现错误,但是这种方法会正确的位置报告错误
List<String> safeStrings = collections .checkedList(strings,String.class);
9.5.6 关于可选操作的说明
在设计的时候,不同的视图支持的操作是不尽相同的。有些只读,有些只能删除,有些无法改变大小等等。但是类库的设计者们为了精简接口,没有为他们单独设置接口,所以在API文档中,定义某些操作是可选的,但是建议不要用。
9.6算法
9.6.1 为什么使用泛型算法
一次实现,多次使用
9.6.2 排序与混排
collection实现了sort方法,采用的是归并排序。
package com.package1;
import java.util.*;
public class Test {
public static void main(String[] args) {
var numbers = new ArrayList<Integer>();
for(int i = 1;i <= 49; i++){
numbers.add(i);
}
Collections.shuffle(numbers);
//shuffle方法会打乱数组
List<Integer> winningCombination = numbers.subList(0,6);
Collections.sort(winningCombination);
System.out.println(winningCombination);
}
}
9.6.3 二分查找
对于有序(注意:一定要是有序的)的数组,可以使用binarySearch方法实现二分查找
只有支持快速随机访问,二分查找才有意义。对于链表来说,二分查找的效率会自动退化为线性查找。
9.6.4 简单算法
有一些常用的简单算法,详见API文档
9.6.5 批操作
有一些成批操作数据的方法,详见API文档
9.6.6 集合与数组的转换
//可以调用List.of的方法来实现数组转换为集合
String[] vaules = ...;
var staff = new HashSet<>(List.of(values));
//但是调用toArray方法从集合转换诚数组只能得到一个Object数组
Object[] values = staff.toArray();
//而且不能使用强制转换。要完成想要的操作,只能调用它的一个变体
String[] values = staff.toArray(new Stringp[0]);
9.6.7 编写自己的算法
应该尽可能的使用接口,泛型算法,实现更高的抽象层级
9.7 遗留的集合
只是介绍,这些集合已经”老龄化“,在实际开发中尽可能的不要使用
9.7.1 Hashtable
作用同HashMap
9.7.2 枚举
Enumeration接口,hashMoreElements和nextElements接口,完全类似于Iterator的hasNext与next
可以直接利用Collection的list方法收集到ArrayList之中
9.7.3 属性映射
property map
- 键与值都是字符串
- 映射可以很容易的保存到文件以及从文件加载
- 有一个二级表存放默认值
var settings = new Properties();
settings.setProperty("width","10.0");
seetings.setProperty("filename","C//example.txt");
它可以很方便的生成或者读取配置文件。
9.7.4 栈
Stack类。
E push(E item);
//将item压入栈顶,并返回item
E pop();
//弹出并返回栈顶的item
E peek();
//返回栈顶元素,但是不弹出
9.7.5 位集
BitSet可以很方便进行位操作。