collectionAndMap
来源:互联网 发布:学校机房监控软件 编辑:程序博客网 时间:2024/06/05 11:55
一、 Introduction
在程序中,数据是基本要素。根据不同的数据形式和数据结构,编程语言提供了不同的存放数据的方式。在java中,一般,单个数据,使用变量保存;多个同类型的数据,使用数组,且需要知道数组的长度;如果,不知道数据的长度,且需要存放多个引用类型的数据,则使用集合。
集合存放对象的引用,集合中的对象就是指集合中对象的引用。集合类型主要有3种:set、list和map。接口Set和list都继承自collection接口。Map是一个单独的接口。
二、 Collection
1. JDKAPI 1.6.0
java.util
接口 Collection<E>
所有超级接口:
Iterable<E>
所有已知子接口:
BeanContext,BeanContextServices,BlockingDeque<E>,BlockingQueue<E>,Deque<E>, List<E>, NavigableSet<E>,Queue<E>, Set<E>, SortedSet<E>
所有已知实现类:
AbstractCollection,AbstractList, AbstractQueue, AbstractSequentialList,AbstractSet, ArrayBlockingQueue, ArrayDeque,ArrayList, AttributeList, BeanContextServicesSupport, BeanContextSupport, ConcurrentLinkedQueue, ConcurrentSkipListSet, CopyOnWriteArrayList, CopyOnWriteArraySet, DelayQueue, EnumSet, HashSet, JobStateReasons, LinkedBlockingDeque, LinkedBlockingQueue, LinkedHashSet,LinkedList, PriorityBlockingQueue, PriorityQueue,RoleList, RoleUnresolvedList, Stack,SynchronousQueue,TreeSet, Vector
public interface Collection<E>
extends Iterable<E>
Collection 层次结构 中的根接口。Collection 表示一组对象,这些对象也称为collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的。JDK 不提供此接口的任何直接实现:它提供更具体的子接口(如 Set 和 List)实现。此接口通常用来传递 collection,并在需要最大普遍性的地方操作这些 collection。
包 (bag) 或多集合 (multiset)(可能包含重复元素的无序 collection)应该直接实现此接口。
所有通用的 Collection 实现类(通常通过它的一个子接口间接实现 Collection)应该提供两个“标准”构造方法:一个是 void(无参数)构造方法,用于创建空 collection;另一个是带有 Collection 类型单参数的构造方法,用于创建一个具有与其参数相同元素新的 collection。实际上,后者允许用户复制任何 collection,以生成所需实现类型的一个等效 collection。尽管无法强制执行此约定(因为接口不能包含构造方法),但是Java 平台库中所有通用的 Collection 实现都遵从它。
此接口中包含的“破坏性”方法,是指可修改其所操作的 collection 的那些方法,如果此 collection 不支持该操作,则指定这些方法抛出 UnsupportedOperationException。如果是这样,那么在调用对该collection 无效时,这些方法可能,但并不一定抛出UnsupportedOperationException。例如,如果要添加的 collection 为空且不可修改,则对该 collection 调用addAll(Collection)方法时,可能但并不一定抛出异常。
一些 collection 实现对它们可能包含的元素有所限制。例如,某些实现禁止 null 元素,而某些实现则对元素的类型有限制。试图添加不合格的元素将抛出一个未经检查的异常,通常是 NullPointerException 或 ClassCastException。试图查询是否存在不合格的元素可能抛出一个异常,或者只是简单地返回 false;某些实现将表现出前一种行为,而某些实现则表现后一种。较为常见的是,试图对某个不合格的元素执行操作且该操作的完成不会导致将不合格的元素插入 collection 中,将可能抛出一个异常,也可能操作成功,这取决于实现本身。这样的异常在此接口的规范中标记为“可选”。
由每个 collection 来确定其自身的同步策略。在没有实现的强烈保证的情况下,调用由另一进程正在更改的 collection 的方法可能会出现不确定行为;这包括直接调用,将collection 传递给可能执行调用的方法,以及使用现有迭代器检查 collection。
Collections Framework 接口中的很多方法是根据 equals方法定义的。例如,contains(Objecto) 方法的规范声明:“当且仅当此collection 包含至少一个满足 (o==null ? e==null :o.equals(e)) 的元素 e 时,返回 true。”不应将此规范理解为它暗指调用具有非空参数 o 的Collection.contains 方法会导致为任意的 e 元素调用 o.equals(e) 方法。可随意对各种实现执行优化,只要避免调用 equals 即可,例如,通过首先比较两个元素的哈希码。(Object.hashCode()规范保证哈希码不相等的两个对象不会相等)。较为常见的是,各种 Collections Framework 接口的实现可随意利用底层Object 方法的指定行为,而不管实现程序认为它是否合适。
此接口是 JavaCollections Framework 的一个成员。
从以下版本开始:
1.2
另请参见:
Set, List, Map, SortedSet, SortedMap, HashSet, TreeSet, ArrayList, LinkedList, Vector, Collections, Arrays, AbstractCollection
2. Methods
boolean
add(E e)
确保此 collection 包含指定的元素(可选操作)。
boolean
addAll(Collection<? extendsE> c)
将指定 collection 中的所有元素都添加到此 collection 中(可选操作)。
void
clear()
移除此 collection 中的所有元素(可选操作)。
boolean
contains(Object o)
如果此 collection 包含指定的元素,则返回 true。
boolean
containsAll(Collection<?> c)
如果此 collection 包含指定 collection 中的所有元素,则返回 true。
boolean
equals(Object o)
比较此 collection 与指定对象是否相等。
int
hashCode()
返回此 collection 的哈希码值。
boolean
isEmpty()
如果此 collection 不包含元素,则返回 true。
Iterator<E>
iterator()
返回在此 collection 的元素上进行迭代的迭代器。
boolean
remove(Object o)
从此 collection 中移除指定元素的单个实例,如果存在的话(可选操作)。
boolean
removeAll(Collection<?> c)
移除此 collection 中那些也包含在指定 collection 中的所有元素(可选操作)。
boolean
retainAll(Collection<?> c)
仅保留此 collection 中那些也包含在指定 collection 的元素(可选操作)。
int
size()
返回此 collection 中的元素数。
Object[]
toArray()
返回包含此 collection 中所有元素的数组。
<T> T[]
toArray(T[] a)
返回包含此 collection 中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同。
3. 例子
boolean
add(E e)
确保此 collection 包含指定的元素(可选操作)。
public voidtest1(){
Collection c1=newArrayList();
c1.add(new Object());
c1.add("嘎嘎");
c1.add(12);
System.out.println(c1);
}
boolean
addAll(Collection<? extendsE> c)
将指定 collection 中的所有元素都添加到此 collection 中(可选操作)。
public voidtest1(){
Collection c1=new ArrayList();
c1.add(newObject());
c1.add("嘎嘎");
c1.add(12);
System.out.println(c1);
Collection c2=new ArrayList();
c2.addAll(c1);
c2.add("hah");
System.out.println(c2);
}
boolean
remove(Object o)
从此 collection 中移除指定元素的单个实例,如果存在的话(可选操作)。
public voidtest1(){
Collection c1=new ArrayList();
c1.add(newObject());
c1.add("嘎嘎");
c1.add(12);
System.out.println(c1);
Collection c2=new ArrayList();
c2.addAll(c1);
c2.add("hah");
System.out.println(c2);
c2.remove("嘎嘎");
System.out.println(c2);
}
boolean
removeAll(Collection<?> c)
移除此 collection 中那些也包含在指定 collection 中的所有元素(可选操作)。
public voidtest1(){
Collection c1=new ArrayList();
c1.add(newObject());
c1.add("嘎嘎");
c1.add(12);
c1.add("hk");
System.out.println(c1);
Collection c2=new ArrayList();
c2.add("hah");
c2.add("嘎嘎");
c2.add(12);
c2.remove("嘎嘎");
c2.removeAll(c1);
System.out.println(c2);
}
boolean
retainAll(Collection<?> c)
仅保留此 collection 中那些也包含在指定 collection 的元素(可选操作)。
public voidtest2(){
Collection c1=new ArrayList();
c1.add(newObject());
c1.add("嘎嘎");
c1.add(12);
c1.add("hk");
Collection c2=new ArrayList();
c2.add("hah");
c2.add("嘎嘎");
c2.add(12);
c2.retainAll(c1);
System.out.println(c2);
//使用retainAll方法,可以清空一个集合,效果同clear方法。
Collection c3=new ArrayList();
c2.retainAll(c3);
//c2.clear();
System.out.println(c2);
}
boolean
isEmpty()
如果此 collection 不包含元素,则返回 true。
public voidtest3(){
Collection c1=new ArrayList();
c1.add(newObject());
c1.add("嘎嘎");
c1.add(12);
c1.add("hk");
Collection c2=new ArrayList();
System.out.println(c1.isEmpty());
System.out.println(c2.isEmpty());
}
boolean
contains(Object o)
如果此 collection 包含指定的元素,则返回 true。
boolean
containsAll(Collection<?> c)
如果此 collection 包含指定 collection 中的所有元素,则返回 true。
public voidtest4(){
Collection c1=new ArrayList();
c1.add(newObject());
c1.add("嘎嘎");
c1.add(12);
c1.add("hk");
System.out.println(c1.contains("嘎嘎"));
Collection c2=new ArrayList();
c2.add("会话");
System.out.println(c1.containsAll(c2));
}
int
size()
返回此 collection 中的元素数。
public voidtest5(){
Collection c1=newArrayList();
c1.add(new Object());
c1.add("嘎嘎");
c1.add(12);
c1.add("hk");
System.out.println(c1.size());
}
Object[]
toArray()
返回包含此 collection 中所有元素的数组。
public voidtest5(){
Collection c1=new ArrayList();
c1.add(newObject());
c1.add("嘎嘎");
c1.add(12);
c1.add("hk");
Object o[]=c1.toArray();
for(Objects:o){
System.out.println(s);
}
System.out.println(c1.size());
}
Iterator<E>
iterator()
返回在此 collection 的元素上进行迭代的迭代器。
Iterator
java.util
接口 Iterator<E>
所有已知子接口: ListIterator<E>, XMLEventReader
所有已知实现类: BeanContextSupport.BCSIterator, EventReaderDelegate, Scanner
public interface Iterator<E>
对 collection 进行迭代的迭代器。迭代器取代了 JavaCollections Framework 中的 Enumeration。迭代器与枚举有两点不同:
迭代器允许调用者利用定义良好的语义在迭代期间从迭代器所指向的 collection 移除元素。
方法名称得到了改进。
此接口是 JavaCollections Framework 的成员。
从以下版本开始:
1.2
另请参见:
Collection,ListIterator, Enumeration
public voidtest6(){
Collection c1=newArrayList();
c1.add(new Object());
c1.add("嘎嘎");
c1.add(12);
c1.add("hk");
Iterator i=c1.iterator();
while(i.hasNext()){
System.out.println(i.next());
}
}
注意:在使用迭代器迭代集合的时候,不可以使用集合的方法去改变集合的容量。因为在创建迭代器的时候,已经将集合的size等信息存放到迭代器中,如果迭代期间,修改集合的容量,则会产生问题。
修改迭代期间集合容量的例子:
public voidtest6(){
Collection c1=newArrayList();
c1.add(new Object());
c1.add("嘎嘎");
c1.add(12);
c1.add("hk");
Iterator i=c1.iterator();
while(i.hasNext()){
System.out.println(i.next());
c1.add("好");
}
}
三、 List
1. JDKAPI 1.6.0
java.util
接口 List<E>
所有超级接口:
Collection<E>,Iterable<E>
所有已知实现类:
AbstractList,AbstractSequentialList, ArrayList, AttributeList,CopyOnWriteArrayList,LinkedList, RoleList, RoleUnresolvedList,Stack, Vector
public interface List<E>
extends Collection<E>
有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。
与 set 不同,列表通常允许重复的元素。更确切地讲,列表通常允许满足 e1.equals(e2) 的元素对 e1 和 e2,并且如果列表本身允许 null 元素的话,通常它们允许多个 null 元素。难免有人希望通过在用户尝试插入重复元素时抛出运行时异常的方法来禁止重复的列表,但我们希望这种用法越少越好。
List 接口在 iterator、add、remove、equals 和 hashCode 方法的协定上加了一些其他约定,超过了 Collection 接口中指定的约定。为方便起见,这里也包括了其他继承方法的声明。
List 接口提供了 4 种对列表元素进行定位(索引)访问方法。列表(像 Java 数组一样)是基于 0 的。注意,这些操作可能在和某些实现(例如 LinkedList 类)的索引值成比例的时间内执行。因此,如果调用者不知道实现,那么在列表元素上迭代通常优于用索引遍历列表。
List 接口提供了特殊的迭代器,称为 ListIterator,除了允许 Iterator 接口提供的正常操作外,该迭代器还允许元素插入和替换,以及双向访问。还提供了一个方法来获取从列表中指定位置开始的列表迭代器。
List 接口提供了两种搜索指定对象的方法。从性能的观点来看,应该小心使用这些方法。在很多实现中,它们将执行高开销的线性搜索。
List 接口提供了两种在列表的任意位置高效插入和移除多个元素的方法。
注意:尽管列表允许把自身作为元素包含在内,但建议要特别小心:在这样的列表上,equals 和 hashCode 方法不再是定义良好的。
某些列表实现对列表可能包含的元素有限制。例如,某些实现禁止 null 元素,而某些实现则对元素的类型有限制。试图添加不合格的元素会抛出未经检查的异常,通常是 NullPointerException 或 ClassCastException。试图查询不合格的元素是否存在可能会抛出异常,也可能简单地返回 false;某些实现会采用前一种行为,而某些则采用后者。概括地说,试图对不合格元素执行操作时,如果完成该操作后不会导致在列表中插入不合格的元素,则该操作可能抛出一个异常,也可能成功,这取决于实现的选择。此接口的规范中将这样的异常标记为“可选”。
此接口是 JavaCollections Framework 的成员。
从以下版本开始:
1.2
另请参见:
Collection,Set, ArrayList, LinkedList, Vector, Arrays.asList(Object[]),Collections.nCopies(int,Object),Collections.EMPTY_LIST,AbstractList,AbstractSequentialList
2. Method
boolean
add(E e)
向列表的尾部添加指定的元素(可选操作)。
void
add(int index, E element)
在列表的指定位置插入指定元素(可选操作)。
boolean
addAll(Collection<? extendsE> c)
添加指定 collection 中的所有元素到此列表的结尾,顺序是指定 collection 的迭代器返回这些元素的顺序(可选操作)。
boolean
addAll(int index,Collection<? extends E> c)
将指定 collection 中的所有元素都插入到列表中的指定位置(可选操作)。
public voidtest7(){
List l1=newArrayList();
l1.add("会话");
l1.add("kk");
List l2=newArrayList();
l2.add("看看");
l2.add("考虑");
l1.add(1,"看看了");
System.out.println(l1);
l1.addAll(l2);
System.out.println(l1);
}
boolean
remove(Object o)
从此列表中移除第一次出现的指定元素(如果存在)(可选操作)。
public voidtest8(){
List l1=newArrayList();
l1.add("会话");
l1.add("kk");
l1.remove(1);
System.out.println(l1);
}
E
set(int index, E element)
用指定元素替换列表中指定位置的元素(可选操作)。
public voidtest9(){
List l1=newArrayList();
l1.add("会话");
l1.add("kk");
l1.set(1,"kl");
System.out.println(l1);
}
E
get(int index)
返回列表中指定位置的元素。
public voidtest9(){
List l1=newArrayList();
l1.add("会话");
l1.add("kk");
l1.set(1,"kl");
System.out.println(l1.get(0));
System.out.println(l1);
}
List<E>
subList(int fromIndex, int toIndex)
返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图。
public voidtest9(){
List l1=newArrayList();
l1.add("会话");
l1.add("kk");
l1.set(1,"kl");
System.out.println(l1.get(0));
List l2=l1.subList(0, 1);
System.out.println(l2);
System.out.println(l1);
}
Iterator<E>
iterator()
返回按适当顺序在列表的元素上进行迭代的迭代器。
ListIterator<E>
listIterator()
返回此列表元素的列表迭代器(按适当顺序)。
遍历list的3种方式
普通循环、增强for循环和list的迭代器
public voidtest10(){
List l1=newArrayList();
l1.add("会话");
l1.add("kk");
l1.add("kl");
System.out.println("普通for循环");
for(inti=0;i<l1.size();i++){
System.out.println(l1.get(i));
}
System.out.println("增强for循环");
//使用增强for,底层采用的是迭代器,循环期间不能修改集合的容量,否则报错
for(Objecto:l1){
System.out.println(o);
}
System.out.println("list的迭代器");
ListIterator i=l1.listIterator();
while(i.hasNext()){
Object o=i.next();
System.out.println(o);
}
while(i.hasPrevious()){
Object o=i.previous();
System.out.println(o);
}
}
四、 ArrayList
java.util
类 ArrayList<E>
java.lang.Object
java.util.AbstractCollection<E>
java.util.AbstractList<E>
java.util.ArrayList<E>
所有已实现的接口:
Serializable,Cloneable, Iterable<E>, Collection<E>,List<E>, RandomAccess
直接已知子类:
AttributeList,RoleList, RoleUnresolvedList
public class ArrayList<E>
extends AbstractList<E>
implements List<E>,RandomAccess, Cloneable, Serializable
List 接口的大小可变数组的实现。实现了所有可选列表操作,并允许包括null 在内的所有元素。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于Vector 类,除了此类是不同步的。)
size、isEmpty、get、set、iterator 和listIterator 操作都以固定时间运行。add 操作以分摊的固定时间运行,也就是说,添加 n 个元素需要 O(n) 时间。其他所有操作都以线性时间运行(大体上讲)。与用于LinkedList 实现的常数因子相比,此实现的常数因子较低。
每个 ArrayList 实例都有一个容量。该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向ArrayList 中不断添加元素,其容量也自动增长。并未指定增长策略的细节,因为这不只是添加元素会带来分摊固定时间开销那样简单。
在添加大量元素前,应用程序可以使用 ensureCapacity 操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。
注意,此实现不是同步的。如果多个线程同时访问一个 ArrayList实例,而其中至少一个线程从结构上修改了列表,那么它必须 保持外部同步。(结构上的修改是指任何添加或删除一个或多个元素的操作,或者显式调整底层数组的大小;仅仅设置元素的值不是结构上的修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用Collections.synchronizedList方法将该列表“包装”起来。这最好在创建时完成,以防止意外对列表进行不同步的访问:
List list = Collections.synchronizedList(newArrayList(...));
此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的remove 或 add 方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。
注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭代器的快速失败行为应该仅用于检测 bug。
此类是 JavaCollections Framework 的成员。
从以下版本开始: 1.2
另请参见: Collection, List, LinkedList, Vector, Collections.synchronizedList(List),序列化表格
ArrayList的用法和List类似。
五、 LinkedList
java.util
类 LinkedList<E>
java.lang.Object
java.util.AbstractCollection<E>
java.util.AbstractList<E>
java.util.AbstractSequentialList<E>
java.util.LinkedList<E>
类型参数: E - 在此collection 中保持的元素的类型
所有已实现的接口:
Serializable,Cloneable, Iterable<E>, Collection<E>,Deque<E>, List<E>, Queue<E>
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>,Deque<E>, Cloneable, Serializable
List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括null)。除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。
此类实现 Deque 接口,为 add、poll 提供先进先出队列操作,以及其他堆栈和双端队列操作。
所有操作都是按照双重链接列表的需要执行的。在列表中编索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。
注意,此实现不是同步的。如果多个线程同时访问一个链接列表,而其中至少一个线程从结构上修改了该列表,则它必须保持外部同步。(结构修改指添加或删除一个或多个元素的任何操作;仅设置元素的值不是结构修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用Collections.synchronizedList方法来“包装”该列表。最好在创建时完成这一操作,以防止对列表进行意外的不同步访问,如下所示:
List list =Collections.synchronizedList(new LinkedList(...));
此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:在迭代器创建之后,如果从结构上对列表进行修改,除非通过迭代器自身的 remove 或 add 方法,其他任何时间任何方式的修改,迭代器都将抛出ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来不确定的时间任意发生不确定行为的风险。
注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何硬性保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。
此类是 JavaCollections Framework 的成员。
从以下版本开始: 1.2
另请参见: List, ArrayList, Vector, 序列化表格
Method
void
addFirst(E e)
将指定元素插入此列表的开头。
void
addLast(E e)
将指定元素添加到此列表的结尾。
E
getFirst()
返回此列表的第一个元素。
E
getLast()
返回此列表的最后一个元素。
boolean
remove(Object o)
从此列表中移除首次出现的指定元素(如果存在)。
E
removeFirst()
移除并返回此列表的第一个元素。
E
removeLast()
移除并返回此列表的最后一个元素。
public voidtest11(){
LinkedList l=newLinkedList();
l.add("hh");
l.add("看看");
l.add(12);
l.add("快快快");
System.out.println(l);
l.addFirst(11);
l.addLast("看看书");
System.out.println(l);
System.out.println(l.getFirst());
System.out.println(l.getLast());
l.removeLast();
System.out.println(l);
l.removeFirst();
System.out.println(l);
}
总结:LinkedList底层采用的链表结构存储数据,它增删相对较快,查询较慢;ArrayList底层采用数据结构存储数据,它查询较快,增删较慢。
六、 Set
java.util
接口 Set<E>
类型参数: E - 此 set 所维护元素的类型
所有超级接口: Collection<E>, Iterable<E>
所有已知子接口: NavigableSet<E>, SortedSet<E>
所有已知实现类:
AbstractSet,ConcurrentSkipListSet,CopyOnWriteArraySet,EnumSet, HashSet, JobStateReasons, LinkedHashSet,TreeSet
public interface Set<E>
extends Collection<E>
一个不包含重复元素的 collection。更确切地讲,set 不包含满足e1.equals(e2) 的元素对 e1 和 e2,并且最多包含一个 null 元素。正如其名称所暗示的,此接口模仿了数学上的set 抽象。
在所有构造方法以及 add、equals 和 hashCode 方法的协定上,Set 接口还加入了其他规定,这些规定超出了从Collection 接口所继承的内容。出于方便考虑,它还包括了其他继承方法的声明(这些声明的规范已经专门针对Set 接口进行了修改,但是没有包含任何其他的规定)。
对这些构造方法的其他规定是(不要奇怪),所有构造方法必须创建一个不包含重复元素的 set(正如上面所定义的)。
注:如果将可变对象用作 set 元素,那么必须极其小心。如果对象是set 中某个元素,以一种影响 equals 比较的方式改变对象的值,那么 set 的行为就是不确定的。此项禁止的一个特殊情况是不允许某个 set 包含其自身作为元素。
某些 set 实现对其所包含的元素有所限制。例如,某些实现禁止null 元素,而某些则对其元素的类型所有限制。试图添加不合格的元素会抛出未经检查的异常,通常是 NullPointerException或 ClassCastException。试图查询不合格的元素是否存在可能会抛出异常,也可能简单地返回 false;某些实现会采用前一种行为,而某些则采用后者。概括地说,试图对不合格元素执行操作时,如果完成该操作后不会导致在 set 中插入不合格的元素,则该操作可能抛出一个异常,也可能成功,这取决于实现的选择。此接口的规范中将这样的异常标记为“可选”。
此接口是 JavaCollections Framework 的成员。
从以下版本开始: 1.2
另请参见: Collection, List, SortedSet, HashSet, TreeSet, AbstractSet, Collections.singleton(java.lang.Object),Collections.EMPTY_SET
七、 HashSet
java.util
类 HashSet<E>
java.lang.Object
java.util.AbstractCollection<E>
java.util.AbstractSet<E>
java.util.HashSet<E>
类型参数: E - 此 set 所维护的元素的类型
所有已实现的接口: Serializable, Cloneable,Iterable<E>, Collection<E>, Set<E>
直接已知子类: JobStateReasons, LinkedHashSet
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>,Cloneable, Serializable
此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素。
此类为基本操作提供了稳定性能,这些基本操作包括 add、remove、contains 和 size,假定哈希函数将这些元素正确地分布在桶中。对此 set 进行迭代所需的时间与 HashSet 实例的大小(元素的数量)和底层HashMap 实例(桶的数量)的“容量”的和成比例。因此,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。
注意,此实现不是同步的。如果多个线程同时访问一个哈希set,而其中至少一个线程修改了该 set,那么它必须 保持外部同步。这通常是通过对自然封装该 set 的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用Collections.synchronizedSet方法来“包装” set。最好在创建时完成这一操作,以防止对该 set 进行意外的不同步访问:
Set s =Collections.synchronizedSet(new HashSet(...));
此类的 iterator 方法返回的迭代器是快速失败 的:在创建迭代器之后,如果对 set 进行修改,除非通过迭代器自身的 remove 方法,否则在任何时间以任何方式对其进行修改,Iterator 都将抛出ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来在某个不确定时间发生任意不确定行为的风险。
注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器在尽最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误做法:迭代器的快速失败行为应该仅用于检测 bug。
此类是 JavaCollections Framework 的成员。
从以下版本开始: 1.2
另请参见: Collection, Set, TreeSet, HashMap, 序列化表格
Method
Iterator<E>
iterator()
返回在此 set 中的元素上进行迭代的迭代器。
public voidtest12(){
HashSet s=newHashSet();
s.add("看看");
s.add("浏览");
s.add(12);
s.add("看看了");
s.add("看看了");
s.add("看看了");
for(Iteratori=s.iterator();i.hasNext();){
System.out.println(i.next());
}
}
HashSet算法保证存储唯一性的原因
HashSet底层采用哈希表,哈希表本质上就是一个链表的数组,也就是一个数组里面保存着链表。向哈希表中添加数据的时候,首先会调用这个对象的hashCode方法,获取这个对象的哈希码后,根据获得得哈希码,计算这个对象的应该保存在哈希表中的区域。计算出来后,就会查看该区域中是否存在数据。如果没有数据,就直接将这个对象保存进去。如果已经有了数据,就会遍历数据,并使用equals方法和每个元素进行比较,如果有相等的就不会添加;遍历结束,如果没有相等的,就将这个数据添加到该区域。
如果使用HashSet保存自定义的对象,需要重写这个对象的equals和hashSet方法。Hash算法不同,得到的hash码就不同,数据存储的位置也不一样,迭代数据也不一样。
例子:
自定义Person类
public class Person {
private Stringname;
private intage;
public Person(Stringname,intage){
super();
this.name =name;
this.age =age;
}
public String getName() {
return name;
}
public voidsetName(String name){
this.name =name;
}
public intgetAge() {
return age;
}
public voidsetAge(intage){
this.age =age;
}
@Override
public inthashCode() {
final int prime= 31;
int result = 1;
result = prime* result+ age;
result = prime* result+ ((name==null)? 0 : name.hashCode());
return result;
}
@Override
public booleanequals(Object obj){
if (this ==obj)
return true;
if (obj ==null)
return false;
if (getClass() !=obj.getClass())
return false;
Person other = (Person) obj;
if (age !=other.age)
return false;
if (name ==null){
if (other.name!=null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "Person [name=" + name + ", age="+ age+ "]";
}
}
使用HashSet保存
public voidtest13(){
HashSet h=new HashSet();
h.add(newPerson("会话",123));
h.add(newPerson("会2话",163));
h.add(newPerson("会3话",13));
h.add(newPerson("会话4",125));
for(Iteratori=h.iterator();i.hasNext();){
System.out.println(i.next());
}
}
八、 LinkedHashSet
java.util
类 LinkedHashSet<E>
java.lang.Object
java.util.AbstractCollection<E>
java.util.AbstractSet<E>
java.util.HashSet<E>
java.util.LinkedHashSet<E>
类型参数: E - 由此 set 维护的元素的类型
所有已实现的接口: Serializable, Cloneable,Iterable<E>, Collection<E>, Set<E>
public class LinkedHashSet<E>
extends HashSet<E>
implements Set<E>,Cloneable, Serializable
具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。此实现与 HashSet 的不同之外在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,即按照将元素插入到 set 中的顺序(插入顺序)进行迭代。注意,插入顺序不 受在 set 中重新插入的 元素的影响。(如果在 s.contains(e) 返回true 后立即调用 s.add(e),则元素 e 会被重新插入到 set s 中。)
此实现可以让客户免遭未指定的、由 HashSet 提供的通常杂乱无章的排序工作,而又不致引起与 TreeSet 关联的成本增加。使用它可以生成一个与原来顺序相同的 set 副本,并且与原 set 的实现无关:
void foo(Set s){
Set copy =new LinkedHashSet(s);
...
}
如果模块通过输入得到一个 set,复制这个 set,然后返回由此副本决定了顺序的结果,这种情况下这项技术特别有用。(客户通常期望内容返回的顺序与它们出现的顺序相同。)
此类提供所有可选的 Set 操作,并且允许 null 元素。与HashSet 一样,它可以为基本操作(add、contains 和 remove)提供稳定的性能,假定哈希函数将元素正确地分布到存储段中。由于增加了维护链接列表的开支,其性能很可能会比HashSet 稍逊一筹,不过,这一点例外:LinkedHashSet 迭代所需时间与 set 的大小 成正比,而与容量无关。HashSet 迭代很可能支出较大,因为它所需迭代时间与其容量成正比。
链接的哈希 set 有两个影响其性能的参数:初始容量 和加载因子。它们与HashSet 中的定义极其相同。注意,为初始容量选择非常高的值对此类的影响比对 HashSet 要小,因为此类的迭代时间不受容量的影响。
注意,此实现不是同步的。如果多个线程同时访问链接的哈希set,而其中至少一个线程修改了该 set,则它必须 保持外部同步。这一般通过对自然封装该 set 的对象进行同步操作来完成。如果不存在这样的对象,则应该使用Collections.synchronizedSet方法来“包装”该 set。最好在创建时完成这一操作,以防止意外的非同步访问:
Set s =Collections.synchronizedSet(new LinkedHashSet(...));
此类的 iterator 方法返回的迭代器是快速失败 的:在迭代器创建之后,如果对 set 进行修改,除非通过迭代器自身的 remove 方法,其他任何时间任何方式的修改,迭代器都将抛出ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来不确定的时间任意发生不确定行为的风险。
注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何强有力的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。
此类是 JavaCollections Framework 的成员。
从以下版本开始: 1.4
另请参见: Object.hashCode(),Collection,Set, HashSet, TreeSet, Hashtable, 序列化表格
HashSet保存数据和添加数据的顺序是不一样的,迭代的时候获取的顺序也是不一样的。如果需要保存的数据时唯一的,同时希望保存的数据和添加数据和迭代的顺序的一致,可以使用LinkedHashSet。使用LinkedHashSet在向哈希表中保存数据时,链表会按照添加顺序记录哈希表中保存数据的节点的内存地址,对LinkedHashSet进行迭代时,按照链表中的记录进行迭代。
例子:
public voidtest14(){
LinkedHashSet l=new LinkedHashSet();
l.add("会话1");
l.add("会话2");
l.add("会话3");
l.add("会话4");
for(Iteratori=l.iterator();i.hasNext();){
System.out.println(i.next());
}
}
九、 TreeSet
java.util
类 TreeSet<E>
java.lang.Object
java.util.AbstractCollection<E>
java.util.AbstractSet<E>
java.util.TreeSet<E>
类型参数: E - 此 set 维护的元素的类型
所有已实现的接口: Serializable, Cloneable,Iterable<E>, Collection<E>, NavigableSet<E>,Set<E>, SortedSet<E>
public class TreeSet<E>
extends AbstractSet<E>
implements NavigableSet<E>,Cloneable, Serializable
基于 TreeMap 的NavigableSet 实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的Comparator 进行排序,具体取决于使用的构造方法。
此实现为基本操作(add、remove 和 contains)提供受保证的 log(n) 时间开销。
注意,如果要正确实现 Set 接口,则 set 维护的顺序(无论是否提供了显式比较器)必须与 equals 一致。(关于与 equals 一致 的精确定义,请参阅Comparable 或 Comparator。)这是因为 Set 接口是按照 equals 操作定义的,但 TreeSet 实例使用它的 compareTo(或 compare)方法对所有元素进行比较,因此从 set 的观点来看,此方法认为相等的两个元素就是相等的。即使 set 的顺序与 equals 不一致,其行为也是 定义良好的;它只是违背了Set 接口的常规协定。
注意,此实现不是同步的。如果多个线程同时访问一个TreeSet,而其中至少一个线程修改了该 set,那么它必须 外部同步。这一般是通过对自然封装该 set 的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用Collections.synchronizedSortedSet方法来“包装”该 set。此操作最好在创建时进行,以防止对 set 的意外非同步访问:
SortedSet s =Collections.synchronizedSortedSet(new TreeSet(...));
此类的 iterator 方法返回的迭代器是快速失败 的:在创建迭代器之后,如果从结构上对 set 进行修改,除非通过迭代器自身的 remove 方法,否则在其他任何时间以任何方式进行修改都将导致迭代器抛出ConcurrentModificationException。因此,对于并发的修改,迭代器很快就完全失败,而不会冒着在将来不确定的时间发生不确定行为的风险。
注意,迭代器的快速失败行为无法得到保证,一般来说,存在不同步的并发修改时,不可能作出任何肯定的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的做法是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测 bug。
此类是 JavaCollections Framework 的成员。
从以下版本开始: 1.2
另请参见: Collection, Set, HashSet, Comparable, Comparator, TreeMap, 序列化表格
例子:
使用treeSet保存字符串,按照ascii码顺序排序。
public voidtest15(){
TreeSet t=new TreeSet();
t.add("看看");
t.add("看2看");
t.add("看3看");
t.add("看看4");
System.out.println(t);
}
使用TreeSet保存自定义对象,需要让保存的对象的类实现Comparable接口,或者给TreeSet集合对象传递一个比较器对象。
Comparable
java.lang
接口 Comparable<T>
类型参数: T - 可以与此对象进行比较的那些对象的类型
所有已知子接口: Delayed, Name, RunnableScheduledFuture<V>, ScheduledFuture<V>
所有已知实现类:
Authenticator.RequestorType,BigDecimal, BigInteger, Boolean, Byte, ByteBuffer, Calendar, Character, CharBuffer, Charset,ClientInfoStatus, CollationKey, Component.BaselineResizeBehavior,CompositeName, CompoundName, Date, Date, Desktop.Action, Diagnostic.Kind, Dialog.ModalExclusionType,Dialog.ModalityType, Double, DoubleBuffer, DropMode, ElementKind,ElementType, Enum, File, Float, FloatBuffer, Formatter.BigDecimalLayoutForm, FormSubmitEvent.MethodType, GregorianCalendar, GroupLayout.Alignment,IntBuffer, Integer, JavaFileObject.Kind,JTable.PrintMode, KeyRep.Type, LayoutStyle.ComponentPlacement,LdapName, Long, LongBuffer, MappedByteBuffer, MemoryType, MessageContext.Scope,Modifier, MultipleGradientPaint.ColorSpaceType, MultipleGradientPaint.CycleMethod, NestingKind, Normalizer.Form,ObjectName, ObjectStreamField, Proxy.Type,Rdn, Resource.AuthenticationType, RetentionPolicy, RoundingMode,RowFilter.ComparisonType,RowIdLifetime, RowSorterEvent.Type, Service.Mode,Short, ShortBuffer, SOAPBinding.ParameterStyle,SOAPBinding.Style, SOAPBinding.Use, SortOrder,SourceVersion, SSLEngineResult.HandshakeStatus, SSLEngineResult.Status, StandardLocation,String, SwingWorker.StateValue, Thread.State,Time, Timestamp, TimeUnit,TrayIcon.MessageType, TypeKind, URI, UUID, WebParam.Mode, XmlAccessOrder, XmlAccessType, XmlNsForm
public interface Comparable<T>
此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 compareTo 方法被称为它的自然比较方法。
实现此接口的对象列表(和数组)可以通过 Collections.sort(和Arrays.sort)进行自动排序。实现此接口的对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。
对于类 C 的每一个 e1 和 e2 来说,当且仅当 e1.compareTo(e2) == 0 与 e1.equals(e2) 具有相同的 boolean 值时,类 C 的自然排序才叫做与 equals 一致。注意,null 不是任何类的实例,即使 e.equals(null) 返回 false,e.compareTo(null) 也将抛出 NullPointerException。
建议(虽然不是必需的)最好使自然排序与 equals 一致。这是因为在使用自然排序与 equals 不一致的元素(或键)时,没有显式比较器的有序集合(和有序映射表)行为表现“怪异”。尤其是,这样的有序集合(或有序映射表)违背了根据 equals 方法定义的集合(或映射表)的常规协定。
例如,如果将两个键 a 和 b 添加到没有使用显式比较器的有序集合中,使(!a.equals(b) && a.compareTo(b) == 0),那么第二个 add 操作将返回 false(有序集合的大小没有增加),因为从有序集合的角度来看,a 和 b 是相等的。
实际上,所有实现 Comparable 的 Java 核心类都具有与 equals 一致的自然排序。java.math.BigDecimal 是个例外,它的自然排序将值相等但精确度不同的BigDecimal 对象(比如 4.0 和 4.00)视为相等。
从数学上讲,定义给定类 C 上自然排序的关系式 如下:
{(x,y)|x.compareTo(y) <= 0}。
整体排序的商 是:
{(x,y)|x.compareTo(y) == 0}。
它直接遵循 compareTo 的协定,商是 C 的等价关系,自然排序是C 的整体排序。当说到类的自然排序与 equals 一致时,是指自然排序的商是由类的equals(Object)方法定义的等价关系。
{(x,y)|x.equals(y)}。
此接口是 JavaCollections Framework 的成员。
从以下版本开始: 1.2
另请参见: Comparator
例子:
创建自定义类继承Comparable,注意重写hashCode和equals方法。
public class Person2 implementsComparable{
private Stringname;
private intage;
public Person2(Stringname, intage){
super();
this.name =name;
this.age =age;
}
public String getName() {
return name;
}
public voidsetName(String name){
this.name =name;
}
public intgetAge() {
return age;
}
public voidsetAge(intage){
this.age =age;
}
@Override
public inthashCode() {
final int prime= 31;
int result = 1;
result = prime* result+ age;
result = prime* result+ ((name==null)? 0 : name.hashCode());
return result;
}
@Override
public booleanequals(Object obj){
if (this ==obj)
return true;
if (obj ==null)
return false;
if (getClass() !=obj.getClass())
return false;
Person2 other = (Person2)obj;
if (age !=other.age)
return false;
if (name ==null){
if (other.name!=null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "Person [name=" + name + ", age="+ age+ "]";
}
@Override
public intcompareTo(Object o){
Person2 p=(Person2)o;
int result=this.age-p.age;
if(result==0){
result=this.name.compareTo(p.getName());
}
return result;
}
}
使用TreeSet保存
public voidtest16(){
TreeSet t=new TreeSet();
t.add(newPerson2("看看",123));
t.add(newPerson2("看看",12));
t.add(newPerson2("看看",113));
t.add(newPerson2("看看",163));
t.add(newPerson2("看2看",163));
System.out.println(t);
for(Iteratori=t.iterator();i.hasNext();){
System.out.println(i.next());
}
}
使用TreeSet保存数据时,传入自定义Comparator的方式。
Comparator
java.util
接口 Comparator<T>
类型参数: T - 此Comparator 可以比较的对象类型
所有已知实现类: Collator, RuleBasedCollator
public interface Comparator<T>
强行对某个对象 collection 进行整体排序 的比较函数。可以将 Comparator 传递给 sort 方法(如Collections.sort或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用 Comparator 来控制某些数据结构(如有序 set或有序映射)的顺序,或者为那些没有自然顺序的对象 collection 提供排序。
当且仅当对于一组元素 S 中的每个 e1 和 e2 而言,c.compare(e1, e2)==0 与 e1.equals(e2) 具有相等的布尔值时,Comparator c 强行对S 进行的排序才叫做与 equals 一致 的排序。
当使用具有与 equals 不一致的强行排序能力的Comparator 对有序 set(或有序映射)进行排序时,应该小心谨慎。假定一个带显式 Comparator c 的有序 set(或有序映射)与从 set S 中抽取出来的元素(或键)一起使用。如果 c 强行对 S 进行的排序是与 equals 不一致的,那么有序 set(或有序映射)将是行为“怪异的”。尤其是有序 set(或有序映射)将违背根据 equals 所定义的 set(或映射)的常规协定。
例如,假定使用 Comparator c 将满足 (a.equals(b)&& c.compare(a, b) != 0) 的两个元素 a 和 b 添加到一个空 TreeSet 中,则第二个 add 操作将返回 true(树 set的大小将会增加),因为从树 set 的角度来看,a 和b 是不相等的,即使这与Set.add方法的规范相反。
注:通常来说,让 Comparator 也实现 java.io.Serializable是一个好主意,因为它们在可序列化的数据结构(像TreeSet、TreeMap)中可用作排序方法。为了成功地序列化数据结构,Comparator(如果已提供)必须实现 Serializable。
在算术上,定义给定 Comparator c 对给定对象 set S实施强行排序 的关系式 为:
{(x, y) suchthat c.compare(x, y) <= 0}.
此整体排序的商 (quotient) 为:
{(x, y) suchthat c.compare(x, y) == 0}.
它直接遵循 compare 的协定,商是 S 上的等价关系,强行排序是S 上的整体排序。当我们说 c 强行对 S 的排序是与 equals 一致 的时,意思是说排序的商是对象的equals(Object)方法所定义的等价关系:
{(x, y) suchthat x.equals(y)}.
此接口是 JavaCollections Framework 的成员。
从以下版本开始: 1.2
另请参见: Comparable, Serializable
例子按照字符串的长度排序。
创建自定义Comparator
public class MyComparator implementsComparator{
@Override
public intcompare(Object o1,Object o2) {
String s1=(String)o1;
String s2=(String)o2;
return s1.length()-s2.length();
}
}
使用TreeSet保存数据
public voidtest17(){
TreeSet t=new TreeSet(newMyComparator());
t.add("lkl");
t.add("lksdfl");
t.add("lksl");
t.add("ldkl");
t.add("lk手动l");
System.out.println(t);
}
十、 Map
java.util
接口 Map<K,V>
类型参数: K - 此映射所维护的键的类型 V - 映射值的类型
所有已知子接口:
Bindings,ConcurrentMap<K,V>,ConcurrentNavigableMap<K,V>,LogicalMessageContext,MessageContext, NavigableMap<K,V>, SOAPMessageContext, SortedMap<K,V>
所有已知实现类:
AbstractMap,Attributes, AuthProvider, ConcurrentHashMap,ConcurrentSkipListMap,EnumMap, HashMap, Hashtable, IdentityHashMap, LinkedHashMap,PrinterStateReasons,Properties, Provider, RenderingHints,SimpleBindings, TabularDataSupport, TreeMap,UIDefaults, WeakHashMap
public interface Map<K,V>
将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。
此接口取代 Dictionary 类,后者完全是一个抽象类,而不是一个接口。
Map 接口提供三种collection视图,允许以键集、值集或键-值映射关系集的形式查看某个映射的内容。映射顺序定义为迭代器在映射的 collection 视图上返回其元素的顺序。某些映射实现可明确保证其顺序,如 TreeMap 类;另一些映射实现则不保证顺序,如 HashMap 类。
注:将可变对象用作映射键时必须格外小心。当对象是映射中某个键时,如果以影响 equals 比较的方式更改了对象的值,则映射的行为将是不确定的。此项禁止的一种特殊情况是不允许某个映射将自身作为一个键包含。虽然允许某个映射将自身作为值包含,但请格外小心:在这样的映射上equals 和 hashCode 方法的定义将不再是明确的。
所有通用的映射实现类应该提供两个“标准的”构造方法:一个 void(无参数)构造方法,用于创建空映射;一个是带有单个 Map 类型参数的构造方法,用于创建一个与其参数具有相同键-值映射关系的新映射。实际上,后一个构造方法允许用户复制任意映射,生成所需类的一个等价映射。尽管无法强制执行此建议(因为接口不能包含构造方法),但是 JDK 中所有通用的映射实现都遵从它。
此接口中包含的“破坏”方法可修改其操作的映射,如果此映射不支持该操作,这些方法将抛出UnsupportedOperationException。如果是这样,那么在调用对映射无效时,这些方法可以(但不要求)抛出UnsupportedOperationException。例如,如果某个不可修改的映射(其映射关系是“重叠”的)为空,则对该映射调用putAll(Map)方法时,可以(但不要求)抛出异常。
某些映射实现对可能包含的键和值有所限制。例如,某些实现禁止 null 键和值,另一些则对其键的类型有限制。尝试插入不合格的键或值将抛出一个未经检查的异常,通常是NullPointerException 或 ClassCastException。试图查询是否存在不合格的键或值可能抛出异常,或者返回 false;某些实现将表现出前一种行为,而另一些则表现后一种。一般来说,试图对不合格的键或值执行操作且该操作的完成不会导致不合格的元素被插入映射中时,将可能抛出一个异常,也可能操作成功,这取决于实现本身。这样的异常在此接口的规范中标记为“可选”。
此接口是 JavaCollections Framework 的成员。
Collections Framework 接口中的很多方法是根据 equals方法定义的。例如,containsKey(Objectkey) 方法的规范中写道:“当且仅当此映射包含针对满足 (key==null ? k==null : key.equals(k)) 的键 k 的映射关系时,返回true”。不 应将此规范解释为:调用具有非空参数 key 的Map.containsKey 将导致对任意的键 k 调用 key.equals(k)。实现可随意进行优化,以避免调用 equals,例如,可首先比较两个键的哈希码(Object.hashCode()规范保证哈希码不相等的两个对象不会相等)。一般来说,只要实现者认为合适,各种 CollectionsFramework 接口的实现可随意利用底层Object 方法的指定行为。
从以下版本开始: 1.2
另请参见: HashMap, TreeMap, Hashtable, SortedMap, Collection, Set
Method
V
put(K key,V value)
将指定的值与此映射中的指定键关联(可选操作)。
void
putAll(Map<? extendsK,? extends V> m)
从指定映射中将所有映射关系复制到此映射中(可选操作)。
public voidtest1(){
Map map=newHashMap();
map.put("hh", 123);
map.put("2hh", 153);
map.put("h2h", 13);
map.put("hh3", 1253);
map.put("h3h", 1763);
Map m2=newHashMap();
m2.put("看看","q");
m2.put("看看","q");
m2.putAll(map);
System.out.println(map);
System.out.println(m2);
}
V
remove(Object key)
如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
void
clear()
从此映射中移除所有映射关系(可选操作)。
public voidtest2(){
Map map=newHashMap();
map.put("hh", 123);
map.put("2hh", 153);
map.put("h2h", 13);
map.put("hh3", 1253);
map.put("h3h", 1763);
System.out.println(map);
map.remove("hh");
System.out.println(map);
map.clear();
System.out.println(map);
}
V
get(Object key)
返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
public voidtest3(){
Map map=new HashMap();
map.put("hh",123);
map.put("2hh",153);
map.put("h2h",13);
map.put("hh3",1253);
map.put("h3h",1763);
System.out.println(map.get("hh"));
}
Set<K>
keySet()
返回此映射中包含的键的 Set 视图。
public voidtest3(){
Map map=new HashMap();
map.put("hh",123);
map.put("2hh",153);
map.put("h2h",13);
map.put("hh3",1253);
map.put("h3h",1763);
Set s=map.keySet();
for(Iteratori=s.iterator();i.hasNext();){
System.out.println(map.get(i.next()));
}
}
Collection<V>
values()
返回此映射中包含的值的 Collection 视图。
public voidtest4(){
Map map=new HashMap();
map.put("hh",123);
map.put("2hh",153);
map.put("h2h",13);
map.put("hh3",1253);
map.put("h3h",1763);
Collection c=map.values();
for(Iteratori=c.iterator();i.hasNext();){
System.out.println(i.next());
}
}
Set<Map.Entry<K,V>>
entrySet()
返回此映射中包含的映射关系的 Set 视图。
public voidtest5(){
Map map=new HashMap();
map.put("hh",123);
map.put("2hh",153);
map.put("h2h",13);
map.put("hh3",1253);
map.put("h3h",1763);
Set c=map.entrySet();
for(Iteratori=c.iterator();i.hasNext();){
System.out.println(i.next());
}
}
boolean
isEmpty()
如果此映射未包含键-值映射关系,则返回 true。
boolean
containsKey(Object key)
如果此映射包含指定键的映射关系,则返回 true。
boolean
containsValue(Object value)
如果此映射将一个或多个键映射到指定值,则返回 true。
public voidtest6(){
Map map=new HashMap();
map.put("hh",123);
map.put("2hh",153);
map.put("h2h",13);
map.put("hh3",1253);
map.put("h3h",1763);
System.out.println(map.isEmpty());
System.out.println(map.containsKey("hh"));
System.out.println(map.containsValue(13));
}
十一、 HashMap
java.util
类 HashMap<K,V>
java.lang.Object
java.util.AbstractMap<K,V>
java.util.HashMap<K,V>
类型参数: K - 此映射所维护的键的类型 V - 所映射值的类型
所有已实现的接口: Serializable, Cloneable,Map<K,V>
直接已知子类: LinkedHashMap, PrinterStateReasons
public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>,Cloneable, Serializable
基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get 和 put)提供稳定的性能。迭代 collection 视图所需的时间与 HashMap 实例的“容量”(桶的数量)及其大小(键-值映射关系数)成比例。所以,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。
HashMap 的实例有两个参数影响其性能:初始容量 和加载因子。容量是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。
通常,默认加载因子 (.75) 在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数HashMap 类的操作中,包括 get 和 put 操作,都反映了这一点)。在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地减少 rehash 操作次数。如果初始容量大于最大条目数除以加载因子,则不会发生rehash 操作。
如果很多映射关系要存储在 HashMap 实例中,则相对于按需执行自动的 rehash 操作以增大表的容量来说,使用足够大的初始容量创建它将使得映射关系能更有效地存储。
注意,此实现不是同步的。如果多个线程同时访问一个哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须保持外部同步。(结构上的修改是指添加或删除一个或多个映射关系的任何操作;仅改变与实例已经包含的键关联的值不是结构上的修改。)这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象,则应该使用Collections.synchronizedMap方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射进行意外的非同步访问,如下所示:
Map m =Collections.synchronizedMap(new HashMap(...));
由所有此类的“collection 视图方法”所返回的迭代器都是快速失败的:在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器本身的 remove 方法,其他任何时间任何方式的修改,迭代器都将抛出ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒在将来不确定的时间发生任意不确定行为的风险。
注意,迭代器的快速失败行为不能得到保证,一般来说,存在非同步的并发修改时,不可能作出任何坚决的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的做法是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。
此类是 JavaCollections Framework 的成员。
从以下版本开始: 1.2
另请参见: Object.hashCode(),Collection,Map, TreeMap, Hashtable, 序列化表格
如果在HashMap中保存自定义的对象,请重写hashCode和equals方法。
创建自定义类
public class Person3{
private Stringname;
public Person3(Stringname) {
super();
this.name =name;
}
public String getName() {
return name;
}
public voidsetName(String name){
this.name =name;
}
@Override
public inthashCode() {
final int prime= 31;
int result = 1;
result = prime* result+ ((name==null)? 0 : name.hashCode());
return result;
}
@Override
public booleanequals(Object obj){
if (this ==obj)
return true;
if (obj ==null)
return false;
if (getClass() !=obj.getClass())
return false;
Person3 other = (Person3)obj;
if (name ==null){
if (other.name!=null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "Person3 [name=" + name + "]";
}
}
在HashMap中保存自定义类
public voidtest7(){
HashMap h=new HashMap();
h.put(newPerson3("会话"), 12);
h.put(newPerson3("会2话"), 152);
h.put(newPerson3("会3话"), 125);
h.put(newPerson3("会4话"), 142);
System.out.println(h);
}
十二、 LinkedHashMap
java.util
类 LinkedHashMap<K,V>
java.lang.Object
java.util.AbstractMap<K,V>
java.util.HashMap<K,V>
java.util.LinkedHashMap<K,V>
类型参数: K - 由此映射维护的键的类型 V - 映射值的类型
所有已实现的接口: Serializable, Cloneable,Map<K,V>
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。此实现与 HashMap 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序)。注意,如果在映射中重新插入键,则插入顺序不受影响。(如果在调用 m.put(k, v) 前 m.containsKey(k)返回了 true,则调用时会将键 k 重新插入到映射m 中。)
此实现可以让客户避免未指定的、由 HashMap(及 Hashtable)所提供的通常为杂乱无章的排序工作,同时无需增加与TreeMap 相关的成本。使用它可以生成一个与原来顺序相同的映射副本,而与原映射的实现无关:
void foo(Mapm) {
Map copy =new LinkedHashMap(m);
...
}
如果模块通过输入得到一个映射,复制这个映射,然后返回由此副本确定其顺序的结果,这种情况下这项技术特别有用。(客户通常期望返回的内容与其出现的顺序相同。)
提供特殊的构造方法来创建链接哈希映射,该哈希映射的迭代顺序就是最后访问其条目的顺序,从近期访问最少到近期访问最多的顺序(访问顺序)。这种映射很适合构建 LRU 缓存。调用 put 或 get方法将会访问相应的条目(假定调用完成后它还存在)。putAll 方法以指定映射的条目集迭代器提供的键-值映射关系的顺序,为指定映射的每个映射关系生成一个条目访问。任何其他方法均不生成条目访问。特别是,collection 视图上的操作不 影响底层映射的迭代顺序。
可以重写 removeEldestEntry(Map.Entry)方法来实施策略,以便在将新映射关系添加到映射时自动移除旧的映射关系。
此类提供所有可选的 Map 操作,并且允许 null 元素。与HashMap 一样,它可以为基本操作(add、contains 和 remove)提供稳定的性能,假定哈希函数将元素正确分布到桶中。由于增加了维护链接列表的开支,其性能很可能比HashMap 稍逊一筹,不过这一点例外:LinkedHashMap 的 collection 视图迭代所需时间与映射的大小 成比例。HashMap迭代时间很可能开支较大,因为它所需要的时间与其容量 成比例。
链接的哈希映射具有两个影响其性能的参数:初始容量和加载因子。它们的定义与 HashMap极其相似。要注意,为初始容量选择非常高的值对此类的影响比对 HashMap 要小,因为此类的迭代时间不受容量的影响。
注意,此实现不是同步的。如果多个线程同时访问链接的哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须保持外部同步。这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象,则应该使用Collections.synchronizedMap方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射的意外的非同步访问:
Map m =Collections.synchronizedMap(new LinkedHashMap(...));
结构修改是指添加或删除一个或多个映射关系,或者在按访问顺序链接的哈希映射中影响迭代顺序的任何操作。在按插入顺序链接的哈希映射中,仅更改与映射中已包含键关联的值不是结构修改。在按访问顺序链接的哈希映射中,仅利用get 查询映射不是结构修改。)
Collection(由此类的所有 collection 视图方法所返回)的 iterator 方法返回的迭代器都是快速失败的:在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器自身的 remove 方法,其他任何时间任何方式的修改,迭代器都将抛出ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来不确定的时间任意发生不确定行为的风险。
注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。
此类是 JavaCollections Framework 的成员。
从以下版本开始: 1.4
另请参见: Object.hashCode(),Collection,Map, HashMap, TreeMap, Hashtable, 序列化表格
使用LinkedHashMap底层采用哈希表和链表实现。
public voidtest8(){
LinkedHashMap h=new LinkedHashMap();
h.put(newPerson3("会话"), 12);
h.put(newPerson3("会2话"), 152);
h.put(newPerson3("会3话"), 125);
h.put(newPerson3("会4话"), 142);
System.out.println(h);
}
十三、 TreeMap
java.util
类 TreeMap<K,V>
java.lang.Object
java.util.AbstractMap<K,V>
java.util.TreeMap<K,V>
类型参数: K - 此映射维护的键的类型 V - 映射值的类型
所有已实现的接口: Serializable, Cloneable,Map<K,V>, NavigableMap<K,V>, SortedMap<K,V>
public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>,Cloneable, Serializable
基于红黑树(Red-Black tree)的 NavigableMap 实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的Comparator 进行排序,具体取决于使用的构造方法。
此实现为 containsKey、get、put 和 remove 操作提供受保证的 log(n) 时间开销。这些算法是 Cormen、Leiserson 和 Rivest 的Introduction to Algorithms 中的算法的改编。
注意,如果要正确实现 Map 接口,则有序映射所保持的顺序(无论是否明确提供了比较器)都必须与 equals 一致。(关于与 equals 一致 的精确定义,请参阅Comparable 或 Comparator)。这是因为 Map 接口是按照 equals 操作定义的,但有序映射使用它的 compareTo(或 compare)方法对所有键进行比较,因此从有序映射的观点来看,此方法认为相等的两个键就是相等的。即使排序与 equals 不一致,有序映射的行为仍然是 定义良好的,只不过没有遵守 Map接口的常规协定。
注意,此实现不是同步的。如果多个线程同时访问一个映射,并且其中至少一个线程从结构上修改了该映射,则其必须外部同步。(结构上的修改是指添加或删除一个或多个映射关系的操作;仅改变与现有键关联的值不是结构上的修改。)这一般是通过对自然封装该映射的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用Collections.synchronizedSortedMap方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射进行意外的不同步访问,如下所示:
SortedMap m =Collections.synchronizedSortedMap(new TreeMap(...));
collection(由此类所有的“collection 视图方法”返回)的 iterator 方法返回的迭代器都是快速失败 的:在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器自身的remove 方法,否则在其他任何时间以任何方式进行修改都将导致迭代器抛出ConcurrentModificationException。因此,对于并发的修改,迭代器很快就完全失败,而不会冒着在将来不确定的时间发生不确定行为的风险。
注意,迭代器的快速失败行为无法得到保证,一般来说,当存在不同步的并发修改时,不可能作出任何肯定的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的做法是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测 bug。
此类及其视图中的方法返回的所有 Map.Entry 对都表示生成它们时的映射关系的快照。它们不支持 Entry.setValue 方法。(不过要注意的是,使用 put 更改相关映射中的映射关系是有可能的。)
此类是 JavaCollections Framework 的成员。
从以下版本开始: 1.2
另请参见: Map, HashMap, Hashtable, Comparable, Comparator, Collection,序列化表格
使用TreeMap保存自定义对象,如果需要排序,请在创建TreeMap时传入比较器或者将自定义对象的类继承Comparable接口。
创建自定义类
public class Student {
private Stringname;
private intscore;
public Student(Stringname, intscore){
super();
this.name =name;
this.score =score;
}
public String getName() {
return name;
}
public voidsetName(String name){
this.name =name;
}
public intgetScore() {
return score;
}
public voidsetScore(intscore){
this.score =score;
}
@Override
public String toString() {
return "Student [name=" + name + ", score="+ score+ "]";
}
}
定义比较器和使用TreeMap保存自定义对象
Comparator c=new Comparator(){
public int compare(Object o1, Object o2) {
Student s1=(Student)o1;
Student s2=(Student)o2;
return (int)s1.getScore()-s2.getScore();
}
};
@Test
public voidtest9(){
TreeMap t=new TreeMap(c);
t.put(new Student("会话",1324),"哈哈");
t.put(new Student("会话",13),"哈哈");
t.put(new Student("会话",124),"哈哈");
t.put(new Student("会话",14),"哈哈");
Set s=t.keySet();
for(Iteratori=s.iterator();i.hasNext();){
System.out.println(i.next());
}
}
- collectionAndMap
- Ubuntu16.04安装有道词典
- 解决ligerUI关闭子窗口,无法刷新父界面的方法
- Elasticsearch学习笔记之二(ES集群)
- eclipse 修改contextroot导致spring springmvc加载2次
- Oracle闪回-非当前的还原表空间的恢复
- collectionAndMap
- 安卓TextView文字悬浮阴影效果实现
- 测试java
- UVa 1592 Database
- 通过串口log信息解决无限重启问题——空指针异常
- 2017开学训练第五周周中总结
- 解决CentOS下无法发送邮件的问题 + selinux
- 机器学习中的正则化
- Spring源码学习之认识AOP