集合与数组都是容器,而它们的最大区别在于:
-
数组可以存储基本数据类型,也可存储对象,而集合只能存储对象(1.5后也可以添加基本数据类型);
-
数组长度固定,而集合长度是可变的,可以存储不同类型的对象。
由于这样的特点,某些场景下集合比数组更适合存储对象。在内存中,数组和集合中存放的都是对象的地址。JAVA的集合框架下有很多容器,不同的容器的数据组织方式和存储方式不同,即不同的数据结构。集合框架的构成关系图如下所示:
集合框架下很多容器的方法都是相同的,下面从collection接口向下介绍集合的方法。
Collection接口
共性方法
-
增:add(Object obj);
-
删:remove(Object obj); clear();
-
改:求交集remainAll(Collection clt); 求差集removeAll(Collection clt)
-
判断: contains(Object obj); isEmpty();
迭代器是取出集合中元素的方法,以内部类的形式定义在集合类的内部,这样取出方式就能直接访问集合中的元素。由于不同的集合容器数据结构不同,所以取出元素的操作也不同,但是都包括取出元素和判断内容等共性操作。方法iterator()返回对应集合的的迭代器对象Iterator,iterator对象有hasNext(),next() ,remove()
collection接口有两个子接口:list和set。list中元素可以重复且元素有序(带有索引),set中元素无序且不可以重复。
List接口
除了上面提到的集合的共有方法,list还有一些特有的方法。由于list带有索引,所以操作索引的方法都是list的特有方法。
-
增:addAll(intdex,Collection); add(index,elem);p
-
删:remove(index);
-
改:set(index,elem);
-
查:get(index); subList(from, to); listIterator(); getIndex(elem);
列表迭代器ListIterator继承了Iterator接口,因为在迭代集合元素时,不能使用集合对象的方法来操作集合元素,所以只能用迭代器的方法来操作集合,Iterator中方法太少,只能通过子接口ListIterator中的方法来操作集合中元素。
ListIterator中常见的方法有add(elem); set(elem); hasPrevious(); nextIndex(); previous(); previousIndex();
List中常见的子类
-
ArrayList数组数据结构,查询速度快,修改速度慢,线程不同步。
-
LinkedList链表数据结构,查询速度慢,修改速度快。
特有方法:addFirst(elem); addLast(elem); removeFirst(); removeLast(); getFirst(); getLast(); 1.6之后出现offerFirst(elem); offerLast(elem); pollFist(); pollLast()
Vector数组数据结构,与ArrayList相同,老版本容器,线程同步。枚举是vector的迭代方式,与iterator对象方法相似。
Set接口
Set 中元素不重复,功能操作与collection接口一致。
常见的子类包括
-
HashSet:数据结构为哈希表,通过先后调用hashcode和equals方法来保证元素唯一性。如果对象的哈希值相同,再调用equals方法来比较,所以如果存入自定义元素时,需要复写这个两个方法。contains和remove方法也如此。
-
TreeSet:数据结构为二叉树,可以对set集合中的元素进行排序。
-
TreeSet排序方式之一:让元素自身具备可比较性,需要实现Comparable接口,覆盖compareTo方法。该方法保证了元素的唯一性,与hash值没关系。这种方式也叫作自然顺序或者默认顺序。
-
TreeSet排序方法之二:当元素自身不具备可比较性,或者具备的可比较性不是所需要的时,需要让集合自身具有可比较性。在集合TreeSet初始化时,构造函数中指定比较器(comparator接口对象)。该比较器实现了comparator接口中的compare方法。
当两种方法都存在的时候,以比较器为主。
泛型
1.5版本之后出现,是一种安全机制。将运行时抛出的类型转换异常,转移到编译时出现,避免了强制类型转换。通常在集合框架中很常见。当类型中所操作的数据类型不确定时,早期定义object类来扩展,现在定义泛型来扩展。
泛型可以定义在类,方法以及接口上,对于类中的静态方法,无法访问类上定义的泛型,因为泛型的类型在对象初始化时才确定,而静态方法在类创建时已经存在。如果静态方法的类型不确定,可以将泛型定义在方法上。
泛型限定
?为通配符,<? extends E>可以接收E类型或者E的子类;<? super E>可以接收E类型或者E的父类。
Map<K,V>接口
将键K映射到值V的对象,Map中的键不能重复,每个键K只对应唯一的值V。
通用方法:
-
增:put(obj,obj);putAll(map<? extends K, ? extends V>)
-
删:clear(); remove(obj k)
-
获取:get(obj);size(); values();entrySet();keyset()
-
判断:contiansKey(obj);containsValue(obj);isEmpty()
-
一次取出所有的键keyset()
将map中的所有键都存入set集合并返回。
-
一次取出所有的值entrySet(),返回类型为Set<Map.Entry<K,V>>
将映射关系存入了集合,而Entry是Map接口中的一个内部接口,
Map<K,V>接口有两个常见子类的实现:
-
HashMap
底层数据结构是hash表,允许使用null值和null键,是不同步的
-
TreeMap
底层数据结构是二叉排序树,根据键排序。
静态类
collections
作为静态工具类,它的方法都是静态的,并没有封装特有数据,专门对集合进行操作。
常见方法:
sort(List<T>)对list进行排序;
max(List<T>)取最大值;
binarySearch(List<T>,T k)二分查找k,如果包含k则返回index,否则返回-(插入点)-1;
reverseOrder()返回比较器,强行逆转了list的自然顺序,重载方法传入一个比较器,可以强行逆转比较器原来的顺序。
这些方法都需要T实现comparable接口或者向方法中传递比较器。
fill(List<T>,T k)将集合中元素全都替换成k
replaceAll(List<T>,T k,T,kn)将List中k都替换成kn
reverse(List<T>)反转list
shuffle(List<T>)使用默认随机源对List元素进行置换,随机排列
多线程同步
集合中的容器大多都是不同步的,为了满足多线程同步,collections静态类中提供了静态方法,返回带有锁的容器。
synchronizedList(List<T>)
synchronizedSet(Set<T>)
synchronizedCollection(Collection<T>)
synchronizedMap(Map<T>)
集合转换数组
为了限定对元素的操作,有时需要将集合转换成数组
toArray(T [])返回一个T类型的数组,传递的数组参数长度小于集合中元素个数时,重新创建合适数组并返回,否则使用参数数组。
高级遍历
JDK1.5后出现抽取迭代器方法到iterable接口,则遍历集合就更加方便了:for(T t: collection c),但是只能做读取操作。