Java常用集合

242 阅读2分钟

Collection FrameWork

集合框架的层次结构如下图所示,可以看到,基本框架由接口以及组成,它们之间只有继承实现两种关系,所有的集合都包含在java.util包下,在日常coding中,无论使用什么集合,都会import这个package。 image.png

Collection接口

该接口包含每个集合的所有基本方法,例如向集合中添加数据、删除数据、清除数据等。所有这些方法都在此接口中进行声明,而方法的具体实现则与接口无关,Collection接口中拥有这些方法可以确保方法的名称对于所有集合都是通用的。简而言之,Collection接口为实现集合类奠定了基础。

List接口

List是Collection的子接口,该接口专用于列表类型的数据,可以在其中存储对象的有序集合。List接口的实现类允许其中存在重复数据。主要实现类有ArrayList,Stack,Vector,LinkedList,由于其子类都实现于List接口,所以我们可以这样书写。

//T是泛型,即任意的类
List <T> arrayList = new ArrayList<> ()
List <T> linkedList = new LinkedList<> ();
List <T> vector = new Vector<> ();

ArrayList类

ArrayList实现了一个动态数组,在需要对数组进行大量操作的情况下很有帮助。其底层由一个数组实现,当对当前list进行添加删除等操作时,就是对底层的数组进行操作。ArrayList支持随机访问,即可以获取当集合的任意位置的值。ArrayList为了保证效率,并没有采用同步的机制,因此是非线程安全的容器,在有线程安全需求的场景中不要使用。不支持基本数据类型,如int,char等,需要使用包装类。
使用ArrayList时可以通过初始化时给定一个大概的size的方式,减少其反复扩容的操作,可以提高操作效率。

import java.io.*;
import java.util.*;
class ArrayListDemo {
	public static void main(String[] args)
	{
		ArrayList<Integer> al = new ArrayList<Integer>();
		for (int i = 1; i <= 5; i++)
			al.add(i);

		System.out.println(al);

		al.remove(3);

		System.out.println(al);

		for (int i = 0; i < al.size(); i++)
			System.out.print(al.get(i) + " ");
	}
}

输出:

[1, 2, 3, 4, 5] 
[1, 2, 3, 5] 
1 2 3 5

LinkedList类

LinkedList是一个线性的数据结构,底层由一个双向链表实现。其中元素不存储在连续的位置,因此不支持随机访问,每个元素都是一个单独的对象,存储在内存的某一块地址中,元素间使用指针进行连接。非线程安全的数据结构,在有线程安全需求的场景中不要使用,不支持基本数据类型,如int,char等,需要使用包装类。

import java.io.*;
import java.util.*;
class LinkedListDemo {
	public static void main(String[] args)
	{
		LinkedList<Integer> ll = new LinkedList<Integer>();

		for (int i = 1; i <= 5; i++)
			ll.add(i);
		System.out.println(ll);

		ll.remove(3);

		System.out.println(ll);

		for (int i = 0; i < ll.size(); i++)
			System.out.print(ll.get(i) + " ");
	}
}

输出:

[1, 2, 3, 4, 5] 
[1, 2, 3, 5] 
1 2 3 5

Vector类

Vector提供了可操作的动态数组,底层由数组实现。这在实现方面与ArrayList相同,但是vector和ArrayList之间的主要区别在于 Vector是同步的,而ArrayList是非同步的。Vector支持随机访问,便于操作和遍历。Vector是线程安全的容器。

import java.io.*;
import java.util.*;
class VectorDemo {
	public static void main(String[] args)
	{
		Vector<Integer> v = new Vector<Integer>();
		for (int i = 1; i <= 5; i++)
			v.add(i);
		System.out.println(v);
		v.remove(3);
		System.out.println(v);
		for (int i = 0; i < v.size(); i++)
			System.out.print(v.get(i) + " ");
	}
}

输出:

[1, 2, 3, 4, 5] 
[1, 2, 3, 5] 
1 2 3 5

LinkedList,ArrayList,Vector区别

1、从存储结构来看:

  • ArrayList和Vector是由数组构成,其默认的初始容量为10。而LinkedList是由双向链表构成(含头结点)。

2、从线程安全来看:

  • ArrayList和LinkedList是线程不安全的,如果需要在并发的环境下使用它们,可以用Collections类中的静态方法synchronizedList()对ArrayList和LinkedList进行调用即可。
  • Vector是线程安全的,它的源码中的大部分方法都包含关键字sydnchronized。

3、从扩容机制来看

  • LinkedList是没有扩容的,因为是链表实现的,而Vector和ArrayList
  • ArrayList扩容之后的容量是之前的1.5倍(默认)。
  • Vector扩容之后的容量是之前的两倍(默认)。
  • 其中Vector可以设置容量增量,而ArrayList不可以设置。

4、从增删查改的效率来看:

  • ArrayList和Vector:如果是在指定位置检索,或者是在集合尾部进行插入和删除的话,时间复杂度是O(1),但是如果是其他地方插入或者删除的话,时间复杂度就变成了O(n)。
  • LinkedList:它的插入和删除的时间复杂度都是O(1),但是它的检索一个元素的话,时间复杂度就O(n)。

Stack类

image.png Stack实现了栈的数据结构,该类基于后进先出的基本原则实现,继承于Vector。除了基本的push和pop操作外,该类还提供了empty、search和peek三个功能。

import java.util.*;
public class StackDemo {
	public static void main(String args[])
	{
		Stack<String> stack = new Stack<String>();
		stack.push("wish");
		stack.push("you");
		stack.push("be");
		stack.push("happy");
		Iterator<String> itr = stack.iterator();
		while (itr.hasNext()) {
			System.out.print(itr.next() + " ");
		}

		stack.pop();
		itr = stack.iterator();

		while (itr.hasNext()) {
			System.out.print(itr.next() + " ");
		}
	}
}

输出

wish you be happy 
wish you be

Queue接口

Queue接口维护FIFO(First In First Out)原则,即先进先出原则,类似于现实世界的队列行。专用于存储元素顺序重要的所有元素。例如,每当我们尝试预订门票时,门票都会以先到先得的方式出售。请求首先到达队列的人将获得票。主要常用实现类有PriorityQueue优先队列。

Queue <T> pq = new PriorityQueue<> ();

PriorityQueue类

image.png
当对象应该基于优先级进行处理时,使用优先级队列。PriorityQueue基于优先级堆。优先级队列的元素按照自然顺序排序,或者由队列构造时提供的Comparator比较器作为依据进行排序,具体取决于使用的构造函数。PriorityQueue不支持随机访问,同时也是非线程安全的容器。

import java.util.*;
class PriorityQueueDemo {
	public static void main(String args[])
	{
                PriorityQueue<Integer> pQueue = new PriorityQueue<Integer>(Comparator.comparing(Integer::intValue));
		pQueue.add(20);
		pQueue.add(10);
		pQueue.add(15);
                //PriorityQueue类提供了poll和peek两种方法,peek不会弹出元素而poll会将元素弹出
		System.out.println(pQueue.poll());
		System.out.println(pQueue.poll());
		System.out.println(pQueue.poll());
	}
}

输出:

10
15
20

Map接口

Map是一种支持数据键值对映射的数据结构,一个Key值对应一个对象,不支持重复键,一个键值只能对应一个值,适用于一对一关系或一对多关系的场景下。常用的实现类有HashMap,TreeMap

Map<T> hm = new HashMap<> ();
Map<T> tm = new TreeMap<> ();

HashMap类

HashMap put过程

未命名文件.jpg
HashMap是KV结构的实现,底层由链表和红黑树组成,当链表长度大于8或数组长度大于64时,底层的存储结构会由数组+链表的形式转换为红黑树的存储结构。要访问HashMap中的元素就必须知道其Key值,通过Key值才能访问到内部元素,同时HashMap是非线程安全的容器。

import java.util.*;
public class HashMapDemo {
	public static void main(String args[])
	{
                //可以通过KeySet()方法获取所有的键值进行遍历
		HashMap<String,String> hm = new HashMap<>();
                hm.put("FirstName","TianLe");
                hm.put("LastName","z");
                hm.put("LastName","Zhou");
                System.out.printf("My name is : %s %s%n", hm.get("LastName"), hm.get("FirstName"));
	}
}

输出:

My name is:Zhou TianLe

TreeMap类

TreeMap与HashMap一样是KV结构的实现,底层数据结构由红黑树实现,同时实现了SortedMap接口,所以Key的键值是有序的,可以通过Comparator比较器进行自定义排序,适用于对Key值顺序有要求的场景下使用,是非线程安全的容器。

import java.util.*;
public class HashMapDemo {
	public static void main(String args[])
	{
                //按照key值从大到小排列
                TreeMap<Integer,String> tm = new TreeMap<>((o1, o2) -> o2 - o1);
                tm.put(1,"min");
                tm.put(2,"mid");
                tm.put(3,"max");
                System.out.println(tm);
	}
}

输出:

{3=max, 2=mid, 1=min}

Set接口

集合是对象的无序集合,其中不能存储重复值。当希望避免重复对象并希望仅存储唯一对象时使用此集合。主要常用实现类有HashSet,TreeSet

Set<T> hs = new HashSet<> ()
Set<T> ts = new TreeSet<> ()

HashSet

HashSet类是哈希表数据结构的固有实现,其底层由HashMap实现,存储对象不可重复,当进行add操作时,如果当前对象是基本数据类型,则直接进行值比较,如果有容器中已经包含该对象则不会再进行插入,如果是引用对象,则会调用对象的HashCode和Equals方法,因此自定义对象要使用HashSet时,一定要重写HashCode和Equals方法。允许插入null元素,同时HashSet是非线程安全的容器。

import java.util.*;
public class HashSetDemo {
	public static void main(String args[])
	{
            HashSet<Integer> hs = new HashSet<>();
            hs.add(1);
            hs.add(2);
            hs.add(3);
            hs.add(1);
            hs.add(3);
            System.out.println(hs);
	}
}

输出:

[1, 2, 3]

TreeSet

TreeSet底层由TreeMap实现,存储对象不可重复与HashSet原理一致,可以通过重写Comparator比较器进行自定义排序,同时TreeSet是非线程安全的容器。

import java.util.*;
public class TreeSetDemo {
	public static void main(String args[])
	{
            TreeSet<Integer> ts = new TreeSet<>(Comparator.comparing(Integer::intValue));
            ts.add(20);
            ts.add(10);
            ts.add(15);
            System.out.println(ts.first());
            System.out.println(ts.last());
            System.out.println(ts);
	}
}

输出:

10
20
[10, 15, 20]