上一篇介绍了 List 接口,旧的子类 Vector,ArrayList 与 Vector 的区别。
接下来介绍一下 LinkedList 类、Queue 接口、Set接口。
一、LinkedList 子类与 Queue 接口
1. LinkedList 子类
LinkedList 表示的是一个链表的操作类,此类的定义如下:
public class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, Serializable
LinkedList 是一个继承于 AbstractSequentialList 的双向链表,实现 List 接口、Deque 接口。它也可以被当作堆栈、队列或双端队列进行操作。实现了 Cloneable 接口,即覆盖了函数 clone(),能够克隆。实现了 java.io.Serializable 接口,这意味着 LinkedList 支持序列化操作,能通过序列化去传输。
ArrayList 底层是由数组支持,而 LinkedList 是由双向链表实现的,其中的每个对象包含数据的同时还包含指向链表中前一个与后一个元素的引用。
2. Queue 接口
Queue 用于模拟队列数据结构,采用“先进先出”的方式。Queue 接口继承了 Collection 的接口,而 Queue 接口下面的实现类是 PriorityQueue,继承的接口是 Deque,Deque 接口的实现类是 ArrayDeque,同时 Deque 还被 LinkedList 类实现。
Queue 接口是 Collection 的子接口,此接口的定义如下:
public interface Queue<E> extends Collection<E>
Queue 接口定义的部分方法:
No | 方法 | 类型 | 描述 |
---|---|---|---|
1 | public E element () | 普通 | 找到链表的表头 |
2 | public boolean offer (E o) | 普通 | 将指定的元素增加到链表的结尾 |
3 | public E peek () | 普通 | 找到但并不删除链表的头 |
4 | public E pool () | 普通 | 找到并删除此链表的头 |
5 | public E remove () | 普通 | 检索并移除表头 |
LinkedList 中操作链表的部分方法:
No | 方法 | 类型 | 描述 |
---|---|---|---|
1 | public void addFirst (E o) | 普通 | 在链表开头增加元素 |
2 | public void addLast (E o) | 普通 | 在链表结尾增加元素 |
3 | public boolean offer (E o) | 普通 | 将制定元素增加到链表的结尾 |
4 | public E removeFirst () | 普通 | 删除链表的第一个元素 |
5 | public E removeLast () | 普通 | 删除链表的最后一个元素 |
LinkedList 本身大量扩充了 Queue 接口和 List 接口的操作,所以在使用时最好直接使用 LinkedList 类完成操作。
示例代码1:为链表的开头和结尾增加数据
import java.util.LinkedList;
public class LinkedListDemo01 {
public static void main(String[] args) {
LinkedList<String> link = new LinkedList<String>();
link.add("A");
link.add("B");
link.add("C");
System.out.println("初始化:" + link);
link.addFirst("E"); //在开头增加数据
link.addLast("F"); //在结尾增加数据
System.out.println("增加头和尾之后的链表:" + link);
}
}
示例代码2:通过多个方式找到链表表头
import java.util.LinkedList;
public class LinkedListDemo02 {
public static void main(String[] args) {
LinkedList<String> link = new LinkedList<String>();
link.add("A"); //增加元素
link.add("B"); //增加元素
link.add("C"); //增加元素
System.out.println("1-1、element()方法找到表头:" + link.element());
System.out.println("1-2、找完之后的链表内容:" + link);
System.out.println("2-1、peek()方法找到表头:" + link.peek());
System.out.println("2-2、找完之后的链表内容:" + link);
System.out.println("3-1、poll()方法找到表头:" + link.poll());
System.out.println("3-2、找完之后的链表内容:" + link);
既然 LinkedList 实现了 Queue 接口,那么就可以按照队列的方式实现 FIFO(先进先出)的操作。
示例代码3:实现 FIFO 操作
import java.util.LinkedList;
public class LinkedListDemo03 {
public static void main(String[] args) {
LinkedList<String> link = new LinkedList<String>();
link.add("A");//增加元素
link.add("B");//增加元素
link.add("C");//增加元素
int length = link.size();//获取长度
for (int i = 0; i < length; i++) {
System.out.print(link.poll() + "、");
}
}
}
二、Set 接口
1. 接口定义
Set 接口也是 Collection 的子接口,但是与 Collection 或 List 接口不同的是,Set 接口不能增加重复的元素。
Set 接口的定义如下:
public interface Set<E> extends Collection<E>
◇ Set 接口的主要方法与 Collection 是一致的;
◇ Set 接口的实例无法像 List 接口那样进行双向输出:Collection 不能进行双向输出,因为没有提供 get() 方法,Set 接口与 Collection 接口的定义一致,所以其本身也不能双向输出。
◇ Set 接口的常用子类有:
1)散列存放:HashSet,使用散列的方式存放内容,本身没有顺序,会根据算出的 Hash 值进行排序,而 List 接口插入内容的顺序就是其保存的顺序;
2)有序存放:TreeSet,使用有序的方式存放内容,所有的内容都可以自动进行排序。
示例代码1:HashSet
import java.util.HashSet;
import java.util.Set;
public class HashSetDemo01 {
public static void main(String[] args) {
Set<String> set = new HashSet<String>();
set.add("A");//有序增加内容
set.add("B");
set.add("C");//重复增加内容
set.add("C");
set.add("C");
set.add("D");
set.add("E");
System.out.println(set);
}
}
代码运行结果:
[D, E, A, B, C]
示例代码2:TreeSet
import java.util.TreeSet;
import java.util.Set;
public class TreeSetDemo01 {
public static void main(String[] args) {
Set<String> set = new TreeSet<String>();
set.add("C");//无序增加内容,重复内容
set.add("E");
set.add("C");
set.add("D");
set.add("A");
set.add("C");
set.add("B");
System.out.println(set);
}
}
代码运行结果(TreeSet 类是可以排序的):
[A, B, C, D, E]
2. 排序及重复元素说明
Comparable 主要是进行排序的操作接口,一个对象数组要想排序,则依靠 Comparable 接口完成,对于 TreeSet 也一样,如果要想使用 TreeSet 接口进行对象的排序操作,则对象所在的类也必须实现 Comparable 接口,覆写 compareTo() 方法制定排序规则。
【如果没有实现 Comparable 接口,则会出现以下错误↓】
Exception in thread "main" java.lang.ClassCastException: setdemo02.Person cannot be cast to java.lang.Comparable
去掉重复元素的操作是依靠 Comparable 接口完成的,但是为了保证正确,所有的属性都应该进行比较,不能只是比较部分属性。真正的取消掉重复的元素,还需要 Object 类中的两个方法帮助:
1)hashCode():表示唯一的编码,一般通过计算表示;
2)equals():进行对象的比较操作。
示例代码3:实现 Comparable 接口去掉重复的元素(还不是真正意义上的去除重复)
import java.util.Set;
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
Set<Person> ts = new TreeSet<Person>();
ts.add(new Person("张三", 30));
ts.add(new Person("李四", 31));
ts.add(new Person("王五", 32));
ts.add(new Person("王五", 32));//有两个相同的元素
ts.add(new Person("赵六", 33));
ts.add(new Person("孙七", 34));
ts.add(new Person("张三", 32));
System.out.println(ts);
}
}
/**
* 实现 Comparable 接口的类才可以进行排序操作
* */
class Person implements Comparable<Person>{
private String name;
private int age;
public Person(String name, int age){
this.age = age;
this.name = name;
}
public String toString(){
return "姓名:" + this.name + ",年龄:" + this.age ;
}
public int compareTo(Person per){
if(this.age > per.age){
return 1;
}else if(this.age < per.age){
return -1;
}else{
// return 0;//应该比较类中的所有属性,不能只比较其中一个属性
return this.name.compareTo(per.name);//调用 String 中的compareTo()方法
}
}
}
代码运行结果:
[姓名:张三,年龄:30, 姓名:李四,年龄:31, 姓名:张三,年龄:32, 姓名:王五,年龄:32, 姓名:赵六,年龄:33, 姓名:孙七,年龄:34]
示例代码4:真正去掉重复元素
import java.util.Set;
import java.util.HashSet;
public class RepeatDemo {
public static void main(String[] args) {
Set<Person03> ts = new HashSet<Person03>();
ts.add(new Person03("张三", 30));
ts.add(new Person03("李四", 31));
ts.add(new Person03("王五", 32));
ts.add(new Person03("王五", 32));//有两个相同的元素
ts.add(new Person03("赵六", 33));
ts.add(new Person03("孙七", 34));
System.out.println(ts);
}
}
class Person03{
private String name;
private int age;
public Person03(String name, int age){
this.age = age;
this.name = name;
}
public boolean equals(Object obj){//覆写 equals,完成对象的比较,比较的是对象的内容
if(this == obj){
return true;
}
if(!(obj instanceof Person03)){
return false;
}
Person03 p = (Person03)obj;//向下转型
if((this.name.equals(p.name)) && (this.age == p.age)){
return true;
}else{
return false;
}
}
public int hashCode(){
return this.name.hashCode() * this.age;//自己定义一个公式,返回 hashCode:
}
public String toString(){
return "姓名:" + this.name + ",年龄:" + this.age ;
}
}
代码运行结果:
[姓名:李四,年龄:31, 姓名:张三,年龄:30, 姓名:孙七,年龄:34, 姓名:王五,年龄:32, 姓名:赵六,年龄:33]
如果要想使用 Set,就必须注意以上的问题。
三、总结
1、实际上此集合中增加元素的操作都是一样的,因为都是 Collection 的子类;
2、各个类有各个类自己的单独的实现,只需要单独使用即可;
3、一个好的类,就应该覆写 Object 类中的 equals()、hashCode()、toString() 三个方法,实际上 String 中已经全部覆写完成了。
4、Set 接口依靠 hashCode() 和 equals() 完成重复元素的判断,这一点在 Map 接口中也有体现。
5、TreeSet 依靠 Comparable 接口完成排序的操作。
参考文章:
https://blog.csdn.net/shengmingqijiquan/article/details/52640584
https://blog.csdn.net/u010871004/article/details/52604171
(以上只是一些大概的内容介绍,在后面的文章中将会逐个介绍其他的子接口。)
希望此篇文章对大家在学习上有所帮助,若有不对之处也请指出!