Java集合类的概述

前述

  复习一下Java中的集合类,是面试笔试中常考察的一个点,特地做的整理。

什么是集合类?

  集合类,也叫容器类。Java集合类可以用来存储数量庞大的对象。

  我们和数组进行对比:

  数组:存储基本数据类型,数据类型单一,长度固定,不能动态增大容量。

  集合:存储的即可是基本类型的值,也可以是对象,可以存储多种数据类型,长度可变,可以动态增大容量。

 

Java集合类的体系

  Java集合类主要有两个接口派生而出:Collection和Map。即集合类都是实现的这两个接口。我们在实际编程中经常使用的有 List、Set、Queue(这些是实现的 Collection 接口)HashMap、TreeMap、HashTable(这些实现的 Map 接口)

Collection接口结构

  Collection 接口位于 Java.util 包下,是一个父接口, List、Set、Queue 都是实现的 Collection 接口。Collection 做为父接口提供一些操作集合类的方法,因此它的子接口也有这些方法。

  Collection 接口不能被实例化,并且在实际的编程过程中几乎不会使用它进行数据的存储。

Java集合类的概述_第1张图片

Map接口结构

  Map 接口实现的是键值对的存储,类似 python 中的 dict。

  Map中比较常见的是 HashMap、TreeMap、Hashtable 类。

Java集合类的概述_第2张图片

 

Set接口

  Set 接口继承自 Collection 接口,Collection 接口有的方法 Set 接口中也有。

  Set 接口自身的特性:

    • 不允许重复元素
    • 不区分先后顺序(无序)
    • 允许值是 null

  Set 接口有两个比较常用的具体实现,HashSet、TreeSet。下面分别说一下这两个集合类。

HashSet

  主要特点是快速查找元素。HashSet 是基于 Hash 算法来实现的,在每次添加新的对象的时候,会根据散列码来判断对象是否重复,散列码的获取是通过 Object 的 hashCode() 来实现的。同样 HashSet 也是无序的。

 1 import java.util.*;
 2 /**
 3  * @author jyroy
 4  * HashSet使用
 5  */
 6 public class HashSetDemo {
 7     public static void main(String[] args) {
 8         HashSet hashSet = new HashSet();
 9         hashSet.add("Tom");
10         hashSet.add("Jack");
11         hashSet.add("Roy");
12         System.out.println(hashSet);
13     }
14 }

  当有重复元素添加时,结果如下

 1 import java.util.*;
 2 /**
 3  * @author jyroy
 4  * HashSet使用
 5  */
 6 public class HashSetDemo {
 7     public static void main(String[] args) {
 8         HashSet hashSet = new HashSet();
 9         hashSet.add("Tom");
10         hashSet.add("Tom");
11         hashSet.add("Jack");
12         hashSet.add("Roy");
13         System.out.println(hashSet);
14     }
15 }

 

TreeSet

  主要特点是会进行自然排序,同样不能重复。参考如下的代码,可以看出 TreeSet 把原本无序的值进行了重新排序,依据的就是自然排序。

 1 import java.util.*;
 2 public class TreeSetDemo {
 3     public static void main(String[] args) {
 4         TreeSet treeSet = new TreeSet();
 5         treeSet.add("Tom");
 6         treeSet.add("Jack");
 7         treeSet.add("Roy");
 8         System.out.println(treeSet);
 9     }
10 }

 

  然而,在实际开发中很多时候我们很多时候遵循的不是自然排序,而是有自己定义的排序规则,我们要做的就是新建一个类并且实现 compareTo() 方法。

  下面的代码实现的是根据年龄由小到大进行排序,比较简单,一看就懂的代码。

 1 import java.util.TreeSet;
 2 class Person implements Comparable{
 3     public String name;
 4     public int age;
 5     @Override
 6     public String toString() {
 7         return "Person [name=" + name + ", age=" + age + "]";
 8     }
 9     @Override
10     public int compareTo(Object o) {
11         Person person = (Person) o;
12         if(this.age < person.age) {
13             return -1;
14         }else if(this.age > person.age) {
15             return 1;
16         }else {
17             return 0;
18         }
19     }
20 }
21 public class TreeSetDemoCompareto {
22     public static void main(String[] args) {
23         TreeSet treeSet = new TreeSet();
24         Person person1 = new Person();
25         Person person2 = new Person();
26         Person person3 = new Person();
27         person1.name = "Tom";
28         person1.age = 20;
29         person2.name = "Jack";
30         person2.age = 21;
31         person3.name = "Roy";
32         person3.age = 22;
33         treeSet.add(person1);
34         treeSet.add(person2);
35         treeSet.add(person3);
36         System.out.println(treeSet);
37     }
38 }

 

List接口

  List 接口继承了 Collection 接口,Collection 接口有的方法 List 中也有。

  List 接口自身的特性:

    • 利用数组方式提供了获取、修改、删除的功能。
    • 可以通过方法来获取元素的位置。
    • 允许重复元素的有序集合。

  List 接口有两个比较常用的具体实现,ArrayList、Vector、LinkedList。下面分别说一下这两个集合类。

ArrayList

  ArrayList 本身是通过数组的方式实现的,大小可以随着对象的增加而增加。查询效率比较高,增加、删除的效率比较低,这也是数组的一种特性。非线程安全的。而且是有序的。

 1 import java.util.*;
 2 /**
 3  * @author jyroy
 4  */
 5 public class ArrayListDemo {
 6     public static void main(String[] args) {
 7         List arrayList = new ArrayList();
 8         arrayList.add("Tom");
 9         arrayList.add("Jack");
10         arrayList.add("Roy");
11         System.out.println(arrayList);
12     }
13 }

 

Vector

  Vector 和 ArrayList 是相似的,都是通过数组来实现的。

  最大的区别就是线程安全方面。Vector 是线程安全的,同步的,Vector类对集合的元素操作时都加了synchronized,保证线程安全。而 ArrayList 是非线程安全的。也是因为线程安全的问题,ArrayList 的查询效率比 Vector 要高。

  另外一个区别是在扩容方面,Vector默认扩容是增长一倍的容量,Arraylist是增长50%+1的容量。

  Vector与ArrayList的remove,add(index,obj)方法都会导致内部数组进行数据拷贝的操作,这样在大数据量时,可能会影响效率。

 1 import java.util.*;
 2 public class VectorDemo {
 3     public static void main(String[] args) {
 4         List vectorList = new Vector();
 5         vectorList.add("Roy");
 6         vectorList.add("Tom");
 7         vectorList.add("Jack");
 8         System.out.println(vectorList);
 9     }
10 }

 

LinkedList

  LinkedList 和 ArrayList、Vector相比,在方法的使用上都是相似的。

  LinkedList 是基于双向链表的,比较利于对象的增加和删除操作,但是对查询的支持不够好,这是链表的特点。 LinkedList 同样是线程不安全的。

 1 import java.util.*;
 2 public class LinkedListDemo {
 3     public static void main(String[] args) {
 4         List linkedList = new LinkedList();
 5         linkedList.add("Roy");
 6         linkedList.add("Jack");
 7         linkedList.remove(0);
 8         linkedList.add("Tom");
 9         System.out.println(linkedList);
10     }
11 }

 

Queue接口

  Queue 是 Collection 的子接口,中文称为队列,特性就是先进先出,先进入队列的对象,在进行删除的时候最先被删除。所有的删除操作都是在队首进行的,所有的插入操作都是在队尾进行的。

 1 import java.util.*;
 2 public class QueueDemo {
 3     public static void main(String[] args) {
 4         Queue queue = new LinkedList();
 5         queue.add("Tom");
 6         queue.add("Roy");
 7         System.out.println(queue.poll());  //出队列操作
 8         queue.add("Jack");
 9         System.out.println(queue);
10     }
11 }

 

HashMap

  HashMap 是基于散列表的 Map 接口的实现类,线程不安全。

  散列表是通过关键码值(Key value)而直接进行访问的数据结构。它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。

  当HashMap中的元素个数超过数组大小(数组总大小length,不是数组中个数size)*loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75,这是一个折中的取值。也就是说,默认情况下,数组大小为16,那么当HashMap中元素个数超过16*0.75=12(这个值就是代码中的threshold值,也叫做临界值)的时候,就把数组的大小扩展为 2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置。

  下面的代码实现了获取key为 2 的元素的 value。

 1 import java.util.*;
 2 public class HashMapDemo {
 3     public static void main(String[] args) {
 4         HashMap hashMap = new HashMap();
 5         hashMap.put(1, "Tom");
 6         hashMap.put(2, "Jack");
 7         hashMap.put(3, "Roy");
 8         System.out.println(hashMap.get(2));
 9         System.out.println(hashMap);
10     }
11 }

 

TreeMap

  TreeMap 是根据红黑树算法实现的,TreeMap最大的特性就是支持自然排序。从下面的代码中也可以非常清晰的看出 TreeMap 利用 key 值进行了自然排序。

  红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。

 1 import java.util.*;
 2 public class TreeMapDemo {
 3     public static void main(String[] args) {
 4         TreeMap treeMap = new TreeMap();
 5         treeMap.put("2", "Tom");
 6         treeMap.put("1", "Roy");
 7         treeMap.put("3", "Jack");
 8         System.out.println(treeMap);
 9     }
10 }

HashTable

  HashTable 和 HashMap 是基本一致的。

  最大的区别是 HashTable 是线程安全的,HashMap 不是。

 1 import java.util.*;
 2 public class HashTableDemo {
 3     public static void main(String[] args) {
 4         Hashtable hashtable = new Hashtable();
 5         hashtable.put("2", "Tom");
 6         hashtable.put("1", "Roy");
 7         hashtable.put("3", "Jack");
 8         System.out.println(hashtable);
 9     }
10 }

 

 

常见问题

  这些问题是在其他一些大佬的博客中看到,做了一个整理,都是 Java 集合类的问题。

  1. ArrayList和LinkedList的特点和区别?

  ArrayList: 底层用数组实现,由于数组可以通过下标直接访问指定索引的元素,因此,ArrayList通过索引查询元素非常快。但由于插入和删除元素时都会进行数组的重新排列,因此,ArrayList的插入和删除操作比较慢。

  LinkedList:底层用链表实现,由于链表没有具体的下标,因此,访问某个索引的节点时需要遍历该节点前面的所有元素,速度比较慢。由于插入和删除元素时只需要更新相应元素的指针(或引用),不用重新排列元素,因此,LinkedList对插入和删除操作比较快。

  LinkedList 比 ArrayList消耗更多的内存,因为 LinkedList 中的每个节点存储了前后节点的引用。


  
2. ArrayList和Vector的区别?  

  ArrayList非线程安全,Vector线程安全。在扩容时,ArrayList默认扩容当前容量的50%,但Vector默认扩容当前容量的100%。

 

  3. HashSet和TreeSet的区别? 

  HashSet基于HashMap,用键来存放HashSet的值,由于HashMap的键不能重复,因此,HashSet的值也不会重复,这是集合的一个特点。
  TreeSet基于TreeMap,也是用键来存放TreeSet的值。TreeMap的底层实现是红黑树,其根据键排序,可以得到排好序的数据。

  4. HashMap和HashTable的区别?

  HashMap非线程安全,HashTable线程安全。
  HashMap可以允许一个null键和多个null值,但HashTable不允许,会出现NullPointerException。

 

  5. HashMap和TreeMap的区别?

  HashMap中存放的键是随机的,具有较快的访问和存取速度,TreeMap中的键是按照自然排序排好的。

 

  6. Java集合框架的基础接口有哪些?

  Collection 和 Map ,一个元素集合,一个是键值对集合; 其中 List、Set、Queue 接口继承了 Collection 接口,一个是有序元素集合,一个是无序元素集合,一个是队列; 而 ArrayList 和 LinkedList 实现了 List 接口,HashSet 实现了 Set 接口,这几个都比较常用; HashMap、HashTable、TreeMap 实现了 Map 接口,并且 HashTable 是线程安全的,HashMap 是非线程安全的,但是 HashMap 性能更好。

  

  7. 如何决定选用HashMap还是TreeMap?

   对于在Map中插入、删除和定位元素这类操作,HashMap是最好的选择。然而,假如你需要对一个有序的key集合进行遍历,TreeMap是更好的选择。

  

  8. 哪些集合类提供对元素的随机访问?

  ArrayList、HashMap、TreeMap和HashTable类提供对元素的随机访问。

 

  9. Array和ArrayList有何区别?什么时候更适合用Array?

  Array可以容纳基本类型和对象,而ArrayList只能容纳对象。

  Array是指定大小的,而ArrayList大小是根据内容自动扩张的。

  Array没有提供ArrayList那么多功能,比如addAll、removeAll和iterator等。尽管ArrayList明显是更好的选择,但也有些时候Array比较好用。

  (1)如果列表的大小已经指定,大部分情况下是存储和遍历它们。

  (2)对于遍历基本数据类型,尽管Collections使用自动装箱来减轻编码任务,在指定大小的基本类型的列表上工作也会变得很慢。

  (3)如果你要使用多维数组,使用[][]比List>更容易。

你可能感兴趣的:(Java集合类的概述)