Java基础之集合框架

集合与数组都是容器,而它们的最大区别在于:

  • 数组可以存储基本数据类型,也可存储对象,而集合只能存储对象(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中常见的子类

  1. ArrayList数组数据结构,查询速度快,修改速度慢,线程不同步。
  2. LinkedList链表数据结构,查询速度慢,修改速度快。

特有方法:addFirst(elem); addLast(elem); removeFirst(); removeLast(); getFirst(); getLast(); 1.6之后出现offerFirst(elem); offerLast(elem); pollFist(); pollLast()

Vector数组数据结构,与ArrayList相同,老版本容器,线程同步。枚举是vector的迭代方式,与iterator对象方法相似。

Set接口

Set 中元素不重复,功能操作与collection接口一致。

常见的子类包括

  1. HashSet:数据结构为哈希表,通过先后调用hashcode和equals方法来保证元素唯一性。如果对象的哈希值相同,再调用equals方法来比较,所以如果存入自定义元素时,需要复写这个两个方法。contains和remove方法也如此。
  2. 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>接口有两个常见子类的实现:

  1. HashMap

底层数据结构是hash表,允许使用null值和null键,是不同步的

  1. 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),但是只能做读取操作。

原文地址:https://www.cnblogs.com/cqumonk/p/3995552.html