您的位置:首页 > 编程语言 > Java开发

Java集合 Java核心技术读书笔记

2014-03-17 18:02 447 查看
1.善用Collection接口,它是集合的底层。

2.迭代器

public interface Iterator<E>{
E next();
boolean hasNext();
void remove();
}
善用迭代器

Collection<String> c = ...;
Iterator<String> iter = c.iterator();
while(iter.hasNext()){
String element = iter.next();
do something with element
}


还有“for each”循环:

for(String element:c){
do something with element
}
3.删除元素

Iterator接口的remove方法将会删除上次调用next方法时返回的元素。如删除字符串集合中的第一个元素:

Iterator<String> iter = c.iterator();
iter.next();
iter.remove();
注意:对next方法和remvoe方法的调用具有互相依赖性。如果调用remvoe之前没有调用next将是不合法的,会抛出IlleStateException异常。

如果想删除两个相邻的元素,不能直接这样调用:

iter.remove();

iter.remove();

必须先调用next越过将要删除的元素。

iter.next();

iter.remove();

iter.next();

iter.remove();

4.善用collection接口提供的方法,如:

int size();

boolean isEmpty();

boolean contains(Object o);

boolean containsAll(Collection<?> c);

boolean equals(Object o);

boolean addAll(Collection<? extends E> c);

boolean remove(Object o);

boolean removeAll(Collection<?> c);

void clear();

boolean retainAll(Collection<?> c);

Object[] toArray();

<T> T[] toArray(T[] a);

5.以Map结尾的类实现了Map接口,其他类则实现了Collection接口

6.链表

数组和数组列表有一个重大缺陷:从中间位置删除或插入一个元素要付出很大的代价,而链表解决了这个问题。在Java程序设计语言中,所有的链表都是双向链接的,即每个结点还存放着指向前驱结点的引用。

List<String> staff = new LinkedList<String>();
staff.add("Amy");
staff.add("Bob");
staff.add("Carl");
staff.remove("Amy");
可以使用ListIterator向.链表新增元素:

List<String> staff = new LinkedList<String>();
staff.add("Amy");
staff.add("Bob");
staff.add("Carl");
ListIterator<String> iter = staff.listIterator();
iter.next();
iter.add("Juliet");
iter.next();
iter.remove();
使用“光标”进行元素的增删时一定要注意“光标”位置

注意:如果在某个迭代器修改集合时,另一个迭代器对其进行遍历,会出现混乱状况,将抛出 ConcurrentModificationException异常。如

List<String> staff = ... ;
Iterator<String> iter1 = staff.iterator();
Iterator<String> iter2 = staff.iterator();
iter1.next();
iter1.remove();
iter2.next();	// 抛出ConcurrentModificationException异常
可以根据需要同时给窗口附加许多迭代器,但这些迭代器只能读取列表。另外,单独附加一个既能读又能写的迭代器。

LinkedList类提供了用来访问某个特定元素的get方法:

LinkedList<String> list = ...;

String obj = list.get(n);

但这种方法效率并不太高,如果发现自己正在使用这个方法,说明 有可能对于所要解决的问题使用了错误的数据结构。

绝对不能使用下面这种方法遍历链表,它的效率极低。

for(int i = 0; i < list.size(); i++)

do something with list.get(i);

优先使用链表,但避免使用以整数索引表示链表中位置的所有方法。如果需要对集合进行随机访问,就使用数组或ArrayList,而不要使用链表。

7.散列集Set

使用散列集存放自定义的类时,就是要负责实现这个类的hashCode方法。注意:自己实现的hashCode要与equals方法兼容,即如果a.equals(b)为true,a与b必须具有相同 的散列码。

8.树集

TreeSet类是 一个有序集合。每次将一个元素添加到树中,都被放置在正确的排序位置上。因此迭代器总是以排好序的顺序访问每个元素。(当前实现使用的红黑树)

将一个元素添加到树中要比添加到散列表中慢,但是,与将元素添加到数组或链表的正确位置上相比还是快很多。

可以将Comparator对象传递给TreeSet构造器来告诉树集使用的比较方法。如:

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);
注意:Comparator这个比较器没有任何数据。它只是比较方法的持有器。有时将这种对象称为函数对象。函数对象通常被定义为“瞬时的”,那使用匿名内部类实现。

SortedSet<Item> sortByDescription = new TreeSet<Item>(new
Comparator<Item>{
public int compare(Item a,Item b){
String descrA = a.getDescription();
String descrB = b.getDescription();
return descrA.compareTo(descrB);
}
});


9.优先级队列并没有对所有的元素进行排序,而是使用了堆。堆是一个可以自我调整的二叉树,对树执行添加和删除操作,可以让最小的元素移动到根,而不必花费时间对元素进行排序。

10.映射表Map有三个视图:

Set<K> keySet()

Collection<K> values()

set<Map.Entry<K,V>> entrySet()

注意:keySet既不是HashSet,也不是TreeSet,而是实现了Set接口的某个其他类。Set接口扩展了Collection接口,因此可以与使用任何集合一样使用keySet。

可以调用迭代器的remove方法从眏射表中删除键以及对应的值。但是不能将元素添加到键集的视图中。如:

Set<String> keys = map.keySet();
Iterator<String> iter = keys.iterator();
iter.next();
iter.remove();


12.专用集与映射表类

(1)弱散列映射表

垃圾回收器中活动的对象。只要眏射表对象是活动的,其中的所有桶也是活动的,它们不能被回收。因此,需要由程序负责从长期存活的眏射表中删除那些无用的值。但也可以使用WeakHashMap来完成这件事情。

13.视图与包装器

1)Arrays类的静态方法asList:

Card[] cardDeck = new Card[52];

List<Card> card = Arrays.asList(cardDeck );

注意:方法返回的对象不是ArrayList,而是一个视图对象,带有访问底层数组的get和set方法。改变数组大小的所有方法(如迭代器的add和remove方法)都会抛出一个UnsupportedOperationException异常。

从Java SE5.0开始,可以直接将各个元素传递给这个方法。

List<String> names = Arrays.asList("Amy","Bob","Carl");

将返回一个实现了List接口不修改的对象。

2)可以为很多集合建立子范围。假如有一个列表staff,使用:List group2 = staff.subList(10,20)从中取出了第10个~第19个元素,第一个索引包含在内,第二个索引不包含在内,与String类的substring操作中的参数情况相同。

对子范围的所有操作都会自动反映到整个列表。如:group2.clear();则元素自动从staff列表中清除,并且group2为空。

对于有序集和映射表,可以使用排序顺序而不是元素位置建立子范围。SortedSet接口声明3个方法:

SortedSet<E> subSet(E from,E to);

SortedSet<E> headSet(E to);

SortedSet<E> tailSet(E from)

这些方法将返回大于等于from且小于to的所有元素子集。有序映射表可以使用:

SortedMap<K,V> subMap(K from,K to);

SortedMap<K,V> headMap(K to);

SortedMap<K,V> tailMap(K from);

Java SE6 引入 NavigableSet接口使子范围可以指定是否包括边界:

NavigableSet<E> subSet(E from,boolean fromInclusive,E to,boolean toInclusive);

NavigableSet<E>headSet(E to,boolean toInclusive);

NavigableSet<E> tailSet(E from,boolean fromInclusive);

3)不可修改的视图

可以使用下面6种方法获得不可修改视图:

Collections.unmodifiableCollection

Collections.unmodifiableList

Collections.unmodifiableSet

Collections.unmodifiableSortedSet

Collections.unmodifiableMap

Collections.unmodifiableSortedMap

不可修改视图对现在的集合增加了一个运行时的检查。如果发现试图对集合进行修改,就抛出一个异常,同时这个集合将保持未修改的状态。如下:

List<String> unmodi = Collections.unmodifiableList(list);
unmodi.add("test");


将抛出一个UnsupportedOperationException异常。

4)注意:unmodifiableCollection返回一个集合,它的equals方法不调用底层集合的equals方法,它继承了Object的equals方法,这个方法只是检测两个对象是否为同一个对象,hashCode方法也是一样的处理方法。但unmodifiableList类和unmodifiableSet类却使用底层集合的equals方法和hasCode方法。

List<String> list = new LinkedList<String>();
List<String> list2 = new LinkedList<String>();
list2.add("test");
list.add("test");
System.out.println(list2.equals(list));	// true

Collection<String>  colle = Collections.unmodifiableCollection(list);
Collection<String>  colle2 = Collections.unmodifiableCollection(list2);
System.out.println(colle.equals(colle2));	//false
System.out.println(colle.hashCode() + "   " + colle2.hashCode());	//两个code不相同

List<String> unmodiList = Collections.unmodifiableList(list);
List<String> unmodiList2 = Collections.unmodifiableList(list2);
System.out.println(unmodiList.equals(unmodiList2));	//true
System.out.println(unmodiList.hashCode() + "   " + unmodiList2.hashCode());	//两个code相同
4)同步视图

如果由多个线程访问集合,就必须确保集不会被意外地破坏。例如,如果一个线程试图将元素添加到散列表中,同时另一个线程正在对散列表进行再散列,其结果将是灾难性的。

类库的设计者使用视图机制来确保常规集合的线程安全,而不是实现线程安全的集合类。如:Collections类的静态方法synchronizedMap可以将任何一个眏射表转换成具有同步访问方法的Map:

Map<String,Employee> map = Collections.synchronizedMap(new HashMap<String,Employee>());

现在就可以由多线程访问map对象了。像get和put这类方法都是串行操作的,即一个方法必须调用完成,才允许另一个线程调用另一个方法。

5)检测视图

Java SE 5.0 增加了一组“被检验”视图,用来对泛型类型发生问题时提供调试支持。

ArrayList<String> strings = new ArrayList<String>();
ArrayList rawList = strings;	//编译警告,但不报错
rawList.add(new Date());	//编译警告,但一样不报错

String date = (String) rawList.get(0);	//报错


上面代码中的add命令在运行时检测不到,只有在调用get方法并将结果转化为String时才抛出异常。而被检测视图可以探测到这类问题:

ArrayList<String> strings = new ArrayList<String>();
List<String> safeStrings = Collections.checkedList(strings, String.class);
List rawList = safeStrings;
rawList.add(new Date());	//抛出异常java.lang.ClassCastException


注意:被检测视图受限于虚假机可以运行的运行时检查。例如,对于ArrayList<Pari<String>>,由于虚拟机有一个单独的“原始‘Pair类,所以,无法阻止插入Pair<Date>

14.集合与数组之间的转换

可以使用Arrays.asList将一个数组转换为集合,如

String[] values = ... ;

HashSet<String> staff = new HashSet<String>(Arrays.asList(values));

可以通过以以下方法将集合转换为数组 :

String[] values = staff.toArray(new String[staff.size()]);

15.算法

Collections类中的方法sort方法可以实现对List接口的集合进行排序。

List<String> staff = new LinkedList<String>();

Collections.sort(staff);

可以将Comparas对象作为第二个参数传递给sort方法。

sort方法使用的归并排序。

传递给排序算法的集合必须满足:

列表支持set方法,则是可修改的。

列表支持add和remove方法,则是可改变大小的

Collections类中的shuffle算法可以随机地混排列表中的元素。

ArrayList<Card> cards = ... ;

Collections.shuffle(cards);

只有可以随机访问的容器,如ArrayList才使用二分查找。如果必须利用迭代方式一次次地遍历链表的一半元素找到中间位置的元素,二分查找就完全失去了优势了。因此,如果为binarySearch算法提供一个链表,它将自动变为线性查找。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: