您的位置:首页 > 产品设计 > UI/UE

Stack/Set/Map/Queue浅析

2017-07-21 15:36 183 查看

“栈”通常是表示“后进先出”的容器。就好比在盘子里叠入披萨,最后放进去的总是在最上面。
ListkedList具有能直接实现栈的所有功能的方法,因此可以直接将LinkedList当作栈使用。
不过,有时候一个真正的“栈”更能把事情说明白:


import java.util.LinkedList;

public class Stack<T>{

private LinkedList<T> storage = new LinkedList<T>();
public void push(T v){
storage.addFirst(v);
}
public T peek(){
return storage.getFirst();
}
public T pop(){
return storage.removeFirst();
}
public boolean empty(){
return storage.isEmpty();
}
public String toString(){
return storage.toString();
}
}


这里使用泛型,引入了在栈的类定义中最简单的可行示例。T表示告诉编译器这是参数化的类型,实际类型会在使用时被替换。如果我们只需要栈的行为,那使用继承就不合适了,因为这样就会产生具有LinkedList的其他所有方法的类。


public static void main(String[] args) {
//创建具有泛型T的Stack类。并声明了T为String类型
Stack<String> stack = new Stack<String>();
for(String s : "My dog has fleas".split(" ")){
stack.push(s);
}
while (!stack.empty()) {
//pop()方法是移除栈顶元素,并且返回。
System.out.println(stack.pop() + " ");
}
}


Set

Set不保存重复元素。如果我们需要查询功能,那么用hashSet是很好的选择,因为它专门针对了查找进行优化。
Set和Collection有完全一样的接口。实际上Set就是Collection,只是行为不同。行为的不同是继承和多态的典型应用。Set是基于对象的值来确定归属的。


public static void main(String[] args) {
Random rand = new Random(47);
Set<Integer> intset = new HashSet<Integer>();
for(int i = 0; i < 10000; i++){
intset.add(rand.nextInt(30));
}
System.out.println(intset);
}
//out
[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]


在hashSet中插入了0-29的10000个随机数,但是实际结果只有0-29,并且是无序的。这里的无序指的是存入和取出的顺序不一致。因为考虑到速度原因,HashSet用到了散列。其顺序与TreeSet和LinkedHashSet都不同,因为他们的实现具有不同的元素存储方式。TreeSet将元素存储在红-黑树数据结构中,而HashSet是散列函数。LinkedHashSet也使用了散列,但是看起来它使用了链表来维护插入顺序。如果我们需要排序,可以用TreeSet来替换HashSet,默认安装字典排序,并且区分大小写。如果想用字母排序,可以向TreeSet构造器传入String.CASE_INSENTIVE_ORDER


public static void main(String[] args) {
String sort = "A B C J Y O M G M U O aa b c j y o m g m u o";
Set<String> words = new TreeSet<String>();
for(String s : sort.split(" ")){
words.add(s);
}
System.out.println("not set para:" + words);
Set<String> words2 = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
for(String s : sort.split(" ")){
words2.add(s);
}
System.out.println("use set para:" + words2);
}
//out
not set para:[A, B, C, G, J, M, O, U, Y, a, b, c, g, j, m, o, u, y]
use set para:[A, aa, B, C, G, J, M, O, U, Y]


很明显String.CASE_INSENSITIVE_ORDER虽然是按照字符排序了,但是其实际含义是不区分大小写。也就是B和b只能在Set中存在一个了。


Map

将对象映射到其他对象的能力是一种解决编程问题的杀手锏。


public static void main(String[] args) {
Random rand = new Random(47);
Map<Integer, Integer> m = new HashMap<>();
for(int i = 0; i < 10000; i++){
int r = rand.nextInt(20);
Integer freq = m.get(r);
m.put(r, freq == null ? 1:freq+1);
}
System.out.println(m);
}
//out
{0=481, 1=502, 2=489, 3=508, 4=481, 5=503, 6=519, 7=471, 8=468, 9=549, 10=513, 11=531, 12=521, 13=506, 14=477, 15=497, 16=533, 17=509, 18=478, 19=464}


自动包装机制,将随机生成的int转换成了Integer类型,用生成的int类型去Map中找,是否有该key,如果没有会返回null,如果有则会返回对应的value值。为null的情况会赋值给freq为1,不为null就进行累加。在累加中,自动拆包又进行的操作,否则Integer不能进行累加的。


Map与数组和其他的Collection一样。可以很简单的扩展到多维结果,比如Map<Stirng,Map<Integer,String>>或者更复杂的数据结构。


//这句话表示,该变量petPeople的数据类型是Map,而Map中key是String,value是List。这个List的类型是未知但且继承String
public static Map<String,List<? extends String>> petPeople = new HashMap<>();
static{
petPeople.put("Dawn", Arrays.asList("Molly","Spot"));
petPeople.put("Kata", Arrays.asList("Shackleton","Elsie May","Margrett"));
petPeople.put("Marilyn", Arrays.asList("Louie aka Louis Snorkelstein Dupree",
"Stanford aka Srinky el Negro",
"Pinkola"));
petPeople.put("Luke", Arrays.asList("Fuzzy","Fizzy"));
petPeople.put("Isaac", Arrays.asList("Freckly"));
}
public static void main(String[] args) {
//keySet返回了所有key组成的Set
System.out.println("People: " + petPeople.keySet());
//values返回了所有对应的value,根据Set的默认排序
System.out.println("Pets: " + petPeople.values());
for (String string : petPeople.keySet()) {
System.out.println(string + " has:");
for (String s : petPeople.get(string)) {
System.out.println("   " + s);
}
}
}


Queue

队列是保持先进先出,和LinkedList相反。就像排队打饭一样,先排队的人,就能先打到饭,排除插队的情况。
队列在并发编程中特别重要,因为它们可以安全地将对象从一个任务传给另一个任务。
LinkedList提供了方法以支持队列的行为,并且它是想了Queue接口,因此LinkedList可以用做Queue的一种实现。通过将LinkedList向上转型为Queue。


public static void printQ(Queue queue){
while(queue.peek() != null){
System.out.print(queue.remove() + " ");
}
}
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);
}


offer()将一个元素插入到队列尾端。在将LinkedList转化为Queue后,LinkedList的方法会不能使用。当然我们可以将Queue又转成LinkedList。


PriorityQueue

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


public static void printQ(Queue queue){
while(queue.peek() != null){
System.out.print(queue.remove() + " ");
}
System.out.println();
}
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));
}
//输出第一次删除被返回的值
PriorityQueueDemo.printQ(priorityQueue);
//构建ints的List容器。此种构建方式,不允许被add
List<Integer> ints = Arrays.asList(25,22,20,18,14,9,3,1,1,2,3,9,14,18,21,23,25);
//将此List给优先级队列
priorityQueue = new PriorityQueue<>(ints);
PriorityQueueDemo.printQ(priorityQueue);
priorityQueue = new PriorityQueue<>(ints.size(),Collections.reverseOrder());
priorityQueue.addAll(ints);
PriorityQueueDemo.printQ(priorityQueue);
}


由此可以看出,队列运行重复,数字小的优先级高(比字母高)。大写字母比小写字母优先级高。
Collections.reverseOrder()同样是在JAVA SE5中增加的反序Comparator。


下节会讲Collection和Iterator和Foreach和适配器方法惯用法

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐