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


    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));
            
            
            
        }
    
    }

      

  • 相关阅读:
    0-Android系统各层中LOG的使用
    Android系统进程Zygote启动过程的源代码分析
    Android应用程序的Activity启动过程简要介绍和学习计划
    分享一个监测企业微信群人员变化的脚本...
    C++ 之 stl::string 写时拷贝导致的问题
    分享一个批量修改文件编码的python脚本
    分享stl sort函数坑点导致coredump问题
    关于使用repo时repo init和repo sync失败的一个解决方案
    sourceinsight sublimetext主题色配置
    父子进程之间的数据拷贝关系
  • 原文地址:https://www.cnblogs.com/ITPower/p/8625627.html
Copyright © 2020-2023  润新知