You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

9.8 KiB

数据结构在程序中非常重要。简单来说数据结构就是在计算机语言如何组织和操作数据。在C中数据结构与操作是分离的因为Java是面向对象的因此对常用的数据结构进行了封装。这一章我们讨论简单的有序数据结构在Java中叫容器的使用。

1. Collection

A collection is a container object that holds a group of objects, often referred to as elements. The Java Collections Framework supports three types of collections, named lists, sets, and maps.

collection 是一个存放一组对象元素的容器。Java的Collection框架包括 List列表有点像ArraySet集合无序且唯一和Mapkey-value的对应关系

image-20230331084818875

上图中虚线部分是接口关系实线部分是类的关系。Set、List、Queue都以接口的方式给出的。

引用网上一个比较完整的类图:容器框架概述

img

上面的关系描述看起来比较复杂,大家尽量去理解就可以了,不用去记。下面会使用到一部分这个图的关系进行分析。

1.1. Collection接口

image-20230331085927484

Collection接口由Iterator接口扩展而来。Iterator可以翻译成迭代器还是什么反正看接口可以大概理解成一个链表的操作接口。

了解Collection接口中的基本操作就可以了一般是add、clear、contains、equals、remove这些。你会发现和前面学习的ArrayList的操作非常类似仔细看类图可以发现ArrayList是间接实现了 Collection 这个接口。

注意List接口并不包含数据有序性质的操作因此该接口并不保证存储数据的有序性可能是无序的就是说没有Index的概念。

1.2. List接口

A list stores elements in a sequential order, and allows the user to specify where the element is stored. The user can access the elements by index.

List 接口存放的元素是有序的,并且允许用户指定元素的存放位置。可以通过 index 对元素进行访问。

image-20230331091840919

List接口在Collection 的基础上更进一步加入了有序操作的能力有index的操作了。注意这里有listIterator的函数接口这是Iterator接口的扩展因此具备有序遍历的特性。

关于listIterator的接口如下

image-20230331093129625

这个接口现阶段不用关心太多只需要知道List的遍历是有序的就可以了例如使用特有的for循环可以保证元素是有序取出的。

2. ArrayList 和 LinkedList

The ArrayList class and the LinkedList class are concrete implementations of the List interface. Which of the two classes you use depends on your specific needs. If you need to support random access through an index without inserting or removing elements from any place other than the end, ArrayList offers the most efficient collection. If, however, your application requires the insertion or deletion of elements from any place in the list, you should choose LinkedList. A list can grow or shrink dynamically. An array is fixed once it is created. If your application does not require insertion or deletion of elements, the most efficient data structure is the array.

这两个类的使用几乎完全一致只不过ArrayList是对读进行优化的通过Index访问元素LinkedList是对写进行优化的通过Index插入和修改元素

import java.util.*;

public class TestArrayAndLinkedList {
	public static void main(String[] args) {
		List<Integer> arrayList = new ArrayList<Integer>();				// List是一个接口哦
		arrayList.add(1); // 1 is autoboxed to new Integer(1)
		arrayList.add(2);
		arrayList.add(3);
		arrayList.add(1);
		arrayList.add(4);
		arrayList.add(0, 10);
		arrayList.add(3, 30);

		System.out.println("A list of integers in the array list:");
		System.out.println(arrayList);

		LinkedList<Object> linkedList = new LinkedList(arrayList);
		linkedList.add(1, "red");
		linkedList.removeLast();
		linkedList.addFirst("green");

		System.out.println("Display the linked list backward:");
		for (int i = linkedList.size() - 1; i >= 0; i--) {
			System.out.print(linkedList.get(i) + " ");
		}
	}
}

这个例子可以看到,这两个类型的使用几乎完全一样。

这里注意:List<Integer> arrayList = new ArrayList<Integer>();真实的对象是ArrayList引用变量的类型是List看看本章节的开始部分List是一个接口ArryList实现了List这个接口因此可以使用接口来操作ArrayList对象。

AbstractList<Integer> arrayList = new ArrayList<Integer>();
ArrayList<Integer> arrayList = new ArrayList<Integer>();

上面这样的赋值也是没有问题的因为后面的代码只使用了List接口的功能能力。但是如果这样

Collection<Integer> arrayList = new ArrayList<Integer>();
arrayList.add(1); // 1 is autoboxed to new Integer(1)
arrayList.add(2);			// 正确使用Collection接口描述的add函数
arrayList.add(3);			// 正确使用Collection接口描述的add函数
arrayList.add(1);			// 正确使用Collection接口描述的add函数
arrayList.add(4);			// 正确使用Collection接口描述的add函数
arrayList.add(0, 10);		// 错误Collection 没有这样的函数能力描述
arrayList.add(3, 30);		// 错误Collection 没有这样的函数能力描述

那么arrayList.add(0, 10);这样的代码将错误因为能力描述是Coolection虽然对象是ArrayList具备add(0, 10)这样的能力但是arrayList的变量类型是Collection并不知道对象有这样的能力因此如果要使用add(0, 10)这样的能力,需要进行类型转换:

Collection<Integer> arrayList = new ArrayList<Integer>();
arrayList.add(1); // 1 is autoboxed to new Integer(1)
arrayList.add(2);
arrayList.add(3);
arrayList.add(1);
arrayList.add(4);
		
if (arrayList instanceof List) {
	List list = (List) arrayList;			
	list.add(0,10);
	list.add(3,30);
}

对象的类型任然是一个ArrayList变量arrayList 和 list 都引用同一个对象arrayList变量的类型是 Collection 因此不能使用 add(0, 10) 这样的能力Collection中没有这样的能力描述list 变量的类型是 List 因此可以使用 add(0, 10) 这样的能力List中有这样的能力描述

3. Stack

image-20230331154437973

关于Stack的使用这里不做过多的描述前面有这样的例子。只不过这个类型是Java的SDK提供的可以直接使用。我们看看下面的代码

import java.util.Stack;
public class Test {

	public static void main(String[] args) {
		Stack<Integer> stack = new Stack();
		stack.add(1);
		stack.add(2);
		stack.add(3);
		stack.add(0, 0);

		for (Integer i : stack) {
			System.out.println(i);
		}
	}
}

这段代码其实把Stack当成了List来使用看看本章开始的类图关系就知道了Stack其实是实现了List的接口的或者是这样

import java.util.List;
import java.util.Stack;
public class Test {

	public static void main(String[] args) {
		List<Integer> stack = new Stack();
		stack.add(1);
		stack.add(2);
		stack.add(3);
		stack.add(0, 0);
		stack.remove(3);

		for (Integer i : stack) {
			System.out.println(i);
		}
	}
}

这样也不会出错,想一想为什么。

4. Queue

image-20230331154505340

import java.util.*;

public class PriorityQueueDemo {
	public static void main(String[] args) {
		PriorityQueue<String> queue1 = new PriorityQueue<String>();
		queue1.offer("Oklahoma");
		queue1.offer("Indiana");
		queue1.offer("Georgia");
		queue1.offer("Texas");

		System.out.println("Priority queue using Comparable:");
		while (queue1.size() > 0) {
			System.out.print(queue1.remove() + " ");
		}

		PriorityQueue<String> queue2 = new PriorityQueue<String>(4, Collections.reverseOrder());
		queue2.offer("Oklahoma");
		queue2.offer("Indiana");
		queue2.offer("Georgia");
		queue2.offer("Texas");

		System.out.println("\nPriority queue using Comparator:");
		while (queue2.size() > 0) {
			System.out.print(queue2.remove() + " ");
		}
	}
}

这个例子很有意思作为了解不需要大家掌握。PriorityQueue在添加数据的时候会对数据进行排序因此如果取队列取出的顺序并不是按照先进先出的原则的而是排序后最小的先被取出。因此结果是

Georgia Indiana Oklahoma Texas 

后面一段在构建队列的时候,有个参数Collections.reverseOrder()表示反向,因此结果就变成了:

Texas Oklahoma Indiana Georgia 

如果严格按照队列的先进先出,应该使用非排序的队列,例如:

import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;

public class PriorityQueueDemo {
	public static void main(String[] args) {
		Queue<String> queue1 = new LinkedBlockingQueue();
		queue1.offer("Oklahoma");
		queue1.offer("Indiana");
		queue1.offer("Georgia");
		queue1.offer("Texas");

		while (queue1.size() > 0) {
			System.out.print(queue1.remove() + " ");
		}

	}
}

这样的结果就是先进先出了:

Oklahoma Indiana Georgia Texas 

5. 本章重点

  1. 本章大多类容作为了解通过本章可以大约了解Java中关于接口和抽象类的使用方式。考试中可能会出现List是类还是接口这样的题或者List<Integer> list = new ArrayList(); 这样的题是否正确。如果大家大约了解本章开始的类图就可以有正确答案了。
  2. ArrayList的操作需要掌握前面已经说了其他的作为了解就可以了。