集合
java集合类库将接口与实现分离
链表
数组
ArrayList:一种可以动态增长和缩减的索引序列
LinkedList:一种可以在任何位置进行高校的插入和删除操作的有序序列
List<String> staff = new LinkedList<String>();
staff.add("a");
staff.add("b");
staff.add("c");
Iterator iter = staff.iterator();
//第一个元素
String first = (String) iter.next();
System.out.println(first);
//第二个元素
String second = (String) iter.next();
System.out.println(second);
for(String e:staff){
System.out.println(e);
}
//移除第二个元素
iter.remove();
for(String e:staff){
System.out.println(e);
}
LinkedList类的ListIterator方法返回了一个实现了ListIterator接口的迭代器对象
ListIterator<String> iter = staff.listIterator();
LinkedList.add方法将对象添加到链表得尾部(迭代器负责)
add方法在迭代器位置之前添加一个新对象
迭代器指向链表表头时,新添加得元素成为表头。迭代器越过链表得最后一个元素时,添加得元素成为列表得表尾
remove方法依赖于迭代器的状态
set方法用一个新元素取代调用next或previous方法返回的上一个元素
//用一个新值取代链表的第一个元素
ListIterator<String> iter = list.listIterator();
String oldValue = iter.next();
iter.set(newValue);
ListIterator类可以从前后两个方向遍历链表中的元素,并可以添加、删除元素
两个迭代器不能同时使用,否则会抛出concurrentmodificationexception异常
防止并发修改的异常:可以根据需要给容器附加许多的迭代器,但是这些迭代器只能读取列表。再单独附加一个既能读又能写的迭代器
检测并发修改:集合可以跟踪改写操作的次数。每个迭代器都维护一个独立的计数值,每个迭代器方法的开始处检查自己改写的计数值是否与集合的改写操作计数值一致。
set操作不视为结构性的修改
toString方法调用了所有元素的toString,contains方法检测某个元素是否出现在链表中
链表访问元素时必须从头开始(ArrayList或数组比较方便),减少列表中间插入或删除元素付出的代价
nextIndex方法返回下一次调用next方法时返回元素的整数索引
previousIndex方法返回下一次调用previous方法时返回元素的整数索引
数组列表
List接口用于描述一个有序集合,集合中的每个元素的位置十分重要。访问元素:迭代器;get或set方法随机访问。
ArrayList封装了一个动态再分配的对象数组
Vector类的所有方法都是同步的,可以两个线程安全的访问vector对象。不同步时使用ArrayList
散列集
可以快速的查找所需要的对象
散列码:由对象的实例域产生的一个整数,由hasCode方法产生
用链表数组实现,每个列表被成为桶
查找表中对象的位置:计算它的散列码,与桶的总数取余,结果就是保存这个元素的桶的索引
散列将元素随机的分散在表的各个位置上
散列冲突:桶被占满
桶数:收集具有相同散列值的桶的数目
散列表太慢就需要再散列,装填因子决定何时对散列表再散列(比如75%)
HashSet类,基于散列表的集,用add方法添加元素,contains方法查看某个元素是否存在
Set<String> words = new HashSet<String>();
long totalTime = 0;
Scanner in = new Scanner(System.in);
String word = in.next();
while(!word.equals("quit")){
long callTime = System.currentTimeMillis();
words.add(word);
callTime = System.currentTimeMillis() - callTime;
totalTime += callTime;
word = in.next();
}
Iterator<String> iter = words.iterator();
for(int i = 1; i <= 2; i++){
System.out.println(iter.next());
}
System.out.println("...");
System.out.println(words.size()+"distinct words."+totalTime+" millseconds");
树集
树集市一个有序集合,可以以任意顺序将元素插入到集合中。对集合进行遍历时,每个值将自动的按照排序后的顺序呈现
将一个元素添加到树中比添加到散列表中慢
默认情况时,树集假定插入的元素实现了comparable接口
可以通过将comparator对象传递给treeset构造器来告诉树集使用不同的比较方法
//实现Comparator接口的类
class ItemComparator implements Comparator<Item>{
public int compare(Item a,Item b){
String descrA = a.getDescription();
String descrB = b.getDescription();
return descrA.compareTo(descrB);
}
}
//将这个类的对象传递给树集的构造器
ItemComparator comp = new ItemComparator();
SortedSet<Item> sortByDescription = new TreeSet<Item>(comp);
比较器没有数据,只是比较方法的持有器,这种对象成为函数对象时匿名内部类的实例
public static void main(String[] args){
SortedSet<Item> parts = new TreeSet<Item>();
parts.add(new Item("a",12));
parts.add(new Item("b",123));
parts.add(new Item("c",1234));
System.out.println(parts);
SortedSet<Item> sortByDescription = new TreeSet<Item>(new Comparator<Item>() {
@Override
public int compare(Item a, Item b) {
String descrA = a.getDescription();
String descrB = b.getDescription();
return descrA.compareTo(descrB);
}
});
sortByDescription.addAll(parts);
System.out.println(sortByDescription);
}
}
class Item implements Comparable<Item>
{
public Item(String aDescription,int aPartNumber){
description = aDescription;
partNumber = aPartNumber;
}
public String getDescription(){
return description;
}
public String toString(){
return "[description=" + description + ",partNUmber" +"]";
}
//Objet报错
public boolean equals(Objet otherObject){
if(this == otherObject) {
return true;
}
if(otherObject == null){
return false;
}
if(getClass() != otherObject.getClass()){
return false;
}
Item other = (Item) otherObject;
return description.equals(other.description)&&partNumber == other.partNumber;
}
public int hasCode(){
return 13 * description.hasCode() + 17 * partNumber;
}
public int compareTo(Item other){
return partNumber - other.partNumber;
}
private String description;
private int partNumber;
}
队列和双端队列
Deque接口,实现双端队列。由ArrayDeque和LinkedList类实现
优先级队列
可以按照任意的顺序插入,总是按照排序的顺序进行检索
采用的数据结构为堆
堆是一个可以自我调整的二叉树,对树执行添加和删除操作,可以让最小的元素移动到根
每一个人物有一个优先级,任务以随机顺序添加到队列中,每当启动一个新的任务时,都将优先级最高得任务从队列中删除
PriorityQueue
PriorityQueue<GregorianCalendar> pd = new PriorityQueue<GregorianCalendar>();
pd.add(new GregorianCalendar(2000,Calendar.DECEMBER,9));
pd.add(new GregorianCalendar(2000,Calendar.DECEMBER,9));
pd.add(new GregorianCalendar(2000,Calendar.DECEMBER,9));
System.out.println("iterating over elements...");
for(GregorianCalendar date : pd){
System.out.println(date.get(Calendar.YEAR));
}
System.out.println("removing elements...");
while(!pd.isEmpty())
{
System.out.println(pd.remove().get(Calendar.YEAR));
}
映射表
存放键值对,知道某些键得信息,查找与之对应的元素
HashMap和TreeMap类都实现了Map接口
散列映射表对键进行散列,树映射表用键的整体顺序对元素进行排序,组织成搜索树
散列或比较函数只能作用与键,与键关联的值不能进行散列或比较
Map<String,Employee> staff = new HashMap<String,Employee>();
Employee harry = new Employee("harry hacker");
staff.put("2020",harrry);
键是唯一的,可替代
get返回键参数存储的上一个值
remove方法用于从映射表删除给定键对应的元素
size方法用于返回映射表中的元素数
KeySet是实现了Set接口的某个其它类的对象,Set接口扩展了collection接口,可以与使用任何集合一样使用keySet
public static void main(String[] args){
Map<String,Employee> staff = new HashMap<String,Employee>();
staff.put("2020",new Employee("a"));
staff.put("2019",new Employee("a"));
//打印所有输入
System.out.println(staff);
//移除一个输入
staff.remove("2020");
//替代某个键值对
staff.put("2019",new Employee("c"));
//查找值
System.out.println(staff.get("2019"));
//遍历键值对
for (Map.Entry<String,Employee> entry:staff.entrySet()){
String key = entry.getKey();
Employee value = entry.getValue();
System.out.println("key="+key+",value="+value);
}
}
}
class Employee{
public Employee(String n){
name = n;
salary = 0;
}
public String toSring(){
return "[name="+name+",salary="+salary+"]";
}
private String name;
private double salary;
}
专用集和映射表类
弱散列映射表
WeakHashMap
如果由一个值,对应的键已经不再使用了
垃圾回收器跟踪活动的对象,需要由程序负责从长期存活的映射表中删除无用的值,或者使用WeakHashMap
WeakReference对象将引用保存到另一个对象中,就是散列表键,垃圾回收器会回收没有被引用的对象和被WeakReference引用的对象,将WeakReference放入队列中。WeakHashMap删除对应条目
链接散列表和链接映射表
LinkedHashSet和LinkedHashMap,记住插入元素项的顺序
LinkedHashMap将用于访问顺序,不是插入顺序
访问顺序对于实现高速缓存的最近最少使用原则十分重要
每次调用get或put是,收到影响的条目从当前位置删除,放到条目链表的尾部
枚举集和枚举映射表
EnumSet是一个枚举类型元素集的高效实现,内部用位序列实现。如果对应的值在集中,对应的位被置为1
可以使用set接口的常用方法来修改EnumSet
EnumMap是一个键类型为枚举类型的映射表可以直接高效的用一个值数组实现
使用时,需要在构造器中指定键类型
EnumMap<Weekday,Employee> personInCharge = new EnumMap<Weekday,Employee>(Weekday.class);
标识散列映射表
IdentityHashMap,键的散列值使用System.identityHashCode方法计算,这是hasCode方法根据对象的内存地址计算散列码使用的方式。在对两个对象比较时使用==
集合框架
框架时一个类的集,包含很多超类
java集合类库构成了集合类的框架
集合有两个基本的接口,collection和map
List是一个有序集合,元素可以添加到容器中某个特定的位置,使用整数索引或使用列表迭代器
集的add方法拒绝添加重复的元素
集的equals方法定义两个集相等的条件是它们包含相同的元素但顺序不必相同
hasCode方法定义保证相同元素的集将会得到相同的散列码
抽象类的例行实现:AbstractCollection,AbstractList,AbstractSequentialList,AbstractSet,AbstractQueue,AbstractMap
集合类(扩展抽象类):LinkedList,ArrayList,ArrayDeque,HashSet,TreeSet,PriorityQueue,HashMap,TreeMap
视图和包装器
keySet方法返回一个实现了Set接口的类对象,这个类的方法对原映射表进行操作,这种集合称为视图
轻量级集包装器
子范围
不可修改的视图
视图对集合修改抛出异常,同时这个集合保持未修改的状态
可以通过集合的原始引用,对集合进行修改,仍然可以让集合的元素调用更改器的方法
视图只是包装了接口而不是实际的集合对象,只能访问接口中定义的方法
同步视图
视图机制保证常规集合的线程安全
被检验视图
对泛型类问题发生问题时提供调试机制
批操作
批操作可以避免频繁的使用迭代器
retainAll
removeAll
集合和数组之间的转换
数组转换为集合
String[] values = ...;
HashSet<String> staff = new HashSet<String>(Arrays.asList(values));
集合转换为数组
Object[] values = staff.toArray();
产生一个对象数组,返回的是Object数组,无法改变类型,必须使用另外一种toArray方法,设计为所希望的元素类型且长度为0的数组
String[] values = staff.toArray(new String[0]);
staff.toArray(new String[staff.size()]);
排序和混排
collection类的sort方法实现了list接口的集合进行排序
List<String> staff = new LinkedList<String>();
Collections.sort(staff);
Comparable<>Item itemComparator = new Comparator<Item>(){
public int compare(Item a,Item b){
return a.partNumber-b.partNumber;
}
}
Collection.sort(items,itemComparator);
降序排序
Collection.sort(staff,Collection.reverseOrder());
java直接将所有的元素转入一个数组,并使用一种归并排序的变体对数组进行排序,再将排序后的序列复制回列表
//定义数组
List<Integer> numbers = new ArrayList<Integer>();
//填充数组
for(int i = 1;i <= 49;i++){
numbers.add(i);
}
//随机打乱列表
Collections.shuffle(numbers);
//选取列表的前6个值
List<Integer> winningCombination = numbers.subList(0,6);
//将选择的数值进行排序和打印
Collections.sort(winningCombination);
System.out.println(winningCombination);
二分查找
集合必须是排号序的
i = Collections.binarySearch(c,element);
i = Collections.binarySearch(c,element,comparator);
返回值大于等于0,标识匹配对象的索引,返回负值,表示没有匹配的元素,返回-i,插入的位置应该为-i-1
集合类:Hashtable类,子类properties,vector的子类,子类stack,BitSet类
Enumeration<Employee> e = staff.elements();
while(e.hasMoreElements()){
Employee e = e.nextElement();
}
//计算2-2000之间的素数
int n = 2000;
//计算时间
long start = System.currentTimeMillis();
BitSet b = new BitSet(n+1);
int count = 0;
int i;
for(i = 2;i <= n;i++){
b.set(i);
}
i = 2;
while(i * i <= n){
if(b.get(i)){
count++;
int k = 2*i;
while(k <= n){
b.clear(k);
k += i;
}
}
i++;
}
while(i <= n){
if(b.get(i)){
count++;
}
i++;
}
long end = System.currentTimeMillis();
System.out.println(count+"primes");
System.out.println((end-start)+" millseconds");
}