Java的Set,Queue,Stack操作

658 阅读2分钟
Java的Set操作

      Set不保存重复的元素。如果你试图将相同对象的多个实体添加到Set中,那么它就会阻止这种重复现象。Set中最常被使用的是测试归属性,你可以很容易地询问某个对象是否在某个Set中。正因如此,查找就称为了Set中最重要的操作,因此你通常都会选择一个HashSet的实现,它专门对快速查找进行了优化。

      Set具有与Collection完全一样的接口,因此没有任何额外的功能,不像前面有两个不同的List。实际上Set就是Collection,只是行为不同。(这是继承与多态思想的典型应用:表现不同的行为。)Set是基于对象的值来确定归属性的。·

      下面是使用存放Integer对象的HashSet的示例:

package p10;

import java.util.HashSet;
import java.util.Random;
import java.util.Set;

public class SetOfInteger {
    public static void main(String[] args) {
        Random random = new Random(47);
        Set<Integer> integerSet = new HashSet<Integer>();
        for(int i = 0;i < 10000;i++){
            integerSet.add(random.nextInt(60));
        }
        System.out.println(integerSet);
        /**
         *  “不保证有序”和“保证无序”不等价
         * [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
         * 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
         * 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
         * 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59]
         */
    }
}

      你还可以注意到,输出的顺序没有任何规律可循,这是因为出于速度原因的考虑,HashSet使用了散列。HashSet所维护的顺序与TreeSet或LinkedHashSet都不同,因为它们的实现具有不同的元素存储方式。TreeSet将元素存储在红--黑树数据结构中,而HashSet使用的是散列函数。LinkedHashList因为查询速度的原因也使用了散列,但是看起来它使用了链表来维护元素的插入顺序。

      如果你想对结果排序,一种方式是使用TreeSet来代替HashSet;

package p10;

import java.util.Random;
import java.util.SortedSet;
import java.util.TreeSet;

public class SortedSetOfInteger {
    public static void main(String[] args) {
        Random random = new Random(47);
        SortedSet<Integer> integers = new TreeSet<>();
        for(int i = 0;i < 10000;i++){
            integers.add(random.nextInt(30));
        }
        System.out.println(integers);
        /**
         * [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
         * 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
         */
    }
}

      你将会执行的最常见的操作之一,就是使用contains()测试Set的归属性,但是还有很多操作会让你想起在小学时所教授的文氏图。

package p10;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

public class SetOperation {
    public static void main(String[] args) {
        Set<String> set1 = new HashSet<>();
        Collections.addAll(set1,"A B C D E F G H I J K L".split(" "));
        set1.add("M");
        System.out.println("H: " + set1.contains("H"));
        System.out.println("N: " + set1.contains("N"));
        Set<String> set2 = new HashSet<>();
        Collections.addAll(set2,"H I J K L".split(" "));
        System.out.println("set2 in set1: "+set1.containsAll(set2));
        set1.remove("H");
        System.out.println("set1: " + set1);
        System.out.println("set2 in set1: "+set1.containsAll(set2));
        System.out.println("set2: "+ set2);
        set1.remove(set2);
        System.out.println("set2 removed from set1: "+set1);
        Collections.addAll(set1,"X Y Z".split(" "));
        System.out.println("'X Y Z' added to set1: " + set1);
        /**
         * H: true
         * N: false
         * set2 in set1: true
         * set1: [A, B, C, D, E, F, G, I, J, K, L, M]
         * set2 in set1: false
         * set2: [H, I, J, K, L]
         * set2 removed from set1: [A, B, C, D, E, F, G, I, J, K, L, M]
         * 'X Y Z' added to set1: [A, B, C, D, E, F, G, I, J, K, L, M, X, Y, Z]
         */
    }
}

Java的Queue操作
  1. 普通队列

      队列是一个典型的先进先出(FIFO)的容器。即从容器的一端放入事物,从另一端取出,并且事物放入容器的顺序与取出的顺序是相同的。队列常被当作一种可靠的将对象从程序的某个区域传输到另一个区域的途径。队列在并发编程中特别重要。

      LinkedList提供了方法以支持队列的行为,并且它实现了Queue接口,因此LinkedList可以用作Queue的一种实现。通过将LinkedList向上转型为Queue,下面的示例使用了在Queue接口中与Queue相关的方法:

package p10;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import java.util.Stack;

public class QueueDemo {
    public static void printQ(Queue queue){
        while (queue.peek() != null){
            System.out.print(queue.remove() + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        Queue<Integer> queue = new LinkedList<>();
        Random rand = new Random(47);
        for(int i = 0;i < 10;i++){
            queue.offer(rand.nextInt(i+10));
        }
        printQ(queue);
        Queue<Character> qc = new LinkedList<>();
        for(char c :"Brontosaurus".toCharArray()){
            qc.offer(c);
        }
        printQ(qc);
        /**
         * 8 1 1 1 5 14 3 1 0 1
         * B r o n t o s a u r u s
         */
    }
}

      offer()方法是与Queue相关的方法之-一-,它在允许的情况下,将一个元素插入到队尾,或者返回false。peek()和elementO都将在不移除的情况下返回队头,但是peek()方法在队列为空时返回null,而element()会抛出NoSuchElementException异常。poll()和remove()方法将移除并返回队头,但是poll()在队列为空时返回null,而remove()会抛出NoSuchElementException异常。

      自动包装机制会自动地将nextInt()方法的int结果转换为queue所需的Integer对象,将char c转换为qc所需的Character对象。Queue接口窄化了对LinkedList的方法的访问权限,以使得只有恰当的方法才可以使用,因此,你能够访问的LinkedList的方法会变少(这里你实际上可以将queue转型回LinkedList,但是至少我们不鼓励这么做)。

  1. 优先队列

      先进先出描述了最典型的队列规则。队列规则是指在给定一组队列中的元素的情况下,确定下一个弹出队列的元素的规则。先进先出声明的是下一个元素应该是等待时间最长的元素。

      优先级队列声明下一个弹出元素是最需要的元素(具有最高的优先级)。例如,在飞机场,当飞机临近起飞时,这架飞机的乘客可以在办理登机手续时排到队头。如果构建了一个消息系统,某些消息比其他消息更重要,因而应该更快地得到处理,那么它们何时得到处理就与它们何时到达无关。PriorityQueue添加到Java SE5中,是为了提供这种行为的一种自动实现。

      当你在PriorityQueue上调用offer()方法来插入一个对象时,这个对象会在队列中被排序。默认的排序将使用对象在队列中的自然顺序,但是你可以通过提供自己的Comparator来修改这个顺序。PriorityQueue可以确保当你调用peek(),poll()和remove()方法时,获取的元素将是队列中优先级最高的元素。

      让PriorityQueue与Integer、String和Character这样的内置类型一起工作易如反掌。在下面的示例中,第一个值集与前一个示例中的随机值相同,因此你可以看到它们从PriorityQueue中弹出的顺序与前一个示例不同。

package p10;

import java.util.*;

public class PriorityQueueDemo {
    public static void main(String[] args) {
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
        Random rand = new Random(47);
        for(int i = 0;i < 10;i++){
            priorityQueue.offer(rand.nextInt(i+10));
        }
        QueueDemo.printQ(priorityQueue);

        List<Integer> ints = Arrays.asList(25,22,20,18,14,9,3,1,1,2,3,9,
                14,18,21,23,25);
        priorityQueue = new PriorityQueue<>(ints);
        QueueDemo.printQ(priorityQueue);

        priorityQueue = new PriorityQueue<>(ints.size(), Collections.reverseOrder());
        priorityQueue.addAll(ints);
        QueueDemo.printQ(priorityQueue);

        String fact = "EDUCATION SHOULD ESCHEW OBFUSCATION";
        List<String> strings = Arrays.asList(fact.split(""));
        PriorityQueue<String> stringPQ = new PriorityQueue<>(strings);
        QueueDemo.printQ(stringPQ);

        stringPQ = new PriorityQueue<>(strings.size(),Collections.reverseOrder());
        stringPQ.addAll(strings);
        QueueDemo.printQ(stringPQ);

        Set<Character> characterSet = new HashSet<>();
        for(char c :fact.toCharArray()){
            characterSet.add(c);
        }
        PriorityQueue<Character> characterPQ = new PriorityQueue<>(characterSet);
        QueueDemo.printQ(characterPQ);

        /**
         * 0 1 1 1 1 1 3 5 8 14
         * 1 1 2 3 3 9 9 14 14 18 18 20 21 22 23 25 25
         * 25 25 23 22 21 20 18 18 14 14 9 9 3 3 2 1 1
         *       A A B C C C D D E E E F H H I I L N N O O O O S S S T T U U U W
         * W U U U T T S S S O O O O N N L I I H H F E E E D D C C C B A A
         *   A B C D E F H I L N O S T U W
         */
    }
}
Deque接口(实现栈和队列)

使用ArrayDeque向上转型为Deque

      Resizable-array implementation of the Deque interface. Array deques have no capacity restrictions; they grow as necessary to support usage. They are not thread-safe; in the absence of external synchronization, they do not support concurrent access by multiple threads. Null elements are prohibited. This class is likely to be faster than Stack when used as a stack, and faster than LinkedList when used as a queue.

package p10;

import java.util.ArrayDeque;
import java.util.Deque;

public class DequeDemo {
    public static void printQ(Deque deque){
        while (!deque.isEmpty()){
            System.out.print(deque.remove() + " ");
        }
        System.out.println();
    }
    public static void printS(Deque deque){
        while (!deque.isEmpty()){
            System.out.print(deque.peek() + " ");
            deque.pop();
        }
        System.out.println();
    }
    public static void main(String[] args) {
        Deque<Integer> queue = new ArrayDeque<>();
        for(int i= 0;i < 10;i++){
            queue.add(i);
        }
        printQ(queue);

        Deque<Integer> stack = new ArrayDeque<>();
        for(int i = 0;i < 10;i++){
            stack.push(i);
        }
        printS(stack);
        /**
         * 0 1 2 3 4 5 6 7 8 9
         * 9 8 7 6 5 4 3 2 1 0
         */
    }
}