Java基础回顾 — — 集合框架

集合

在Java 程序设计中,经常会遇到需要使用一个容器来包含众多元素,而各个元素的类型与及集合中的元素个数又是无法预知的情况。我们在此之前一般会使用数组来作为容器来装载元素。但是现在有个问题,就是当元素的个数未知时,如果使用数组的话,一般会将数组的大小初始化的比较大,但是这样有可能造成空间的不足或者浪费,这个时候我们就可以使用集合来当作这个容器。

在Java中,集合类存在于 java.util 包中,这些不同的集合类具有着不同的存储对象的方式,并提供了相对应的方法以便于用户对集合进行增删改查等操作。常用的集合有 List 集合、Set 集合、Map 集合。其中 List 集合和 Set 集合都是继承了 Collection 接口,各接口还提供了不同的实现类。具体的继承关系如下(下图所显示的集合或接口皆为常用的,在这也只介绍常用的集合类(接口)):

集合与数组的对比

相同点:

都是容器。

不同点:

  • 数组在初始化的时候需要声明其大小,而集合不需要。

  • 一旦初始化后,数组的长度是不可变的,而集合可以根据需要动态的改变大小。

  • 数组的存放的类型只能是一种,集合存放的类型可以不是一种(不加泛型时添加的类型是Object)。

  • 数组时Java语言内置的数据类型,是线性排列的,执行效率和类型检查都是最快的。ArrayList 集合就是底层基于数组创建的容器类。

集合与数组的互相转化:

注:集合与数组的互转一般指 Collection 集合与及其子类和数组之间的转化。

  • 集合转化为数组。集合提供了两个方法:
Object[] toArray()      返回一个包含此集合中所有元素的数组。  //返回的集合的类型为 Object 型
<T> T[] toArray(T[] a)  返回包含此集合中所有元素的数组; 返回的数组的运行时类型是指定数组的运行时类型。     // 将集合元素转化数组并将值赋予数组 a。

第一个方法它的返回的数组类型为 Object 类型,不论你的集合泛型为什么类型全部统一转换为 Object 类,我们如果想要其成为与集合一致的类型就必须进行转换,而且不能强行进行转换,否则会出现 java.lang.ClassCastException 异常。

第二种方法较第一种简单,它可以在转化的时候可以自己指定要转化的集合的泛型,推荐使用第二种。

  • 数组转集合,一般指将数组转化为 AarrayList 集合。Arrays 类(数组的工具类)提供了个方法:
static <T> List<T> asList(T...a)    // 静态方法,将指定数组转化为固定长度的 ArrayList 集合
// 注:此种方法转换的集合是具有固定长度的,不具有增删元素的功能,否则系统会报错

如果想使用转化后的集合,就必须另外实例化一个集合,然后将此集合赋值给新实例化的集合。

Collection 接口

Collection 接口是层次结构的根接口。构成 Collection 的单位称作元素,Collection 是一个接口,故不可直接使用,但是它提供了添加元素、删除元素和管理元素的方法。List 接口和 Set 接口作为 Collection 的子接口,故那些方法同样适用于 List 和 Set 集合。其常用方法如下:

void add(Object o)	  将指定的对象添加到该集合中
boolean remove(Object o)  将指定的对象从该集合中删除
boolean isEmpty()	  返回布尔值,用于判断该集合是否为空
Iterator<E> iterator()    返回在此 Collection 的元素上进行迭代的迭代器。用于遍历集合中的对象
int size()	          返回 int 类型的值,获取该集合中的元素个数

List 集合

List 集合包括 List 接口与及 List 接口的所有实现类。List 集合中的元素允许重复,各元素的顺序就是对象的插入顺序。类似于 Java 的数组,用户可以通过索引(元素在集合中的位置)来访问集合中的元素。

List 接口

List 接口继承了 Collection 接口,故其包含 Collection 接口的所有方法,除此之外,其还定义了另外两种重要的方法:

E get(int index)                获取集合中指定位置的元素
E set(int index, E element)     替换集合中指定位置的元素,并将被替换的元素返回。

List 接口的实现类

由于 List 接口不能被直接实例化,因此在 JDK 中提供了其实现子类。其中最常见的子类有 ArrayList 类和 LinkedList 类,其区别如下。

  • LinkedList 类是底层基于链表创建的容器类,即利用链表来保存集合中的元素。链表的优点就是便于向集合中插入和删除对象元素,因此在往集合中增删元素的操作的时候,其效率是比较高的。但是链表的缺点就是查询时的效率是比较低的
  • ArrayList 类是底层基于数组创建的容器类,即利用数组来保存集合中的所有元素,故继承了数组的一些优点,如根据索引位置对集合的数据进行快速的随机访问查询效率得到了大大的提升。但同样的,当要想在指定位置插入元素或者删除指定位置的元素时速度相对较慢,因为当它在添加或者删除元素的时候,虚拟机会创建一个新的数组,降低了其效率。故当需要往集合中大量的做增删操作的时候,ArrayList 并不适合使用。

在实例化 List 接口对象的时候,一般优先选择 ArrayList 集合,只有当你知道这个集合的增删操作特别频繁的时候,才使用 LinkedList 集合。

迭代器 Iterator

迭代器 Iterator 是一个接口,不饿能被直接使用。常被使用于集合遍历的时候。在之前我们知道要遍历一个容器中的元素的时候,我们可以使用 for 循环来遍历集合,但在 java.util 包中提供了一个 Iterator 接口,该接口是一个专门为集合迭代元素设计的迭代器,使用此迭代器迭代集合中的元素将使得集合的迭代遍历效率更加快捷。其常用的方法:

boolean hasNext()    如果迭代器中还有元素迭代的话,返回值为true
E next()             返回迭代的下一个元素
void remove()        从迭代器指向的 Collection 中移除迭代器返回的最后一个元素(可选操作)

这些方法就是迭代器中常说的“问”、“取”、“删”操作。当我们在遍历迭代器中的元素的时候(初始时,指向元素的指针不指向第一个元素),我们一般先“询问”迭代器中是否含有元素(将指针移至下一个元素,第一次移动即指向第一个元素),如果含有元素(指针指向的元素不为 null),我们则“取出”该元素(next 方法返回该元素的值),然后再次“询问”是否含有下一个元素,以此循环遍历。故使用迭代器遍历元素的时候我们一般选择 while() 循环。

Set 集合

Set 集合中的对象元素是不以特定的顺序排序的,它只是简单的将元素加入到集合当中,但是 Set 集合中不能存在重复的元素。Set 集合由 Set 接口与及 Set 接口的实现类组成。

Set 接口

Set 接口除了继承自 Collection 接口的所有构造方法以及 add、equals 和 hashCode方法之外,它还增加了一些其他规定,它必须创建一个不包含重复元素的集合。因此。在向 Set 集合中添加元素的时候,它首先判断该元素是否已经存在,然后再确定是否执行添加操作,如图所示:

Set 接口的实现类

Set 接口的实现类中常用的类由 HashSet 类和 TreeSet 类。

  • HashSet 集合是 Set 接口的一个实现类,它不允许有重复的元素。HashSet 的主要依据来在于哈希算法直接将元素指定到一个地址,然后当向该集合中插入元素的时候,它会先自动调用 equals 方法来判断该指定位置是否存在重复元素。判断是通过它们的 HashCode 来进行比较的。HashSet 集合中的常用方法都是重写了 Set 接口中的方法,此集合允许保存 null 值。
  • TreeSet 集合不仅实现了 Set 接口,还实现了 java.util.SortedSet 接口,因此 TreeSet 类实现的 Set 集合在遍历集合时按照自然排序的递增顺序进行遍历元素。当然,我们也可以让它以我们制定的排序规则进行排序。此集合不能保存 null 值。
TreeSet 中增加的方法:
E first()                             返回此 Set 集合中当前第一个(最低)元素
E last()                              返回此 Set 集合中当前最后一个(最高)元素
Comparator<? super E> comparator()    返回对此 Set 中的元素进行排序的比较器。如果此 Set 使用的是自然排序,则返回null
SortedSet<E> headSet(E toElement)     返回此集合的部分的视图,其元素严格小于 toElement。 
SortedSet<E> subSet(E fromElement, E toElement) 返回此集合的部分的视图,其元素的范围从 fromElement(含)到 toElement。 
SortedSet<E> tailSet(E fromElement)   返回此组件的元素大于或等于 fromElement的部分的视图。 

比较器,即 Comparator 接口,它提供了一个抽象方法 compare(T o1,T o2),这个方法指定了两个对象的比较规则,如果o1大于o2,方法返回整数(通常为+1);如果o1小于o2,方法返回负数(通常为-1);如果o1等于o2,方法返回0。

另外还有一个接口 Comparable ,它也能实现比较规则。它提供了 compare to(T o)抽象方法,将调用方法的对象与参数对象进行比较,返回值的规则与上面的 Comparator.compare() 方法相同。

如果想制定 TreeSet 的排序规则,可以在实现这个对象的时候,将一个已经写好的构造器作为 TreeSet 的构造参数传入,或者让 TreeSet 中的所有元素实现 Comparator 接口。

HashSet 类和 TreeSet 类都是 Set 接口的实现类,它们当中都不允许有重复的元素出现。但不同的是,HashSet 不在意元素之间的顺序,而 TreeSet 类则在希望按照元素的自然顺序(自然顺序的意思是与插入顺序无关,而是与元素本身的内容有关)进行排序使用(或者是自定义的顺序)。

Collections 集合

Collections 集合为 Collection 集合的常用工具类,包含了对集合进行操作的多态算法,此类仅由静态方法组成。其常用的方法:

static void reverse(List<?> list) 反转指定列表中元素的顺序。

Map 集合

Map 中文意思为地图。我们知道在地图中的每一个点上的东西(事物)都有一个特定的坐标与之相对应,例如我们为什么在地图上能找到我们的家,因为因为我们可以根据经纬度唯一定位到该地址。这就是一对一的关系,其实在我们生活中有很多这种关系,例如每个人只有唯一的身份证号码对应,每辆车都有唯一的车牌号与之对应。在 Java 中我们就用 Map 集合来存储具有这样对应的关系。Map 是接口,但是它没有继承 Collection 接口,它提供的是 key 到 value 的映射,Map 中不能包含相同的 key ,但可以包含相同的 value,每个 key 只能映射到一个 value。另外,key 还决定了存储对象在映射中的存储位置,但不是由 key 对象本身决定的,而是通过一种“散列技术”进行处理,产生一个散列码的整数值来确定存储对象在映射中的存储位置,Map 集合包括 Map 接口与及其所有的实现子类。

Map 接口

Map 接口提供了将 key 映射到值的对象。一个映射不能包含重复的 key,每个 key 最多只能映射到一个 value。Map 中常用的方法如下:

V put(K key, V value)                 向集合中添加指定的 key 和 value 的映射关系。如果 key 已存在,则会替换掉已经存在的 value 值,并返回。
boolean containsKey(Object key)       如果此映射包含指定键的映射,则返回 true。
boolean containsValue(Object value)   如果此 map 将一个或多个键映射到指定的值,则返回 true。
V get(Object key)                     返回到指定键所映射的值,或 null 如果此映射包含该键的映射。
Set<K> keySet()                       返回此 Map 中包含的键的Set视图。
Set<Map.Entry<K,V>> entrySet()        返回此 Map 中包含的映射的Set视图,此方法一般在遍历 Map 集合时使用较多。
Collection<V> values()                返回此 Map 中包含的值的Collection视图。 

Map 实现子类

Map 接口的常用的实现类有俩个,分别为 HashMap 类和 TreeMap 类。

  • HashMap 类是基于哈希表的 Map 接口实现的,此实现提供了其所有的可选的映射操作,并允许使用 null 键和 null 值,但必须保证键的唯一性。HashMap 通过哈希表对其内部的映射关系进行快速查找。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
  • TreeMap 类不仅实现了 Map 接口,它还实现了 java.util.SortedMap 接口,因此,集合中的映射关系具有一定的顺序。但在添加、删除和定位映射关系的时候,TreeMap 类的性能较弱。由于 TreeMap 类实现的 Map 集合中的映射关系是根据键对象按照一定的顺序排列的,因此不允许出现 null 键。

由于 HashMap 的添加、删除映射关系的时候效率较高,因而在使用 Map 集合的时候,一般优先使用 HashMap 集合实现 Map 接口,除非希望所创建的 Map 集合中的对象具有一定的顺序时使用 TreeMap 集合。

Map 集合的遍历

Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1, "a");
map.put(2, "b");
map.put(3, "ab");
map.put(4, "ab");
map.put(4, "ab");// 和上面相同 , 会自己筛选
System.out.println(map.size());

Map 集合的遍历方法有四种(如对上面的 Map 进行遍历):

  • 通过 keySet 方法得到所有的 key 的集合。然后再通过 key 访问到对应的 value值。进行遍历
Set<Integer> set = map.keySet();     //得到所有key的集合
for (Integer in : set) { 
    String str = map.get(in);
    System.out.println(in + "     " + str); 
}
  • 通过 Map.entrySet()方法返回的 Set 集合,然后对该 Set 集合转为迭代器进行迭代遍历;
Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry<Integer, String> entry = it.next();
    System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
  • 通过 Map.entrySet()遍历 key 和 value 值,推荐;
for (Map.Entry<Integer, String> entry : map.entrySet()) {
    int key = entry.getKey();
    String value = entry.getValues();
    System.out.println("key= " + key + " and value= " + value);
}
  • 通过 Map.values 方法得到所有的 value 的集合,然后对集合进行遍历。此方法不能遍历出所有的 key。
for (String v : map.values()) {
    System.out.println("value= " + v);
}

集合的使用场合

  • List 集合关注的是索引,其元素是顺序存放的,注重的是有序、重复。例如一个班的学生成绩,成绩是可以重复的,就可以使用 List 集合进行存取。
  • Set 集合关注的是对象元素的唯一性,它的值不允许重复,其注重的是无序、不重复。例如一个班的学号,学号是不可重复,是唯一的。
  • Map 集合关注的唯一的标识符(KEY),它将唯一的键映射某一个元素。例如一个班的学号与及姓名,学号是为唯一的,但姓名是可以重复的。

集合的具体继承关系图(取自百度百科):

集合中常见的题:

  • Java 中 Set 、 List 和 Map 有什么不同?

答:

  • Set 可以允许重复的对象;可以插入多个null元素;是一个有序容器,保持了每个元素的插入顺序,输出的顺序就是插入的顺序;常用的实现类有 ArrayList、LinkedList 和 Vector。ArrayList 最为流行,它提供了使用索引的随意访问,而 LinkedList 则对于经常需要从 List 中添加或删除元素的场合更为合适。
  • List 不允许重复对象;无序容器,你无法保证每个元素的存储顺序,TreeSet通过 Comparator  或者 Comparable 维护了一个排序顺序;只允许一个 null 元素;Set 接口最流行的几个实现类是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基于 HashMap 实现的 HashSet;TreeSet 还实现了 SortedSet 接口,因此 TreeSet 是一个根据其 compare() 和 compareTo() 的定义进行排序的有序容器。
  • Map不是collection的子接口或者实现类。Map是一个接口;Map 的 每个 Entry 都持有两个对象,也就是一个键一个值,Map 可能会持有相同的值对象但键对象必须是唯一的; TreeMap 也通过 Comparator  或者 Comparable 维护了一个排序顺序; Map 里你可以拥有随意个 null 值但最多只能有一个 null 键;Map 接口最流行的几个实现类是 HashMap、LinkedHashMap、Hashtable 和 TreeMap。(HashMap、TreeMap最常用)

  • HashMap和Hashtable之间的区别?

答:

  • HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。
  • HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
  • Comparable 和 Comparator的不同之处?

答:

  • Comparator位于包java.util 下,而Comparable位于包   java.lang 下
  • Comparable 自然排序(实体类实现);Comparator 是定制排序(无法修改实体类时,直接在调用方创建)。
  • Arraylist 、LinkedList 与 Vector 的区别?

答:

  • LinkedList 类不是线程安全的,其底层是基于双链表实现的,故其对于元素的插入、删除的效率较高
  • ArrayList 类不是线程安全的,它的底层是基于数组操作的,故其对元素的查找效率较高,较适合随机访问。
  • Vector 类是同步的,它的一些方法保证了 Vector 中的对象是线程安全的,故其性能较差。其底层也是基于数组来操作的。
  • 集合与数组之间的转换

答:

  • 集合转数组:JDK 提供了两个方法 toArray() 和 toArray(T[] a),前者将集合转为Object数组,后者转变为指定类型数组。
  • 数组转集合:数组的工具类 Arrays 提供了 asList 方法,其返回值为 List 集合。但是返回的集合具有固定的大小,不能对其进行增删操作。
  • 还有个笨方法:当在遍历集合(数组)的时候,将元素添加到数组(集合)中。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值