java编程思想第四版第十一章总结

简介: 容器类被分为两类:Collection和Map

1. 容器类被分为两类:Collection和Map


  • Collection是一个接口:包括:


  • List接口:


  • ArrayList:按照被插入顺序保存元素, 查询快, 增删改慢


  • LinkedList:按照被插入顺序保存元素, 增删改块,查询慢。


  • Set接口:元素不能重复


  • HashSet:已相当复杂的方式来存储元素,这种技术是最快的获取元素的方式


  • TreeSet:按照结果的升序保存元素


  • 可以自行执行排序方式。默认是按照字典顺序排序。如果想按照字母顺序排序,可以向TreeSet构造器中传入String。


  • CASE_INSENTIVE_ORDER.


  • LinkedHashSet:按照添加顺序保存元素


  • Queue接口


  • Map接口


  • HashMap:已相当复杂的方式来存储元素(散列函数),这种技术是最快的获取元素的方式


  • TreeMap:按照比较结果的升序保存,元素存储在红黑树数据结构。


  • LinkedHashMap:按照添加顺序保存元素。


2. 定义集合的时候,使用向上转型,是一个好的习惯


  List<Apple> list = new ArrayList<Apple>();


  注意:ArrayList已经被向上转型为List,这样做的好处是,如果你想使用其他List的实现类也是可以的。缺点是,在ArrayList中有一些额外的方法,不包含在List中,如果需要调用这些方法,还需要使用ArrayList来定义。


3. Collection集合的使用


  • 添加一组元素


package net.mindview.holding;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class AddingGroups {
    public static void main(String[] args) {
        //1. Collection集合,有一个构造方法, 可以接受一个集合作为参数, 将数组内容直接作为Collections的内容.
        Collection<Integer> collection = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5));
        Integer[] moreInts = {6, 7, 8, 9, 10};
        //2. 集合有一种方法,添加一个数组为它的元素. 这种方法没有Collections.addAll高效
        //原因: 第一种方式首先要初始化,然后将数组转换为集合后, 赋值. 第二种方法方法直接赋值,所以更高效
        collection.addAll(Arrays.asList(moreInts));
        //定义一个集合, 使用Collections.addAll()方式添加元素是一种推荐的方式.效率高
        Collections.addAll(collection, 11,12,13,14,15);
        Collections.addAll(collection, moreInts);
        //将数组直接转换为list,有两点需要说明:1. 大小固定, 不能添加元素,删除元素, 可以修改. 2. 局限性,下面的代码说明.
        List<Integer> list = Arrays.asList(16,17,18,19,20);
        list.set(1, 99);                
    }
}


相信看上面的注释, 下面说一说Arrays.asList()的局限性.


package net.mindview.holding;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
 * 这里说的是一个间接继承和直接继承的问题
 */
class Snow {}
class Powder extends Snow{}
class Light extends Powder{}
class Heavy extends Powder{}
class Crusty extends Snow{}
class Slush extends Snow{}
public class AsListInference {
    public static void main(String[] args) {
        //1. 下面的定义可以通过, 他们都继承自Snow
        List<Snow> snow1 = Arrays.asList(new Powder(), new Crusty(), new Slush());
        //2. 下面的定义不能通过, 因为他们都是间接继承自snow.而非直接
        //List<Snow> snow2 = Arrays.asList(new Light(), new Heavy());
        //3. 上面的方式编译不通过,但是下面的却可以.为什么呢? 因为他从第一个元素了解到了此目标类型是snow
        List<Snow> snow3 = new ArrayList<Snow>();
        Collections.addAll(snow3, new Light(), new Heavy());
        //4. 方法2可以通过显示类型参数说明,来明确指出转换的类型
        List<Snow> snow4 = Arrays.<Snow>asList(new Light(), new Heavy());
    }
}


容器的打印


  • 数组的打印,使用Arrays.toString(数组名);


public static void main(String[] args) {
    String[]  str = new String[5];
    System.out.println(str);
    System.out.println(Arrays.toString(str));
}


  • 容器的打印, 直接打印容器名, 无需任何帮助.


4. Stack


  栈: 先进后出(LIFO),有时栈也被称为叠加栈, 因为最后“压入”的,最先弹出。


  LinkedList具有能够直接实现栈的所有功能的方法。因此可以直接将LinkedList作为栈直接使用。


  也就是说LinkedList中有方法是先进后出的。


package net.mindview.holding;
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.removeFirst();
    }
    //取出
    public T pool(){
        return storage.removeFirst();
    }
    public boolean empty(){
        return storage.isEmpty();
    }
    public String toString(){
        return storage.toString();
    }
}


package net.mindview.holding;
public class StackTest {
    public static void main(String[] args) {
        Stack<String> stack = new Stack<String>();
        for(String s:"this is my dog!".split(" ")){
            stack.push(s);
        }
        if(!stack.empty()){
            System.out.println(stack.peek());
            System.out.println(stack.pop()+" ");
            System.out.println(stack.pop()+" ");
            System.out.println(stack.peek());
        }
    }
}


运行结果:


dog!
dog!
my
is


通过这个案例: 可以看出, 所谓的先进后出,指的是, add最后进来的, remove时最先出去. 跟排序没有任何关系.


5. Queue 队列


  队列是一个典型的先进先出的容器. 即从容器的一段放入,从另一端取出. 并且事物放入容器的顺序与取出的顺序是相同的。


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


package net.mindview.holding;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
public class QueueDemo {
    public static void print(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<Integer>();
        Random rand = new Random(47);
        for(int i=0; i<10; i++){
            //向队列里放入元素
            queue.offer(rand.nextInt(i+10));
        }
        print(queue);
        Queue<Character> qc = new LinkedList<Character>();
        for(char c:"Brontosaurus".toCharArray()){
            //向队列里放入元素
            qc.offer(c);
        }
        print(qc);
    }
}


8 1 1 1 5 14 3 1 0 1 
B r o n t o s a u r u s 


  • offer()方法,:在允许的情况下,将一个元素插入到队尾。或者返回false。


  • peek()和element():在不移除的情况下返回对头。但是peek方法在队列为空时,返回null。而element()会抛出NoSuchElementException异常。


  • poll()和remove():将移除并返回队头。poll在队列为空时返回null,remove在队列为空是抛出NoSuchElementException异常。


这里想Queue中放入元素使用的时offer,取出元素使用的时peek,删除元素使用的remove。先放进去的先取出来。


  我们再说到Stack时,看到LinkedList可以实现Stack先进后出。看到队列的Queue的时候, 又说LinkedList可以实现Queue先进先出。这是怎么回事呢?来看看API,原来是这么回事


6. PriorityQueue:优先级队列


  优先级队列声明,下一个弹出元素是最需要的元素。也就是说是优先级最高的元素。当你使用offer方法来出入一个对象时,这个对象会在队列中被排序。默认的顺序将使用对象在队列中的自然顺序。但你也可以通过自己的Comparator来修改这个顺序。


  PriorityQueue可以确保当你调用peek(), poll(), remove()方法时, 获取元素将是队列中优先级最高的元素.


package net.mindview.holding;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Random;
import java.util.Set;
public class PriorityQueueDemo {
    public static void main(String[] args) {
        /*
         * 对于数字而言, 最小的数字优先级最高
         */
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<Integer>();
        Random rand = new Random(47);
        for(int i=0;i<10;i++){
            priorityQueue.offer(rand.nextInt(i+10));
        }
        QueueDemo.print(priorityQueue);
        /*
         * 将一个list集合中的元素放入队列
         * 并且使用自定的排序方式排序
         */
        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<Integer>(ints);
        QueueDemo.print(priorityQueue);
        //提供了一个构造器, 使用自定义的排序方法.第二个参数是新的排序方法,继承了Comparator类.
        priorityQueue = new PriorityQueue<Integer>(ints.size(), Collections.reverseOrder());
        priorityQueue.addAll(ints);
        QueueDemo.print(priorityQueue);
        /*
         * 字符串集合放入到优先级队列
         */
        String fact = "EDUCATION SHOULD ESCHEW OBFUSACTION";
        List<String> strings = Arrays.asList(fact.split(" "));
        PriorityQueue<String> stringPQ = new PriorityQueue<String>(strings);
        QueueDemo.print(stringPQ);
        /*
         * 使用set存储不重复的字符集合
         * 最小的值有最高的优先级. 空格的优先级比字母高
         */
        Set<Character> charSet = new HashSet<Character>();
        for(char c: fact.toCharArray()){
            charSet.add(c);
        }
        PriorityQueue<Character> charPQ = new PriorityQueue<Character>(charSet);
        QueueDemo.print(charPQ);
    }
}


运行结果:


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 
EDUCATION ESCHEW OBFUSACTION SHOULD 
  A B C D E F H I L N O S T U W 

1. 数字越小,优先级越高


  2. 空格的优先级比字母高


  3. 字符串,字符都可转换为对应的数字处理.


7. Iterator


  java中, 用迭代器Iterator而不是集合Collection来表示集合间的共性。但是, 实现了Collection就意味着需要提供Iterator()方法。

 

  (未完,待完善)


8. Foreach和迭代器


  Iterable接口:该接口包含一个能够产生Iterator的iterator()方法,并且Iterable接口被foreach用来在序列中移动。因此,如果你创建的类实现了Iterable接口,都可以将它用于foreach语句中:


package net.mindview.holding;
import java.util.Iterator;
/**
 * Iterable 接口包含一个能够产生Iterator的iterator()方法. 并且Iterable接口被用来在foreach用来在序列中移动。
 * 因此,如果你创建了任何实现Iterable的类,都可以将其用于foreach语句中。
 * @author samsung
 *
 */
public class IterableClass implements Iterable<String>{
    protected String[] words = ("And that is how we know the Earth to be banana-shaped.").split(" ");
    @Override
    public Iterator<String> iterator() {
        // TODO Auto-generated method stub
        return new Iterator<String>(){
            private int index = 0;
            @Override
            public boolean hasNext() {
                return index < words.length;
            }
            @Override
            public String next() {
                return words[index++];
            }
            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }
    public static void main(String[] args) {
        //只要这个类实现了Iterable,就可以使用foreach语句遍历
        for(String str: new IterableClass()){
            System.out.println(str);
        }
    }
}


运行结果


And
that
is
how
we
know
the
Earth
to
be
banana-shaped.


9.适配器方法


  我们知道一个类如果实现了Iterable接口, 他就要重写返回Iterator类型的iterator方法,我们使用的时候,就可以使用foreach的方式来遍历这个类。但是,这种实现接口的方式,只能够有一个种遍历方法。假如:我现在想要有多种遍历方案。比如:正序遍历,反序遍历,该如何实现呢?我们使用适配器方法来实现。代码如下:


package net.mindview.holding;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
class ReversibleArrayList<T> extends ArrayList<T>{
    private static final long serialVersionUID = 1L;
    public ReversibleArrayList(Collection<T> c) {
        super(c);
    }
    /**
     * 实现了一个反转, 将传递过来的集合,反向输出
     */
    public Iterable<T> reversed(){
        return new Iterable<T>(){
            @Override
            public Iterator<T> iterator() {
                return new Iterator<T>(){
                    int current = size()-1;
                    @Override
                    public boolean hasNext() {
                        return current >= 0;
                    }
                    @Override
                    public T next() {
                        return get(current--);
                    }
                    @Override
                    public void remove() {
                        // TODO 
                    }
                };
            }
        };
    }
}
public class AdapterMethodIdiom {
    public static void main(String[] args) {
        ReversibleArrayList<String> r = new ReversibleArrayList(Arrays.asList("To be or not to be".split(" ")));
        for(String str: r){
            System.out.print(str + " ");
        }
        System.out.println();
        for(String str:r.reversed()){
            System.out.print(str + " ");
        }
    }
}


这个例子展示了, 我在一个类中,可以定义多种foreach循环的方式。下面我们使用这种方式,为IterableClass定义两种其他的循环方式:


package net.mindview.holding;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
/**
 * 继承了IterableClass类,就拥有了一种遍历方法了
 * @author samsung
 *
 */
public class MultiIteratorClass extends IterableClass{
    /**
     * 反序遍历
     * @param args
     */
    public Iterable<String> reverse(){
        return new Iterable<String>(){
            @Override
            public Iterator<String> iterator() {
                return new Iterator<String>(){
                    int count = words.length-1;
                    @Override
                    public boolean hasNext() {
                        return count >= 0;
                    }
                    @Override
                    public String next() {
                        return words[count--];
                    }
                    @Override
                    public void remove() {
                    }
                };
            }
        };
    }
    /**
     * 随机访问遍历
     * 这里没有创建自己的Iterator,而是直接返回被打乱的List中的Iterator.
     * 这里使用Collections.shuffle()方法并没有影响到原来的数组,这是将原来数组的元素的引用打乱了.注意,是引用打乱了.
     * 
     */
    public Iterable<String> randomized(){
        return new Iterable<String>(){
            @Override
            public Iterator<String> iterator() {
                List<String> shuffled = new ArrayList<String>(Arrays.asList(words));
                Collections.shuffle(shuffled, new Random(47));
                return shuffled.iterator();
            }
        };
    }
    public static void main(String[] args) {
        MultiIteratorClass m = new MultiIteratorClass();
        for(String s:m){
            System.out.print(s+" ");
        }
        System.out.println();
        for(String s: m.reverse()){
            System.out.print(s+" ");
        }
        System.out.println();
        for(String s: m.randomized()){
            System.out.print(s+" ");
        }
    }
}


这里面在说说Collection.shuffle()方法. 看下面的例子就明白了


package net.mindview.holding;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class ModifyingArraysAsList {
    public static void main(String[] args) {
        Random rand = new Random(47);
        Integer[] ia = {1,2,3,4,5,6,7,8,9,10};
        /*
         * list1包装了一层
         * 从结果可以看出: 如果数组转List后被包装一层,调用Collections.shuffle打乱顺序,
         * 打乱的是数组中元素的引用,数组的顺序没有改变
         */
        List<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia));
        System.out.println("Before shuffling:"+list1);
        Collections.shuffle(list1, rand);
        System.out.println("After shuffling: "+list1);
        System.out.println("array: "+Arrays.toString(ia));
        /*
         * list2没有包装
         * 从结果可以看出: 如果数组转List后没有包装,调用Collections.shuffle打乱顺序,打乱的是数组中元素的顺序
         */
        List<Integer> list2 = Arrays.asList(ia);
        System.out.println("Before shuffling:"+list2);
        Collections.shuffle(list2, rand);
        System.out.println("After shuffling: "+list2);
        System.out.println("array: "+Arrays.toString(ia));
    }
}
相关文章
|
3月前
|
Java 开发者
Java多线程编程中的常见误区与最佳实践####
本文深入剖析了Java多线程编程中开发者常遇到的几个典型误区,如对`start()`与`run()`方法的混淆使用、忽视线程安全问题、错误处理未同步的共享变量等,并针对这些问题提出了具体的解决方案和最佳实践。通过实例代码对比,直观展示了正确与错误的实现方式,旨在帮助读者构建更加健壮、高效的多线程应用程序。 ####
|
3月前
|
安全 Java UED
深入浅出Java多线程编程
【10月更文挑战第40天】在Java的世界中,多线程是提升应用性能和响应能力的关键。本文将通过浅显易懂的方式介绍Java中的多线程编程,从基础概念到高级特性,再到实际应用案例,带你一步步深入了解如何在Java中高效地使用多线程。文章不仅涵盖了理论知识,还提供了实用的代码示例,帮助你在实际开发中更好地应用多线程技术。
101 5
|
2月前
|
Java 程序员
Java编程中的异常处理:从基础到高级
在Java的世界中,异常处理是代码健壮性的守护神。本文将带你从异常的基本概念出发,逐步深入到高级用法,探索如何优雅地处理程序中的错误和异常情况。通过实际案例,我们将一起学习如何编写更可靠、更易于维护的Java代码。准备好了吗?让我们一起踏上这段旅程,解锁Java异常处理的秘密!
|
2月前
|
存储 缓存 Java
Java 并发编程——volatile 关键字解析
本文介绍了Java线程中的`volatile`关键字及其与`synchronized`锁的区别。`volatile`保证了变量的可见性和一定的有序性,但不能保证原子性。它通过内存屏障实现,避免指令重排序,确保线程间数据一致。相比`synchronized`,`volatile`性能更优,适用于简单状态标记和某些特定场景,如单例模式中的双重检查锁定。文中还解释了Java内存模型的基本概念,包括主内存、工作内存及并发编程中的原子性、可见性和有序性。
Java 并发编程——volatile 关键字解析
|
2月前
|
算法 Java 调度
java并发编程中Monitor里的waitSet和EntryList都是做什么的
在Java并发编程中,Monitor内部包含两个重要队列:等待集(Wait Set)和入口列表(Entry List)。Wait Set用于线程的条件等待和协作,线程调用`wait()`后进入此集合,通过`notify()`或`notifyAll()`唤醒。Entry List则管理锁的竞争,未能获取锁的线程在此排队,等待锁释放后重新竞争。理解两者区别有助于设计高效的多线程程序。 - **Wait Set**:线程调用`wait()`后进入,等待条件满足被唤醒,需重新竞争锁。 - **Entry List**:多个线程竞争锁时,未获锁的线程在此排队,等待锁释放后获取锁继续执行。
85 12
|
2月前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
223 2
|
3月前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
3月前
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####
|
2月前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
2月前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
72 3

热门文章

最新文章