(九)集合
1. 如果对ArrayList进行迭代,迭代器将从索引0开始,每迭代一次,索引值+1,如果访问HashSet中的元素,每个元素将会按照某种随机的次序出现。虽然可以确定在得带过程中能够遍历到集合中的所有元素,但却无法预知元素访问的次序。
2. Java库中的具体集合
| 集合类型 | 描述 | 缺点 | 
| Arraylist | 一种可以动态增长和缩减的索引序列 | 数组和数组列表中间位置删除一个元素需要付出很大的代价,原因是数组中处于被删除元素之后的所有元素都要向数组的前端移动,增加一个元素之后,所有的元素都要向数组的后端移动 | 
| LinkedList | 一种可以在任何位置进行高效的插入和删除操作的有序序列 | |
| ArrayDeque | 一种用循环数组实现的双端队列 | |
| HashSet | 一种没有重复元素的无序集合 | |
| TreeSet | 一种有序集合 | |
| EnumSet | 一种包含枚举类型值得集合 | |
| LinkedHashSet | 一种可以记住插入次序的集合 | |
| PriorityQuene | 一种允许高效删除最小元素的集合 | |
| HashMap | 一种存储键值关联的数据结构 | |
| TreeMap | 一种键值有序排列的映射表 | |
| EnumMap | 一种键值属于枚举类型的映射表 | |
| LinkedHashMap | 一种可以记住键值项添加次序的映射表 | |
| WeakHashMap | 一种其值无用武之地后可以被垃圾回收器回收的映射表 | |
| IdentityHashMap | 一种用==而不是equals比较键值的映射表 | 
3. 在Java设计语言中,所有的链表实际上都是双向链表,即每个结点还存放着指向前驱结点的引用
4. 在使用ListIterator的游标功能的时候,需要特别注意,在调用next方法之后,remove方法删除的是迭代器左侧的元素,如果调用previous方法之后,remove方法删除的是迭代器右侧的元素,并且不能在同一行中调用两次remove。而add方法只依赖于迭代器的位置,remove方法依赖于迭代器的状态
5. 如果在某个迭代器修改集合时,另一个迭代器对其进行遍历,一定会出现混乱的状态
6. 链表使用举例
- 
import java.util.*; public class LinkedListTest{ public static void main(String[] args) { //创建链表aList List<String> aList = new LinkedList<>(); aList.add("aaa"); aList.add("bbb"); aList.add("ccc"); //创建链表bList List<String> bList = new LinkedList<>(); bList.add("ddd"); bList.add("eee"); bList.add("fff"); bList.add("ggg"); //创建迭代器 aIter和迭代器bIter ListIterator<String> aIter = aList.listIterator(); Iterator<String> bIter = bList.iterator(); //每隔一个aList中的元素添加bList中的元素 while(bIter.hasNext()){ if(aIter.hasNext()) aIter.next(); aIter.add(bIter.next()); } //打印间隔插入b元素的aList链表 System.out.println(aList); //这一步很重要,迭代器的游标能够记住位置信息,需要从头操作链表的时候就需要归位游标 bIter = bList.iterator(); //间隔删除bList链表中的元素 while(bIter.hasNext()){ bIter.next(); if(bIter.hasNext()){ bIter.next(); bIter.remove(); } } //打印bList System.out.println(bList); //从aList中删除bList中的元素 aList.removeAll(bList); //打印删除后的链表a System.out.println(aList); } }  
7. List接口用于描述一个有序的集合,并且集合中每个元素的位置十分重要。有两种访问元素的协议:一种是用迭代器,另一种是用get和set方法随机的访问每个元素。后者不适用于链表,但对数组却很有用。集合类库提供了一个大家熟悉的Arrayist类,这个类也实现了List接口,ArrayList封装了一个动态再分配的对象数组。
8. 使用动态数组时,可以使用Vector类,他的所有方法都是同步的,可以由两个以上的线程安全的访问同一个Vector对象,但是如果由一个线程访问Vector对象,代码在同步操作上要耗费大量的时间。ArrayList不是同步的,因此在不需要同步的时候使用ArrayList,而不要使用Vector。
9. 散列表:HashTable:为每一个对象计算一个整数,称为散列码。
- 散列码是由对象的实例域产生的一个整数。
- 具有不同数据域的对象将产生不同的不同的散列码
- 如果自定义类,就要负责实现这个类的hashCode方法,自己实现的hashCode应该与equals方法兼容
- Java中散列表用链表数组实现,每个列表被称为桶。
- 要想查找表中对象的位置,就要先计算出他的散列码,然后与桶的总数取余,所得到的结果就是保存这个元素的桶的索引
- 如果这个桶中含有其他元素,这种现象被称为散列冲突,这时需要用新对象与桶中的所有对象进行比较,查看这个对象是否已经存在
- 在更改集中的元素时要格外小心,如果元素的散列码发生了改变,元素在数据结构中的位置也会发生变化
- 散列表可以用于实现几个重要的数据结构,其中最简单的就是set类型,set是没有重复元素的集合
- HashSet中的contains方法已经被重新定义,用来快速查看是否某个元素已经出现在集中,他只在某个桶中查找元素,而不必查看集合中的所有元素
10. 下面的示例是使用散列集HashSet存储txt文件中的单词,并计算时间花销,从打印结果看,set集合完全没有顺序
- 
import java.util.*; public class HashSetTest{ public static void main(String[] args) { Set<String> set = new HashSet<>(); long totalTime = 0; Scanner sc = new Scanner(System.in); while(sc.hasNext()){ Long callTime = System.currentTimeMillis(); set.add(sc.next()); callTime = System.currentTimeMillis() - callTime; totalTime = totalTime + callTime; } Iterator<String> iter = set.iterator(); for(int i = 0; i <= 20 && iter.hasNext(); i++){ System.out.println(iter.next()); } System.out.println("..."); System.out.println(set.size() + "words" + totalTime + "milliseconds"); } }  
11. 树集TreeSet与散列集HashSet十分类似。
- 但是树集是一个有序集合,可以以任意顺序将元素插入到集合中,在对集合进行遍历时,每个值将自动的按照排序后的顺序呈现。
- 排序过程是自动进行的,排序是用树结构完成的,使用的是红黑树。
- 每次将一个元素添加到树中时,都被放置在正确的排序位置上
- 将一个元素添加到树中比添加到散列表中慢,因为要排序嘛。但是仍比将元素添加到数组或者链表的正确位置上还是快很多
- 如果要将自定义的对象,就必须通过实现Comparable接口,并实现其中的comparaTo方法来自定义排列顺序
- 树的排序必须是全排序,也就是说任意两个元素必须是可比的
12. 示例程序创建了两个Item对象的树集,第一个按照部件编号排序,这个是Item对象的默认排序,第二个通过使用一个定制的比较器来按照描述信息排序
- 
import java.util.*; /** *自定义对象进行排序,需要实现Comparable接口 */ public class Item implements Comparable<Item>{ //实例域 private String description; private int partNumber; //默认的构造方法 public Item(){ } //自定义的构造方法 public Item(String description, int partNumber){ this.description = description; this.partNumber = partNumber; } //get方法 public String getDescription(){ return this.description; } //重写toString方法 public String toString(){ return "[description="+description+",partNumber="+partNumber+"]"; } //重写equals方法 public boolean equals(Object otherObject){ if(this == otherObject) return true; if(otherObject == null) return false; if(this.getClass() != otherObject.getClass()) return false; Item other = (Item) otherObject; return Objects.equals(this.description,other.getDescription()) && this.partNumber == other.partNumber; } //重写hashCode方法,根据对象不同的实例域计算hash值 public int hashCode(){ return Objects.hash(description,partNumber); } //重写compareTo方法,实现Compareable接口必须重写这个方法 public int compareTo(Item other){ return Integer.compare(this.partNumber,other.partNumber); } } import java.util.*; public class TreeSetTest{ public static void main(String[] args) { SortedSet<Item> parts = new TreeSet<>(); parts.add(new Item("Toaster",1234)); parts.add(new Item("Widget",5678)); parts.add(new Item("Modem",9999)); System.out.println(parts); SortedSet<Item> sortByDescrition = new TreeSet<>(new Comparator<Item>(){ public int compare(Item a, Item b){ String descrA = a.getDescription(); String descrB = b.getDescription(); return descrA.compareTo(descrB); } }); sortByDescrition.addAll(parts); System.out.println(sortByDescrition); } } 

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号