集合
*视图与包装器 和 批操作 两节没怎么看
1.Collection 接口 中至少包含add和iterator方法,使用add方法时不允许添加已经存在的对象(添加失败时add方法会返回false,否则返回true)。Iterator接口中包含next,hasNext, remove 方法。
2.实现了 Iterable接口 的集合才可以使用for each进行遍历。而Collection接口扩展了Iterable接口,所以所有的标准类库中的集合都可以使用for each来进行遍历
3.应该认为Java的迭代器是位于两个元素之间的位置。每次调用next方法,就越过一个元素,next方法会返回刚刚越过的那个元素。
4.Iterator的next方法和remove方法是相互依赖的。在调用remove方法之前必须先调用next方法。如:
Iterator<String> it = c.iterator(); //c在这里是一个集合
it.next();
it.remove(); //此时删掉了刚刚越过的那个元素
it.next(); //如果想要删掉两个相邻的元素,也必须两次调用next方法
it.remove();
5.AbstractCollection实现了更多常用的方法,如contains方法(判断依据是equals,不是==)。
6.标准库中,以Map结尾的集合实现的是Map接口,而不是Collection接口。
7.具体的集合
(1)链表
[1]Java中的链表(LinkedList)都是双向链表,每个结点存放着前一个和后一个结点的引用。
[2]链表与泛型集合的一个重要区别是链表是一个有序集合,每个对象的位置十分重要。
[3]依赖于位置的add方法(比如添加一个元素到某个具体的位置)由迭代器进行负责。插入时,迭代器位于哪个位置,就会将元素插入到哪个位置。
[4]上面提到的add方法存在于ListIterator接口中,这个接口中还包含previous和hasPrevious方法,用来反向遍历链表。
[5]在调用previous方法之后也可以调用Iterator的remove方法,删除的是刚刚越过的那个结点。
[6]ListIterator中的add方法依赖于迭代器的位置,Iterator中的remove方法依赖于迭代器的状态。
[7]一个迭代器在访问集合时,如果发现集合被另一个迭代器修改了,就会抛出一个ConcurrentModificationException异常。但是仅限于添加、删除元素的 结构性修改,即如果使用set修改则不会抛出异常
[8]LinkedList类提供了一个用于访问某个特定元素的get方法,但是应该避免使用这个方法。同时也要避免使用以整数索引表示链表中位置的所有方法。(想要使用整数索引来访问的话,完全可以用ArrayList,没必要用LinkedList)
[9]ListIterator中还包含nextIndex和previousIndex方法用来返回迭代器所在位置的下一个元素的索引和上一个元素的索引(注意迭代器是位于元素之间的)
[10]list.ListIterator(n)将返回一个迭代器,这个迭代器指向索引为n的元素前面(左边)的位置。但是效率比较低。
(2)数组列表(ArrayList):在不需要同步时使用ArrayList类,需要同步时(如两个线程安全的访问同一个对象)使用Vector类。
(3)散列集:使用散列码来查找集合中的元素,但是无法控制元素出现的顺序。
[1]使用数组来进行存储,数组中的每个元素(称为桶)又是一个链表,链表(桶)用来存放散列值相同(冲突)的元素。
[2]set类型(如HashSet)就是使用散列表实现的。其中的add方法会先检查是否已经存在该对象。
[3]HashSet的contains方法已经被重写,不必每个元素都查看,可以根据散列码直接定位到对应的桶。(但是还是使用equals判断是否已经存在)
(4)树集(TreeSet):可以对添加到里面的元素自动进行排序。
[1]将一个元素添加到树集中,要比添加到散列集中慢,但是比添加到ArrayList或者LinkedList中要快。
[2]一般情况下,实现Comparable接口使用compareTo方法比较对象之间的大小。但是,有时,比如两组TreeSet对象的比较标准不一样(当然两组是各自比较各自的),就不能使用Comparable了。这时,可以使用Comparator接口,将其传递给TreeSet的构造器,如:
TreeSet<Item> itemTreeSet = new TreeSet<>( new Comparator<Item>(){ //只是作为函数对象(没有任何数据,只是作为比较方法的持有器),一般使用匿名内部类
public int compare(Item a, Item b){
String descrA = a.getDescription();
Stirng descrB = b.getDescription();
return descrA.compareTo(descrB);
}
});
(5)队列(Queue)(尾部添加,头部删除)与双端队列(头部尾部都可以添加删除):ArrayDeque(循环数组队列)和LinkedList(链表队列)实现了Deque接口,且都提供了双端队列。(deque的意思就是双端队列)
(6)优先级队列(PriorityQueue):使用堆实现。与TreeSet一样,既可以保存实现了Comparable接口的类对象,也可以保存在构造器中提供比较器的对象。
注意:优先级队列不一定保证所有元素都是按序排列的,但是它能够保证队列的头一定是最小的元素(调用PriorityQueue的remove方法时删除的一定是最小的),这是TreeSet和PriorityQueue的区别
(7)映射表(Map)
[1]HashMap和TreeMap实现了Map接口,在需要排序时使用TreeMap,不需要排序时使用HashMap。
[2]散列或者比较函数只能作用于键。
[3]put函数用来向map中添加键值对,如果对同一个键两次调用put方法,第二个值就会取代第一个值,而put函数会返回第一个值。
[4]三个视图:Set keySet()(既不是HashSet,也不是TreeSet) , Collection values() , Set
for(Map.Entry<String, Employee> entry : staff.entrySet()) //staff是一个HashMap对象
{
String key = entry.getKey();
Employee value = entry.getValue();
...
}
(8)专用集与映射表集
[1]如果程序中对某个键的最后一次引用已经消亡,垃圾回收器此时却不能够回收该对象。(因为映射表对象是活动的,所以其中的所有桶也都是活动的,垃圾回收器不能回收活动的对象)WeakHashMap就是用来解决这一问题。
[2]LinkedHashSet和LinkedHashMap,使用链表将各个桶中的元素链接起来,最近一次操作的元素将被放在链表的尾部,也就是说,是按照元素的访问次序来对元素进行排序。(对于最近最少使用原则非常有用,比如内存满了,可以直接将头部的元素删掉,然后添加新的元素)
[3]IdentityHashMap,键的散列值不使用hashCode方法,而是使用System.identityHashCode方法计算的,即根据对象的内存地址来计算散列码。而且,在对对象进行比较时,使用==,而不是equals
8.(1)将数组转化为一个集合:
String[] values = …;
HashSet staff = new HashSet<>(Arrays.asList(values)); //Arrays.asList方法返回一个包装了普通数组的List包装器
(2)将集合转为一个数组:
String[] values = (String[])staff.toArray(); //将会出错,因为toArray方法返回的是一个重新构造的Object类型的数组,无法转换为String类型
可以使用另一个toArray方法:
String[]values = (String[])staff.toArray(new String[0]); //正确。这个toArray方法同样也是返回一个Object类型的数组,但是在构造时是根据toArray方法参数的类型构造的,所以可以进行转化
9.**Collection中包含静态方法**sort和shuffle,还有binarySearch方法(先检查要排序的集合是否实现了RandomAccess接口,如果实现了就采用二分查找,否则采用线性查找。另外,如果查找不到某个元素,方法将返回一个负值,这种情况下应该将键插入到-i-1的位置)
10.遗留的集合
(1)Hashtable(小写t)与HashMap的作用一样,他们拥有相同的接口。Hashtable和Vector的方法都是同步的,HashMap则不是。如果对同步性和遗留代码没有任何要求,那么就使用HashMap。
(2)Enumeration接口,包含两个方法hasMoreElements和nextElement。另外,静态方法Collection.enumeration方法将产生一个枚举对象。
Enumeration<Employee> e = staff.elements();
while(e.hasMoreElements())
{
Employee ee = e.nextElement();
...
}
(3)属性映射表(Properties类实现了Java的属性映射表):[1]通常用于程序的特殊配置选项。[2]键和值都是字符串。[3]表可以保存到文件中,也可以从文件中加载。[4]使用一个默认的辅助表。
(4)Stack类包含pop和push方法。但是Stack类扩展为Vector类,可以让栈使用不属于栈操作的insert和remove方法(这点并不让人满意)。
(5)位集(BitSet):用于存放一个位序列,要比使用Boolean对象的ArrayList更加高效。get方法获取某一位的状态,set方法将某一位设置为true,clear方法将某一位设置为false