Java 常用集合框架List、Set、Map和Queue等体系汇总



一、集合框架

整体框架图:

在这里插入图片描述

在这里插入图片描述

简化版框架图:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


总体分析:

看上面的框架图,先抓住它的主干,即 CollectionMap

  • List , Set, Map 都是接口,前两个继承至 Collection 接口,Map为独立接口。

  • Collection 是一个接口,是高度抽象出来的集合,它包含了集合的基本操作和属性。Collection 包含了 ListSet 两大分支。

    (1)List 是一个有序的队列,每一个元素都有它的索引。第一个元素的索引值是0。List 的实现类有 LinkedList , ArrayList, Vector, Stack

    (2)Set 是一个不允许有重复元素的集合。Set 的实现类有 HastSet、LinkedHashSet 和 TreeSet。HashSet 依赖于 HashMap,它实际上是通过 HashMap 实现的;TreeSet依赖于 TreeMap,它实际上是通过 TreeMap 实现的。

  • Map 是一个映射接口,即 key - value 键值对。Map 中的每一个元素包含 “一个key” 和 “key对应的value”。Map 下有 Hashtable,LinkedHashMap,HashMap,TreeMap

  • Iterator 是遍历集合的工具,即我们通常通过 Iterator 迭代器来遍历集合。我们说 Collection 依赖于 Iterator,是因为 Collection 的实现类都要实现 iterator() 函数,返回一个 Iterator 对象。ListIterator 是专门为遍历 List 而存在的。

  • Enumeration 是 JDK 1.0 引入的抽象类。作用和 Iterator 一样,也是遍历集合;但是 Enumeration 的功能要比 Iterator 少。Enumeration只能在 Hashtable, Vector, Stack 中使用。

  • Collection 接口下还有个 Queue 接口,有 PriorityQueue 类。Queue 接口与 List、Set 同一级别,都是继承了 Collection 接口。



二、Collection接口

Collection 接口是处理对象集合的根接口,其中定义了很多对元素进行操作的方法,其子接口也都可以使用这些方法。Collection 接口有三个主要的子接口 List、Set 和 Queue,注意Map不是Collection的子接口。

Collection接口的方法:
在这里插入图片描述
在这里插入图片描述
另外,Collection 中有一个 iterator() 函数,它的作用是返回一个 Iterator 接口。通常,我们通过 Iterator 迭代器来遍历集合。ListIterator 是 List 接口所特有的,在 List 接口中,通过 ListIterator() 返回一个 ListIterator 对象。

下面介绍两个主要的接口 List 和 Set。


1、List接口

List 接口继承于 Collection 接口,它可以定义一个允许重复的有序集合。List 中的元素是有序的,所以我们可以通过索引来访问 List 中的元素,这类似于Java的数组。 并且可以对列表中每个元素的插入位置进行精确地控制。实现 List 接口的集合主要有:ArrayList、LinkedList、Vector、Stack

在这里插入图片描述
在这里插入图片描述

(1)ArrayList

  • ArrayList 是一个动态数组,也是我们最常用的集合,非同步的。它允许任何符合规则的元素插入甚至包括 null。每一个 ArrayList 都有一个初始容量(10),该容量代表了数组的大小。随着容器中的元素不断增加,容器的大小也会随着增加。

  • 它的随机访问速度是最快的,但是对随机项的插入和删除操作代价是比较昂贵的,除非变动是在 ArrayList 末端,原因是从数组中间删除一个元素,所有的元素都要向数组的前端移动,同理,在数组中间插入一个元素也是如此。


(2)LinkedList

  • LinkedList 是一个双向链表,实现了List接口,允许 null 元素。它除了有 ArrayList 的基本操作方法外还额外提供了get(),remove(),insert() 方法在 LinkedList 的首部或尾部操作。 这些操作使 LinkedList 可被用作堆栈(stack),队列(queue)或双向队列(deque)。

  • 由于实现的方式不同,LinkedList 不能随机访问,它所有的操作都是要按照双重链表的需要执行。在列表中索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。这样做的好处就是可以通过较低的代价在List中进行插入和删除操作。

  • 与 ArrayList 一样,LinkedList 也是非同步的。


(3)Vector

与 ArrayList 相似,但是 Vector 是同步的。所以说 Vector 是线程安全的动态数组。它的操作与 ArrayList几乎一样。


(4)Stack

Stack 继承自Vector,实现一个后进先出的堆栈。Stack提供了 5 个额外的方法使得Vector得以被当作堆栈使用:

  • push() 和 pop() 方法,
  • peek() 方法得到栈顶的元素,
  • empty() 方法测试堆栈是否为空,
  • search() 方法检测一个元素在堆栈中的位置。


(5)使用示例

import java.util.ArrayList;
import java.util.List;

/*List接口的特点:
*   1. 它是一个元素存取有序的集合
*   2. 它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)。
*   3. 集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。
* */
public class Demo01List {
    public static void main(String[] args) {
        //通过多态的方法来是实现,List作为一个接口不能直接被使用,可以通过ArrayList子类来使用
        List<String> list = new ArrayList<>();

        list.add("唐僧");//从尾部添加
        list.add("孙悟空");
        list.add("猪八戒");
        System.out.println(list);
        list.add(1,"华蒙");//从指定位置添加
        System.out.println(list);

        System.out.println("删除索引位置为2的元素");
        System.out.println(list.remove(2));//删除索引位置为2的元素,并返回删除的元素

        list.set(0, "三毛");//将指定位置的元素替换为新元素
        System.out.println(list);

        for(int i = 0;i<list.size();i++){
            System.out.println(list.get(i));//get(int i) 返回指定位置的元素
        }

    }
}

//输出结果:
[唐僧, 孙悟空, 猪八戒]
[唐僧, 华蒙, 孙悟空, 猪八戒]
删除索引位置为2的元素
孙悟空
[三毛, 华蒙, 猪八戒]
三毛
华蒙
猪八戒



2、Set接口

  • Set 是一种不包含重复的元素的无序集合 ,即任意的两个元素 e1 和 e2 都有e1.equals(e2) == false。Set 最多有一个 null 元素。

  • 虽然Set中元素没有顺序,但是元素在set中的位置是由该元素的HashCode决定的,其具体位置其实是固定的。

  • Set接口有三个具体实现类,分别是散列集HashSet、链式散列集LinkedHashSet和树形集TreeSet。

  • 在set接口中的不重复是有特殊要求的。比如:对象A和对象B,本来是不同的两个对象,正常情况下它们是能够放入到 Set 里面的,但是如果对象 A 和 B 都重写了 hashcode() 和 equals() 方法,并且重写后的 hashcode() 和 equals() 方法是相同的话,那么 A 和 B 是不能同时放入到 Set 集合中去的,也就是 Set 集合中的去重和 hashcode() 与 equals() 方法直接相关。


(1)HashSet

  • HashSet 是一个没有重复元素的集合。它是由 HashMap 实现的,无序,即元素插入的顺序与输出的顺序不一致

  • HashSet 允许使用 null 元素,但最多存入一个。HashSet 是非同步的

  • HashSet 按 Hash 算法来存储集合的元素,因此具有很好的存取和查找性能

  • HashSet 通过一个 HashMap 存储元素,元素是存放在 HashMap 的 Key 中,而 Value 统一使用一个 Object 对象

  • HashSet 中存储的元素的是无序的,但是由于 HashSet 底层是基于 Hash 算法实现的,使用了 hashcode ,所以 HashSet 中相应的元素的位置是固定的

  • 如果 Set 中的可变元素改变了自身状态导致 Object.equals(Object) == true 将导致一些问题。

  • HashSet 依赖 hashCode() 和 equals() 来保证元素唯一性。


(2)LinkedHashSet

  • LinkedHashSet 继承自 HashSet ,其底层是基于 LinkedHashMap 来实现的非同步

  • HashSet 是无序的,而 LinkedHashSet 是有序的

  • LinkedHashSet 集合同样是根据元素的 hashCode() 值来决定元素的存储位置,但是它同时使用链表维护元素的次序。LinkedHashSet 将会以元素的添加顺序访问集合的元素

  • LinkedHashSet 集合底层数据结构是链表和哈希表。由链表保证元素有序,由哈希表保证元素唯一


(3)TreeSet

  • TreeSet 是一个有序集合,其底层是基于 TreeMap 实现的,非线程安全

  • TreeSet 的底层数据结构采用二叉树来实现,元素唯一且已经排好序。

  • TreeSet 可以确保集合元素处于排序状态。TreeSet支持两种排序方式:自然排序和定制排序,其中自然排序为默认的排序方式。当我们构造 TreeSet 时,若使用不带参数的构造函数,则 TreeSet 使用自然比较器;若用户需要使用自定义的比较器,则需要使用带比较器的参数。

  • TreeSet 集合不是通过 hashcode 和 equals 函数来比较元素的。它是通过 compare 或者 comparaeTo 函数来判断元素是否相等。compare 函数通过判断两个对象的 id,若相同则判断为重复元素,不会被加入到集合中。


(4)List 和 Set 适用场景

在这里插入图片描述



(5)使用示例

package com.test;  
  
import java.util.HashSet;  
import java.util.LinkedHashSet;  
import java.util.TreeSet;  
  
/**  
 * @description 几个set的比较  
 *    HashSet:哈希表是通过使用称为散列法的机制来存储信息的,元素并没有以某种特定顺序来存放;  
 *    LinkedHashSet:以元素插入的顺序来维护集合的链接表,允许以插入的顺序在集合中迭代;  
 *    TreeSet:提供一个使用树结构存储Set接口的实现,对象以升序顺序存储,访问和遍历的时间很快。  
 * @author Zhou-Jingxian  
 *  
 */  
public class SetDemo {  
  
    public static void main(String[] args) {  
  
        HashSet<String> hs = new HashSet<String>();  
        hs.add("B");  
        hs.add("A");  
        hs.add("D");  
        hs.add("E");  
        hs.add("C");  
        hs.add("F");  
        System.out.println("HashSet 顺序:\n"+hs);  
          
        LinkedHashSet<String> lhs = new LinkedHashSet<String>();  
        lhs.add("B");  
        lhs.add("A");  
        lhs.add("D");  
        lhs.add("E");  
        lhs.add("C");  
        lhs.add("F");  
        System.out.println("LinkedHashSet 顺序:\n"+lhs);  
          
        TreeSet<String> ts = new TreeSet<String>();  
        ts.add("B");  
        ts.add("A");  
        ts.add("D");  
        ts.add("E");  
        ts.add("C");  
        ts.add("F");  
        System.out.println("TreeSet 顺序:\n"+ts);  
    }  
}


//输出结果:
HashSet 顺序:[D, E, F, A, B, C]
LinkedHashSet 顺序:[B, A, D, E, C, F]
TreeSet 顺序:[A, B, C, D, E, F]



3、Queue接口

  • 队列是一种特殊的线性表,它只允许在表的前端进行删除操作,而在表的后端进行插入操作,即 FIFO(先进先出)特点。

  • Queue 接口提供了支持add,remove操作,但是不建议使用,一般用 offer(),poll() 和 peek() 方法来替代。

  • LinkedList 类实现了 Queue 接口,因此也可以把 LinkedList 当成 Queue 来用。队列不允许插入 null 元素,但是 LinkedList 可以。


实现 Queue 和 Deque 接口的类图:
在这里插入图片描述

Queue接口常用方法:

- add(E e) 插入一个元素到队列中,失败时抛出异常 IllegalStateException (队列容量不够)
- remove() 返回并移除队列的头部元素,队列为空时, 抛出异常
- element() 返回队列头部的元素,队列为空时, 抛出异常
- offer(E e) 插入一个元素到队列中,失败时返回false
- peek() 返回队列头部的元素,队列为空时返回null
- poll() 返回并移除队列的头部元素,队列为空时返回null



(1)Deque接口

  • Deque 表示双端队列,双端队列是在两端都可以进行插入和删除的队列。

  • Deque 是一个比 Stack 和 Queue 功能更强大的接口,它同时实现了栈和队列的功能。

  • ArrayDeque 和 LinkeList 实现了 Deque 接口。


Dueue接口常用方法:

  • add(E e) 将新元素添加到队列的尾端(当不超过队列的容量时)
  • addFirst(E e) 将新元素添加到队列的头部
  • addLast(E e) 将新元素添加到队列的尾部
  • contains(Object o) 双端队列是否含有对象o
  • descendingIterator()倒叙返回队列的迭代器
  • element() 返回队列的头部元素
  • getFirst() 获取头部元素
  • getLast() 获取尾部元素
  • iterator() 迭代队列
  • offer(E e) 将新元素插入到队列尾部
  • offerFirst(E e) 将新元素添加到队列的头部
  • offerLast(E e) 将新元素添加到队列的尾部
  • peek() 返回队列的头部元素
  • peekFirst() 获取头部元素
  • peekLast() 获取尾部元素
  • pool() 返回并移除队列的头部元素
  • poolFirst() 获取并移除头部元素
  • poolLast() 获取并移除尾部元素
  • pop() 将一个元素出栈
  • push(E e) 讲一个元素压入栈
  • remove() 移除队列的头部元素
  • remove(Object o) 移除队列中第一个o
  • removeFirst() 移除队列的头部元素
  • removeFirstOccurrence(Object o) 移除队列中第一个o
  • removeLast() 移除队列的尾部元素
  • removeLastOccurrence(Object o) 移除队列中最后一个o
  • size()

实现Queue和Deque接口的类:
在这里插入图片描述



ArrayDeque类

ArrayDeque 类是双端队列的数组实现类,底层数据存储在数组中,数组双端队列是可扩容的。由于没有额外的功能,这里不详细讨论了。


使用示例:

import java.util.*;
public class QueueDemo {
    public static void main(String[] args) {
       ArrayDeque<String> ad = new ArrayDeque<String>();

        ad.add("D");
        ad.offerFirst("E");
        ad.addFirst("F");
        ad.offer("A");
        ad.addLast("B");
        ad.offerLast("C");

        System.out.println("队列中的元素:" + ad.toString());
        System.out.println("队列中的第一个元素:" + ad.peekFirst());
        System.out.println("移除队列中的元素:" + ad.remove());
        System.out.println("队列中的元素:" + ad.toString());
        System.out.println("移除队列中的元素:" + ad.pollLast());
        System.out.println("队列中的元素:" + ad.toString());
    }  
}
// 队列中的元素:[A, B, C, E, D, F]
// 队列中的第一个元素:A
// 移除队列中的元素:A
// 队列中的元素:[B, D, C, E, F]
// 移除队列中的元素:B
// 队列中的元素:[C, D, F, E]


(2)PriorityQueue类

  • 优先级队列是不同于先进先出队列的另一种队列。每次从队列中取出的是具有最高优先权的元素。

  • 逻辑上使用堆结构(完全二叉树)实现,物理上使用动态数组实现,并非像TreeMap一样完全有序,但是如果按照指定方式出队,结果可以是有序的。

  • 如果不提供Comparator的话,优先队列中元素默认按自然顺序排列,也就是数字默认是小的在队列头,字符串则按字典序排列。


使用示例1:

import java.util.*;
public class QueueDemo {
    public static void main(String[] args) {
        PriorityQueue<String> pq = new PriorityQueue<String>();

        // 队列中的元素按照自然顺序排序,元素加入的顺序与元素在队列中的顺序无关
        pq.add("D");
        pq.add("E");
        pq.add("F");
        pq.add("A");
        pq.add("B");
        pq.add("C");

        // 输出队列中的元素,不保证以任何特定的顺序遍历优先级队列中的元素。
        // toString方法中使用了迭代器。
        System.out.println("队列中的元素:" + pq.toString());
        System.out.println("队列中的第一个元素:" + pq.peek());
        System.out.println("移除队列中的元素:" + pq.remove());
        System.out.println("队列中的元素:" + pq.toString());
        System.out.println("移除队列中的元素:" + pq.poll());
        System.out.println("队列中的元素:" + pq.toString());
    }  
}
// 队列中的元素:[A, B, C, E, D, F]
// 队列中的第一个元素:A
// 移除队列中的元素:A
// 队列中的元素:[B, D, C, E, F]
// 移除队列中的元素:B
// 队列中的元素:[C, D, F, E]

使用示例2:

import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;
 
public class test {
	private String name;
	private int population;
	public test(String name, int population)
	{
		this.name = name;
	    this.population = population;
	}
	public String getName()
	{
	     return this.name;
	}
 
	public int getPopulation()
	{
	     return this.population;
	}
	public String toString()
	{
	     return getName() + " - " + getPopulation();
	}
	public static void main(String args[])
	{
		Comparator<test> OrderIsdn =  new Comparator<test>(){
			public int compare(test o1, test o2) {
				// TODO Auto-generated method stub
				int numbera = o1.getPopulation();
				int numberb = o2.getPopulation();
				if(numberb > numbera)
				{
					return 1;
				}
				else if(numberb<numbera)
				{
					return -1;
				}
				else
				{
					return 0;
				}
			
			}
 
			
			
		};
		Queue<test> priorityQueue =  new PriorityQueue<test>(11,OrderIsdn);
		
		test t1 = new test("t1",1);
		test t3 = new test("t3",3);
		test t2 = new test("t2",2);
		test t4 = new test("t4",0);
		priorityQueue.add(t1);
		priorityQueue.add(t3);
		priorityQueue.add(t2);
		priorityQueue.add(t4);
		System.out.println(priorityQueue.poll().toString());
	}
}


(3)使用示例

import java.util.LinkedList;
import java.util.Queue;
 
public class Main {
    public static void main(String[] args) {
        //add()和remove()方法在失败的时候会抛出异常(不推荐)
        Queue<String> queue = new LinkedList<String>();
        //添加元素
        queue.offer("a");
        queue.offer("b");
        queue.offer("c");
        queue.offer("d");
        queue.offer("e");
        for(String q : queue){
            System.out.println(q);
        }
        System.out.println("===");
        System.out.println("poll="+queue.poll()); //返回第一个元素,并在队列中删除
        for(String q : queue){
            System.out.println(q);
        }
        System.out.println("===");
        System.out.println("element="+queue.element()); //返回第一个元素 
        for(String q : queue){
            System.out.println(q);
        }
        System.out.println("===");
        System.out.println("peek="+queue.peek()); //返回第一个元素 
        for(String q : queue){
            System.out.println(q);
        }
    }
}

//输出结果:
a
b
c
d
e
===
poll=a
b
c
d
e
===
element=b
b
c
d
e
===
peek=b
b
c
d
e



三、Map接口

  • Map 与 List、Set 接口不同,它是由一系列键值对组成的集合,提供了 key 到 Value 的映射,同时它也没有继承Collection

  • 在 Map 中它保证了 key 与 value 之间的一一对应关系,也就是说一个 key 对应一个value,所以它不能存在相同的 key 值,当然 value 值可以相同。

  • Map 接口有四种主要的实现类,分别是 HashMap、Hashtable、LinkedHashMap 和 TreeMap

  • TreeMap 是有序的,HashMap 和 HashTable 是无序的。

  • Hashtable 的方法是同步的,HashMap 的方法不是同步的,这是两者最主要的区别。

在这里插入图片描述


(1)HashMap 和 Hashtable

  • Hashtable 是线程安全的,HashMap 不是线程安全的

  • HashMap 效率较高,Hashtable 效率较低。如果对同步性或与遗留代码的兼容性没有任何要求,建议使用 HashMap。 查看 Hashtable 的源代码就可以发现,除构造函数外,Hashtable 的所有 public 方法声明中都有 synchronized 关键字,而 HashMap 的源码中则没有。

  • Hashtable 不允许 null 值,HashMap 允许 null 值(key和value都允许)

  • Hashtable 的父类是 Dictionary,HashMap 的父类是 AbstractMap 。

在这里插入图片描述

简单示例:

	Hashtable numbers = new Hashtable();
	numbers.put(“one”, new Integer(1));
	numbers.put(“two”, new Integer(2));
	numbers.put(“three”, new Integer(3));
	// 要取出一个数,比如2,用相应的key:
	Integer n = (Integer)numbers.get(“two”);
	System.out.println(“two =+ n);

(2)LinkedHashMap

  • LinkedHashMap 是 HashMap 的一个子类,它保留插入的顺序,如果需要输出的顺序和输入时的相同,那么就选用 LinkedHashMap

  • LinkedHashMap 是 Map 接口的哈希表和链接列表实现的,具有可预知的迭代顺序。此实现提供所有可选的映射操作,并 允许使用 null 值和 null 键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

  • LinkedHashMap 实现与 HashMap 的不同之处在于,LinkedHashMap 维护着一个运行于所有条目的双重链接列表。

  • 根据链表中元素的顺序可以分为:按插入顺序的链表按访问顺序(调用get方法)的链表 。默认是按插入顺序排序,如果指定按访问顺序排序,那么调用 get() 方法后,会将这次访问的元素移至链表尾部,不断访问可以形成按访问顺序排序的链表。

  • LinkedHashMap 实现不是同步的。如果多个线程同时访问链接的哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须保持外部同步。

  • 由于LinkedHashMap需要维护元素的插入顺序,因此性能略低于HashMap的性能,但在迭代访问Map里的全部元素时将有很好的性能,因为它以链表来维护内部顺序。


(3)TreeMap

  • TreeMap 是一个有序的 key-value 集合,非同步基于红黑树实现,每一个 key-value 节点作为红黑树的一个节点。

  • TreeMap 存储时会进行排序的,会根据 key 来对 key-value 键值对进行排序,其中排序方式也是分为两种,一种是自然排序,一种是定制排序,具体取决于使用的构造方法。

    • 自然排序: TreeMap 中所有的 key 必须实现 Comparable 接口,并且所有的 key 都应该是同一个类的对象,否则会报 ClassCastException 异常

    • 定制排序: 定义 TreeMap 时,创建一个 comparator 对象,该对象对所有的 TreeMap 中所有的 key 值进行排序,采用定制排序的时候不需要 TreeMap 中所有的 key 必须实现 Comparable 接口

  • TreeMap 判断两个元素相等的标准:两个 key 通过 compareTo() 方法返回 0 ,则认为这两个 key 相等。

  • 如果使用自定义的类来作为 TreeMap 中的 key 值,且想让 TreeMap 能够良好的工作,则必须重写自定义类中的 equals() 方法,TreeMap 中判断相等的标准是:两个 key 通过 equals() 方法返回为 true,并且通过 compareTo() 方法比较应该返回为 0 。

在这里插入图片描述

a. 自然排序

自然排序要进行以下操作:

  • Student类中实现 Comparable 接口

  • 重写 Comparable 接口中的 CompareTo() 方法


测试类1:

public class MyClass {
    public static void main(String[] args) {
        TreeSet<Student> ts=new TreeSet<Student>();
        //创建元素对象
        Student s1=new Student("zhangsan",20);
        Student s2=new Student("lis",22);
        Student s3=new Student("wangwu",24);
        Student s4=new Student("chenliu",26);
        Student s5=new Student("zhangsan",22);
        Student s6=new Student("qianqi",24);

        //将元素对象添加到集合对象中
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);

        //遍历
        for(Student s:ts){
            System.out.println(s.getName()+"-----------"+s.getAge());
        }
    }
}

Student 类:

public class Student implements Comparable<Student>{
    private String name;
    private int age;

    public Student() {
        super();
        // TODO Auto-generated constructor stub
    }

    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    //compareTo(T o)  比较此对象与指定对象的顺序。
    public int compareTo(Student s) {
        //return -1; //-1表示放在红黑树的左边,即逆序输出
        //return 1;  //1表示放在红黑树的右边,即顺序输出
        //return o;  //表示元素相同,仅存放第一个元素
        //主要条件 姓名的长度,如果姓名长度小的就放在左子树,否则放在右子树
        int num=this.name.length()-s.name.length();
        //姓名的长度相同,不代表内容相同,如果按字典顺序此 String 对象位于参数字符串之前,则比较结果为一个负整数。
        //如果按字典顺序此 String 对象位于参数字符串之后,则比较结果为一个正整数。
        //如果这两个字符串相等,则结果为 0
        int num1=num==0?this.name.compareTo(s.name):num;
        //姓名的长度和内容相同,不代表年龄相同,所以还要判断年龄
        int num2=num1==0?this.age-s.age:num1;
        return num2;
    }
}


输出结果:

lis-----------22
qianqi-----------24
wangwu-----------24
chenliu-----------26
zhangsan-----------20
zhangsan-----------22

b. 比较器排序

比较器排序步骤:

  • 单独创建一个比较类,这里以MyComparator为例,并且要让其继承Comparator接口

  • 重写Comparator接口中的Compare方法

    compare(T o1,T o2)      比较用来排序的两个参数。
    
  • 在主类中使用下面的 构造方法

    // 构造一个新的空 TreeSet,它根据指定比较器进行排序。
    TreeSet(Comparator<? superE> comparator)
    

测试类:

public class MyClass {

    public static void main(String[] args) {
        //创建集合对象
        //TreeSet(Comparator<? super E> comparator) 构造一个新的空 TreeSet,它根据指定比较器进行排序。
        TreeSet<Student> ts=new TreeSet<Student>(new MyComparator());

        //创建元素对象
        Student s1=new Student("zhangsan",20);
        Student s2=new Student("lis",22);
        Student s3=new Student("wangwu",24);
        Student s4=new Student("chenliu",26);
        Student s5=new Student("zhangsan",22);
        Student s6=new Student("qianqi",24);

        //将元素对象添加到集合对象中
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);

        //遍历
        for(Student s:ts){
            System.out.println(s.getName()+"-----------"+s.getAge());
        }
    }
}


Student 类:

public class Student {
    private String name;
    private int age;

    public Student() {
        super();
        // TODO Auto-generated constructor stub
    }

    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

}

MyComparator 类:

public class MyComparator implements Comparator<Student> {

    @Override
    public int compare(Student s1,Student s2) {
        // 姓名长度
        int num = s1.getName().length() - s2.getName().length();
        // 姓名内容
        int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
        // 年龄
        int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
        return num3;
    }

}

运行结果:

lis-----------22
qianqi-----------24
wangwu-----------24
chenliu-----------26
zhangsan-----------20
zhangsan-----------22


(4)使用示例

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.TreeMap;

public class MapTest {

    public static void main(String[] args) {

        //HashMap
        HashMap<String,String> hashMap = new HashMap();
        hashMap.put("4", "d");
        hashMap.put("3", "c");
        hashMap.put("2", "b");
        hashMap.put("1", "a");

        Iterator<String> iteratorHashMap = hashMap.keySet().iterator();

        System.out.println("HashMap-->");

        while (iteratorHashMap.hasNext()){

            Object key1 = iteratorHashMap.next();
            System.out.println(key1 + "--" + hashMap.get(key1));
        }

        //LinkedHashMap
        LinkedHashMap<String,String> linkedHashMap = new LinkedHashMap();
        linkedHashMap.put("4", "d");
        linkedHashMap.put("3", "c");
        linkedHashMap.put("2", "b");
        linkedHashMap.put("1", "a");

        Iterator<String> iteratorLinkedHashMap = linkedHashMap.keySet().iterator();

        System.out.println("LinkedHashMap-->");

        while (iteratorLinkedHashMap.hasNext()){

            Object key2 = iteratorLinkedHashMap.next();
            System.out.println(key2 + "--" + linkedHashMap.get(key2));
        }

        //TreeMap
        TreeMap<String,String> treeMap = new TreeMap();
        treeMap.put("4", "d");
        treeMap.put("3", "c");
        treeMap.put("2", "b");
        treeMap.put("1", "a");

        Iterator<String> iteratorTreeMap = treeMap.keySet().iterator();

        System.out.println("TreeMap-->");

        while (iteratorTreeMap.hasNext()){

            Object key3 = iteratorTreeMap.next();
            System.out.println(key3 + "--" + treeMap.get(key3));
        }

    }

}



//输出结果:
HashMap-->
3--c
2--b
1--a
4--d
LinkedHashMap-->
4--d
3--c
2--b
1--a
TreeMap-->
1--a
2--b
3--c
4--d



四、Iterator 与 ListIterator 详解

1、Iterator

Iterator 是一个接口,它是集合的迭代器。集合可以通过 Iterator 去遍历集合中的元素。Iterator提供的API接口如下:

  • boolean hasNext():判断集合里是否存在下一个元素,若有则返回 true。
  • Object next():返回集合里下一个元素。
  • void remove():删除集合里上一次next方法返回的元素。

使用示例:

/*

Iterator的定义如下:
public interface Iterator<E> {}

*/

public class IteratorExample {
    public static void main(String[] args) {
        ArrayList<String> a = new ArrayList<String>();
        a.add("aaa");
        a.add("bbb");
        a.add("ccc");
        System.out.println("Before iterate : " + a);
        Iterator<String> it = a.iterator();
        while (it.hasNext()) {
            String t = it.next();
            if ("bbb".equals(t)) {
                it.remove();
            }
        }
        System.out.println("After iterate : " + a);
    }
}

//输出结果:
Before iterate : [aaa, bbb, ccc]
After iterate : [aaa, ccc] 


注意:

  • Iterator只能单向移动。

  • Iterator.remove() 是唯一安全的方式来在迭代过程中修改集合;如果在迭代过程中以任何其它的方式修改了基本集合将会产生未知的行为。而且每调用一次 next() 方法, remove() 方法只能被调用一次,如果违反这个规则将抛出一个异常。


2、ListIterator

  • ListIterator 是一个功能更加强大的迭代器, 它继承于 Iterator 接口,只能用于各种 List 类型的访问。

  • 可以通过调用 listIterator() 方法产生一个指向 List 开始处的 ListIterator。

  • 还可以调用 listIterator(n) 方法创建一个一开始就指向列表索引为 n 的元素处的 ListIterator。

ListIterator接口定义如下:

public interface ListIterator<E> extends Iterator<E> {
    boolean hasNext();
 
    E next();
 
    boolean hasPrevious();
 
    E previous();
 
    int nextIndex();
 
    int previousIndex();
 
    void remove();
 
    void set(E e);
 
    void add(E e);
     
}

由以上定义我们可以推出 ListIterator 可以:

  • 双向移动(向前 / 向后遍历)

  • 产生相对于迭代器在列表中指向的当前位置的前一个和后一个元素的索引

  • 可以使用 set() 方法替换它访问过的最后一个元素

  • 可以使用 add() 方法在 next() 方法返回的元素之前或 previous() 方法返回的元素之后插入一个元素

使用示例:

public class ListIteratorExample {
 
    public static void main(String[] args) {
        ArrayList<String> a = new ArrayList<String>();
        a.add("aaa");
        a.add("bbb");
        a.add("ccc");
        System.out.println("Before iterate : " + a);
        ListIterator<String> it = a.listIterator();
        while (it.hasNext()) {
            System.out.println(it.next() + ", " + it.previousIndex() + ", " + it.nextIndex());
        }
        while (it.hasPrevious()) {
            System.out.print(it.previous() + " ");
        }
        System.out.println();
        it = a.listIterator(1);
        while (it.hasNext()) {
            String t = it.next();
            System.out.println(t);
            if ("ccc".equals(t)) {
                it.set("nnn");
            } else {
                it.add("kkk");
            }
        }
        System.out.println("After iterate : " + a);
    }
}


//输出结果:
Before iterate : [aaa, bbb, ccc]
aaa, 0, 1
bbb, 1, 2
ccc, 2, 3
ccc bbb aaa 
bbb
ccc
After iterate : [aaa, bbb, kkk, nnn]



五、Collection 和 Collections 的区别


1、Collection

  • java.util.Collection 是一个集合接口(集合类的一个顶级接口)。

  • 它提供了对集合对象进行基本操作的通用接口方法。

  • Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有 List 与 Set。


关系如下:

  • Collection
    • List
      • LinkedList
      • ArrayList
      • Vector
        • Stack
    • Set


2、Collections

  • java.util.Collections 是一个包装类(工具类/帮助类)。

  • 它包含有各种有关集合操作的静态多态方法

  • 此类不能实例化,就像一个工具类,用于对集合中元素进行排序、搜索以及线程安全等各种操作,服务于 Java 的 Collection 框架。


使用示例:

import java.util.ArrayList; 
import java.util.Collections; 
import java.util.List; 
  
public class TestCollections { 
      
    public static void main(String args[]) { 
        //注意List是实现Collection接口的 
        List list = new ArrayList(); 
        double array[] = { 112, 111, 23, 456, 231 }; 
        for (int i = 0; i < array.length; i++) { 
            list.add(new Double(array[i])); 
        } 
        Collections.sort(list); 
        for (int i = 0; i < array.length; i++) { 
            System.out.println(list.get(i)); 
        } 
        // 结果:23.0 111.0 112.0 231.0 456.0 
    } 
}


参考:

常用的几种java集合类总结

Java集合中List,Set以及Map等集合体系详解(史上最全)

java集合超详解

java集合框架综述

Java集合之Queue接口

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值