• 第十一章 持有对象


    第十一章 持有对象

    如果一个程序只包含固定数量且其生命周期都是已知对象,那么这是一个非常简单的程序

    需要任意时刻,任意位置创建任意数量的对象,不能依靠创建命名的引用来持有每一个对象:

    Mytpye aReference;(不知道会产生多少这样的引用)

    Java有多种方式保存对象:

    • 数组,编译器支持的类型,数组是保存一组对象最有效的方式。(保存基本类型数据,有固定大小)

    集合类(没有固定大小 Collection “容器”):

    • List
    • Set 每个值都只保存一个对象
    • Queue
    • Map 是允许你将某些对象与其他一些对象关联起来的关联数组

    11.1 泛型和类型安全的容器

    编辑器允许你向容器中插入不正确的类型

    最基本容器 ArrayList 保存的是Object,

    可以通过ArrayList.add 把Apple对象放进这个容器,当你使用ArrayList.get() 方法取出来你认为是Apple对象时,实际上时 Object引用,必须将其强制转型为((Apple)apples.get(i)).id();

    @SuppressWarning 注解及其参数表示只有关“不受检查的异常”的警告信息应该被抑制

    泛型 可以声明 ArrayList,尖括号括起来的是参数类型,(可以声明多个),指定了容器类型可以保存的类型,放置 编译器将错误类型的对象放置到容器中

    不仅限于 只能将该确切类型的对象放置到容器中,向上转型也作用于泛型

    练习1

    /* Create a new class called Gerbil with an int gerbilNumber that's
     * initialized in the constructor. Give it a method called hop() that displays
     * which gerbil number that is, and that it's hopping. Create an ArrayList and
     * add Gerbil objects to the List. Now use the get() method to move through
     * the List and call hop() for each Gerbil.
     */
    import java.util.*;
    class Gerbil {
        private int gerbilNumber;
        public Gerbil(int i) {
            gerbilNumber = i;
        }
        public void hop() {
            System.out.println("Gerbil " + gerbilNumber + " hops");
        }
    }
    
    public class No1Ex {
        public static void main(String[] args) {
            ArrayList<Gerbil> gerbils = new ArrayList<Gerbil>();
            for(int i = 0; i < 3; i++)
                gerbils.add(new Gerbil(i));
            for(int i = 0; i < 3; i++)
                gerbils.get(i).hop();
            // or, alternatively, using foreach syntax:
            for(Gerbil g : gerbils)
                g.hop();
        }
    }
    ========================================================================
    Gerbil 0 hops
    Gerbil 1 hops
    Gerbil 2 hops
    Gerbil 0 hops
    Gerbil 1 hops
    Gerbil 2 hops
    

    11.2 基本概念

    容器的用途是保存对象,两个不同的概念:

    • Collection 一个独立元素的序列
      • List 按照插入的顺序保存元素
      • Set 不能有重复元素
      • Quene 按照排队规则来确定对象产生的顺序
    • Map 成对 “键值对” 对象,允许用键 来查找值
      • ArrayList 允许你使用数字来查找值
      • 映射表 允许我们使用另一个对象来查找某个对象 也称为 “关联数组”
      • 字典 使用键对象来查找值对象

    **练习2 **

    // Modify SimpleCollection.java to use a Set for c.
    import java.util.*;
    
    public class No2Ex {
        public static void main(String[] args) {
            Set<Integer> c = new HashSet<Integer>();
            for(int i = 0; i < 10; i++)
                c.add(i); // Autoboxing
            for(Integer i : c)
                System.out.print(i + ", ");
        }
    }
    =====================================================
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 
    

    **练习3 **

    /* Modify innerclasses/Sequence.java so that you can add any number
     * of elements to it.
     */
    import java.util.*;
    
    interface Selector {
        boolean end();
        Object current();
        void next();
    }
    
    public class No3Ex {
        private ArrayList<Object> items = new ArrayList<Object>();
        public void add(Object x) {
            items.add(x);
        }
        private class Sequence3Selector implements Selector {
            private int i = 0;
            public boolean end() {
                return i == items.size();
            }
            public Object current() {
                return items.get(i);
            }
            public void next() {
                i++;
            }
        }
        public Selector selector() {
            return new Sequence3Selector();
        }
        public static void main(String[] args) {
            No3Ex s3 = new No3Ex();
            for(int i = 0; i < 10; i++)
                s3.add(i);
            Selector selector = s3.selector();
            while(!selector.end()) {
                System.out.print(selector.current() + " ");
                selector.next();
            }
            s3.add(10);
            s3.add(11);
            s3.add(12);
            s3.add(13);
            s3.add(13);
            s3.add("good bye");
            while(!selector.end()) {
                System.out.print(selector.current() + " ");
                selector.next();
            }
        }
    }
    ========================================================================
    0 1 2 3 4 5 6 7 8 9 10 11 12 13 13 good bye 
    

    11.3 添加一组元素

    Arrays.asList() 方法接受一个数组或是用逗号分隔的元素列表(使用可变参数),并将其转换成一个List对象。

    Collections.addAll()方法接受一个Collection对象,以及一个数组或是用一个逗号分隔的列表,将元素添加到Collection中。

    数组不可以该改变大小

    Arrays.lIst 方法限制是它对所产生的List 的类型做出了最理想的假设,并没有注意你对他会赋予什么样的类型

    # give a hint using an explicit type argument specification
    List<Snow> snow4 = Arrays.<Snow>asList(new Light(),new Heavy())
    

    11.4 容器的打印

    使用 Arrays.toString ()来产生数组的可打印表示

    set:

    • HashSet 相当复杂的方式来存储元素,存储的顺序看起来无实际意义
    • TreeSet 比较结果的升序保存对象
    • LinkedHashSet 添加的顺序保存对象

    Map:

    • HashMap 最快的查找速度
    • TreeMap 比较结果的升序保存键
    • LinkedHashMap 插入顺序来保存其元素

    练习4

    /* Create a generator class that produces character names (as String objects)
     * from your favorite movie (you can use Snow White or Star Wars as a
     * fallback) each time you call next(), and loops around to the beginning of
     * the character list when it runs out of names. Use this generator to fill
     * an array, an ArrayList, a LinkedList, a HashSet, a LinkedHashSet, and a
     * TreeSet, then print each container.
     */
    import java.util.*;
    
    class Generator {
        int key = 0;
        public String next() {
            switch(key) {
                default:
                case 0 : key++; return "Snow White";
                case 1 : key++; return "Bashful";
                case 2 : key++; return "Doc";
                case 3 : key++; return "Dopey";
                case 4 : key++; return "Grumpy";
                case 5 : key++; return "Happy";
                case 6 : key++; return "Sleepy";
                case 7 : key = 0; return "Sneezy";
            }
        }
        public void fillA(String[] a) {
            for(int i = 0; i < a.length; i++)
                a[i] = next();
        }
        public Collection fill(Collection<String> c, int n) {
            for(int i = 0; i < n; i++)
                c.add(next());
            return c;
        }
    }
    
    public class No4Ex  {
        public static void main(String[] args) {
            Generator gen = new Generator();
            String[] a = new String[10];
            gen.fillA(a);
            for(String s : a)
                System.out.print(s + ", ");
            System.out.println();
            System.out.println(gen.fill(new ArrayList<String>(), 10));
            System.out.println(gen.fill(new LinkedList<String>(), 10));
            System.out.println(gen.fill(new HashSet<String>(), 10));
            System.out.println(gen.fill(new LinkedHashSet<String>(), 10));
            System.out.println(gen.fill(new TreeSet<String>(), 10));
        }
    }
    =========================================================================
    Snow White, Bashful, Doc, Dopey, Grumpy, Happy, Sleepy, Sneezy, Snow White, Bashful, 
    [Doc, Dopey, Grumpy, Happy, Sleepy, Sneezy, Snow White, Bashful, Doc, Dopey]
    [Grumpy, Happy, Sleepy, Sneezy, Snow White, Bashful, Doc, Dopey, Grumpy, Happy]
    [Sleepy, Happy, Sneezy, Snow White, Doc, Grumpy, Dopey, Bashful]
    [Snow White, Bashful, Doc, Dopey, Grumpy, Happy, Sleepy, Sneezy]
    [Bashful, Doc, Dopey, Grumpy, Happy, Sleepy, Sneezy, Snow White]
    

    11.5 List

    List承诺可以将元素维护再特定的序列中,List接口再Collection的基础上添加了大量的方法,使得List的中间可以插入和移除元素。

    两种基本的List:

    • ArrayList,插入删除和移动元素慢,擅长随机访问
    • LinkedList 代价较低的List中间进行插入和删除操作,提供了优化顺序访问

    练习5

    package 第十一章持有对象;
    
    /* Modify ListFeatures.java so that it uses Integers (remember
     * autoboxing!) instead of Pets, and explain any difference in
     * results.
     */
    import java.util.*;
    
    public class No5Ex {
        // method to make a List<Integer> with random values < n:
        public static List<Integer> listOfRandInteger(int length, int n) {
            Random rand = new Random();
            List<Integer> li = new ArrayList<Integer>();
            for(int i = 0; i < length; i++)
                li.add(rand.nextInt(n));
            return li;
        }
        public static void main(String[] args) {
            Random rand = new Random();
            List<Integer> li = listOfRandInteger(7, 10);
            System.out.println("1: " + li);
            Integer h = new Integer(rand.nextInt(10));
            li.add(h);
            System.out.println("2: " + li);
            System.out.println("3: " + li.contains(h));
            // removes the first instance equivalent to Integer h:
            li.remove(h);
            System.out.println("3.5: " + li);
            Integer p = li.get(2);
            System.out.println("4: " + p + " " +  li.indexOf(p));
            Integer cy = new Integer(rand.nextInt(10));
            System.out.println("5: " + cy +" " + li.indexOf(cy));
            System.out.println("6: " + li.remove(cy));
            System.out.println("7: " + li.remove(p));
            System.out.println("8: " + li);
            li.add(3, new Integer(rand.nextInt(10)));
            System.out.println("9: " + li);
            List<Integer> sub = li.subList(1, 4);
            System.out.println("sublist: " + sub);
            System.out.println("10: " + li.containsAll(sub));
            // will also sort sub elements within li:
            Collections.sort(sub);
            System.out.println("sorted sublist: " + sub);
            System.out.println("11: " + li.containsAll(sub));
            System.out.println("11.25: " + li);
            // will also shuffle sub elements within li:
            Collections.shuffle(sub, rand);
            System.out.println("11.5: " + li);
            System.out.println("shuffled sublist: " + sub);
            System.out.println("12: " + li.containsAll(sub));
            List<Integer> copy = new ArrayList<Integer>(li);
            System.out.println("12.5: " + li);
            sub = Arrays.asList(li.get(1), li.get(4));
            System.out.println("sub: " + sub);
            copy.retainAll(sub);
            System.out.println("13: " + copy);
            copy = new ArrayList<Integer>(li);
            copy.remove(2);
            System.out.println("14: " + copy);
            copy.removeAll(sub);
            System.out.println("15: " + copy);
            if(copy.size() > 1) // to avoid out of bounds exception
                copy.set(1, 8); // autoboxing int -> Integer
            System.out.println("16: " + copy);
            if(copy.size() > 2)
                copy.addAll(2, sub);
            System.out.println("17: " + copy);
            System.out.println("18: " + li.isEmpty());
            li.clear();
            System.out.println("19: " + li);
            System.out.println("20: " + li.isEmpty());
            li.addAll(listOfRandInteger(4, 10));
            System.out.println("21: " + li);
            Object[] o = li.toArray();
            System.out.println("22: " + o[3]);
            Integer[] ia = li.toArray(new Integer[0]);
            System.out.println("23: " + ia[3]);
        }
    }
    ===================================================================
    1: [6, 5, 9, 8, 6, 1, 3]
    2: [6, 5, 9, 8, 6, 1, 3, 3]
    3: true
    3.5: [6, 5, 9, 8, 6, 1, 3]
    4: 9 2
    5: 2 -1
    6: false
    7: true
    8: [6, 5, 8, 6, 1, 3]
    9: [6, 5, 8, 9, 6, 1, 3]
    sublist: [5, 8, 9]
    10: true
    sorted sublist: [5, 8, 9]
    11: true
    11.25: [6, 5, 8, 9, 6, 1, 3]
    11.5: [6, 9, 5, 8, 6, 1, 3]
    shuffled sublist: [9, 5, 8]
    12: true
    12.5: [6, 9, 5, 8, 6, 1, 3]
    sub: [9, 6]
    13: [6, 9, 6]
    14: [6, 9, 8, 6, 1, 3]
    15: [8, 1, 3]
    16: [8, 8, 3]
    17: [8, 8, 9, 6, 3]
    18: false
    19: []
    20: true
    21: [6, 1, 8, 8]
    22: 8
    23: 8
    

    练习7

    /* Create a class, then make an initialized array of objects of your class
     * Fill a List from your array. Create a subset of your List by using
     * subList(), then remove this subset from your List.
     */
    import java.util.*;
    
    class Tester {
        public static int counter = 0;
        private int id = counter++;
        public String toString() { return String.valueOf(id); }
    }
    
    public class No7Ex {
        public static void main(String[] args) {
            Tester[] t = new Tester[10];
            for(int i = 0; i < t.length; i++)
                t[i] = new Tester();
            List<Tester> lt = new ArrayList<Tester>();
            for(Tester x : t) lt.add(x);
            System.out.println("list of Tester: " + lt);
            List<Tester> sub = lt.subList(2, 6);
            System.out.println("subList: " + sub);
            // produces run time ConcurrentModificationException:
            // lt.removeAll(sub);
            // so, first make copy, remove sub, re-assign lt:
            List<Tester> copy = new ArrayList<Tester>(lt);
            copy.removeAll(sub);
            System.out.println("copy: " + copy);
            lt = copy;
            System.out.println("list of Tester: " + lt);
        }
    }
    ======================================================================
    list of Tester: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    subList: [2, 3, 4, 5]
    copy: [0, 1, 6, 7, 8, 9]
    list of Tester: [0, 1, 6, 7, 8, 9
    

    11.6 迭代器

    任何容器,都必须有某种方式可以插入元素并将它们再次取回 对于List 是add(),和get().

    怎么样不重写代码就可以应用不同的容器类型?

    迭代器(设计模式)

    迭代器遍历并选择序列中的对象,而客户端程序员不必知道或关心该序列底层的结构。

    迭代器通常被称为 轻量级对象:创建它的代价小。

    Java的Iterator只能单向移动

    • 使用方法Iterator()要求容器返回一个Iterator。Iterator将准备好返回序列的第一个元素
    • 使用next()获得序列的下一个元素
    • 使用hasNext检查序列中是否还有元素
    • 使用Remove()将迭代器新近返回的元素删除

    练习8

    // Modify Exercise 1 so it uses an Iterator to move through the List while
    // calling hop().
    import java.util.*;
    
    public class No8Ex {
        public static void main(String[] args) {
            ArrayList<Gerbil> gerbils = new ArrayList<Gerbil>();
            for(int i = 0; i < 3; i++)
                gerbils.add(new Gerbil(i));
            Iterator<Gerbil> it = gerbils.iterator();
            while(it.hasNext())
                it.next().hop();
        }
    }
    ======================================================================
    Gerbil 0 hops
    Gerbil 1 hops
    Gerbil 2 hops
    

    练习9

    // Modify innerclasses/Sequence.java so that Sequence works with an Iterator
    // instead of a Selector.
    import java.util.*;
    
    public class No9Sequence {
        private ArrayList<Object> items = new ArrayList<Object>();
        public void add(Object x) {
            items.add(x);
        }
        public Iterator iterator() {
            return items.iterator();
        }
        public static void main(String[] args) {
            No9Sequence sequence = new No9Sequence();
            for(int i = 0; i < 10; i++)
                sequence.add(Integer.toString(i));
            Iterator it = sequence.iterator();
            while(it.hasNext()) {
                System.out.print(it.next() + " ");
            }
        }
    }
    =============================================================================
    0 1 2 3 4 5 6 7 8 9 
    

    练习10

    /* Change Exercise 9 in the Polymorphism chapter to use an ArrayList to
     * hold the Rodents and an Iterator to move through the sequence of
     * Rodents.
     */
    import java.util.*;
    
    class RandomRodentGenerator {
        private Random rand = new Random();
        public Rodent next() {
            switch(rand.nextInt(3)) {
                default:
                case 0: return new Mouse();
                case 1: return new Rat();
                case 2: return new Squirrel();
            }
        }
    }
    
    class Rodent {
        private String name = "Rodent";
        protected void eat() {
            System.out.println("Rodent.eat()"); }
        protected void run() {
            System.out.println("Rodent.run()"); }
        protected void sleep() {
            System.out.println("Rodent.sleep()"); }
        public String toString() { return name; }
    }
    
    class Mouse extends Rodent {
        private String name = "Mouse";
        protected void eat() {
            System.out.println("Mouse.eat()"); }
        protected void run() {
            System.out.println("Mouse.run()"); }
        protected void sleep() {
            System.out.println("Mouse.sleep()"); }
        public String toString() { return name; }
    }
    
    class Rat extends Rodent {
        private String name = "Rat";
        protected void eat() {
            System.out.println("Rat.eat()"); }
        protected void run() {
            System.out.println("Rat.run()"); }
        protected void sleep() {
            System.out.println("Rat.sleep()"); }
        public String toString() { return name; }
    }
    
    class Squirrel extends Rodent {
        private String name = "Squirrel";
        protected void eat() {
            System.out.println("Squirrel.eat()"); }
        protected void run() {
            System.out.println("Squirrel.run()"); }
        protected void sleep() {
            System.out.println("Squirrel.sleep()"); }
        public String toString() { return name; }
    }
    
    public class No10Rodent {
        private static RandomRodentGenerator gen =
                new RandomRodentGenerator();
        public static void main(String[] args) {
            List<Rodent> rodentList = new ArrayList<Rodent>();
            for(int i = 0; i < 3; i++)
                rodentList.add(gen.next());
            Iterator<Rodent> it = rodentList.iterator();
            while(it.hasNext()) {
                Rodent r = it.next();
                System.out.print(r + ": ");
                r.eat();
                r.run();
                r.sleep();
            }
        }
    }
    =========================================================================
    Squirrel: Squirrel.eat()
    Squirrel.run()
    Squirrel.sleep()
    Squirrel: Squirrel.eat()
    Squirrel.run()
    Squirrel.sleep()
    Mouse: Mouse.eat()
    Mouse.run()
    Mouse.sleep()
    

    练习11

    /* Write a method that uses an Iterator to step through a Collection and
     * print the toString() of each object in the container. Fill all the different
     * types of Collections with objects and apply your method to each container.
     */
    import java.util.*;
    
    public class No11Ex {
        public static void printAny(Collection c) {
            Iterator it = c.iterator();
            while(it.hasNext())
                System.out.print(it.next() + " ");
            System.out.println();
        }
        public static void main(String[] args) {
            ArrayList<Integer> al =
                    new ArrayList<Integer>(Arrays.asList(1, 2, 3));
            LinkedList<Character> ll =
                    new LinkedList<Character>(Arrays.asList('a', 'b', 'c'));
            HashSet<Float> hs =
                    new HashSet<Float>(Arrays.asList(1.1f, 2.2f, 3.3f));
            TreeSet<Double> ts =
                    new TreeSet<Double>(Arrays.asList(1.11, 2.22, 3.33));
            LinkedHashSet<Integer> lhs =
                    new LinkedHashSet<Integer>(Arrays.asList(11, 22, 33));
            printAny(al);
            printAny(ll);
            printAny(hs);
            printAny(ts);
            printAny(lhs);
        }
    }
    ========================================================================
    1 2 3 
    a b c 
    3.3 1.1 2.2 
    1.11 2.22 3.33 
    11 22 33 
    

    11.6.1 Listlterator

    ListIterator 是一个强大的Iterator子集

    可以双向移动,产生相当于迭代器再列表中指向的当前位置的前一个和后一个元素的索引。set()方法替换它访问过的最后一个元素

    练习12

    /* Write a method that uses an Iterator to step through a Collection and
     * print the toString() of each object in the container. Fill all the different
     * types of Collections with objects and apply your method to each container.
     */
    import java.util.*;
    
    public class No11Ex {
        public static void printAny(Collection c) {
            Iterator it = c.iterator();
            while(it.hasNext())
                System.out.print(it.next() + " ");
            System.out.println();
        }
        public static void main(String[] args) {
            ArrayList<Integer> al =
                    new ArrayList<Integer>(Arrays.asList(1, 2, 3));
            LinkedList<Character> ll =
                    new LinkedList<Character>(Arrays.asList('a', 'b', 'c'));
            HashSet<Float> hs =
                    new HashSet<Float>(Arrays.asList(1.1f, 2.2f, 3.3f));
            TreeSet<Double> ts =
                    new TreeSet<Double>(Arrays.asList(1.11, 2.22, 3.33));
            LinkedHashSet<Integer> lhs =
                    new LinkedHashSet<Integer>(Arrays.asList(11, 22, 33));
            printAny(al);
            printAny(ll);
            printAny(hs);
            printAny(ts);
            printAny(lhs);
        }
    }
    1 2 3 
    a b c 
    3.3 1.1 2.2 
    1.11 2.22 3.33 
    11 22 33 
    

    11.7 LinkedList

    再List中插入和移除比ArrayList更高效,但是随机访问方面要逊色一些

    练习13

    public class Controller13 {
    	// A class from java.util to hold Event objects:
    	private LinkedList<Event> eventList = new LinkedList<Event>();
    	public void addEvent(Event c) { eventList.add(c); }		
    	public void run() {		
    		LinkedList<Event> eventListCopy = 
    			new LinkedList<Event>(eventList);
    		ListIterator<Event> it 
    			= eventListCopy.listIterator();
    		while(it.hasNext()) {	
    			it.next().action();
    			it.previous();		
    			System.out.println(it.next());		
    		}
    	}	
    }
    ====================================================================
    

    练习14

    /* Create an empty LlinkedList<Integer>. Using a ListIterator, add Integers
     * to the list by always inserting them in the middle of the list.
     */
    import java.util.*;
    
    public class No14Ex {
        static void addMiddle(LinkedList<Integer> l, Integer[] ia) {
    
            for(Integer i : ia) {
                ListIterator<Integer> it =
                        l.listIterator((l.size())/2);
                it.add(i);
                System.out.println(l);
            }
        }
        public static void main(String[] args) {
            LinkedList<Integer> li = new LinkedList<Integer>();
            Integer[] x = {0, 1, 2, 3, 4, 5, 6, 7};
            No14Ex.addMiddle(li, x);
        }
    }
    =========================================================
    [0]
    [1, 0]
    [1, 2, 0]
    [1, 3, 2, 0]
    [1, 3, 4, 2, 0]
    [1, 3, 5, 4, 2, 0]
    [1, 3, 5, 6, 4, 2, 0]
    [1, 3, 5, 7, 6, 4, 2, 0]
    

    11.8 Stack

    “栈” 通常是指 “后进先出” (LIFO)的容器。

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

    练习15

    /* Stacks are often used to evaluate expressions in programming 
    * languages. Using net.mindview.util.Stack, evaluate the following
    * expression, where '+' means "push the following letter onto the 
    * stack," and '-' means "pop the top of the stack and print it":
    * "+U+n+c---+e+r+t---+a+i+n+t+y---+ -+r+u--+l+e+s---"
    */
    import java.util.Stack;
    
    public class No15Ex {
        public static void main(String[] args) {
            Stack<Character> sc = new Stack<Character>();
            sc.push('U');
            sc.push('n');
            sc.push('c');
            System.out.print(sc.pop());
            System.out.print(sc.pop());
            System.out.print(sc.pop());
            sc.push('e');
            sc.push('r');
            sc.push('t');
            System.out.print(sc.pop());
            System.out.print(sc.pop());
            System.out.print(sc.pop());
            sc.push('a');
            sc.push('i');
            sc.push('n');
            sc.push('t');
            System.out.print(sc.pop());
            System.out.print(sc.pop());
            System.out.print(sc.pop());
            sc.push(' ');
            System.out.print(sc.pop());
            sc.push('r');
            sc.push('u');
            System.out.print(sc.pop());
            System.out.print(sc.pop());
            sc.push('l');
            sc.push('e');
            sc.push('s');
            System.out.print(sc.pop());
            System.out.print(sc.pop());
            System.out.print(sc.pop());
        }
    }
    ================================================================
    cnUtretni ursel
    

    11.9 Set

    set 如果你试图将相同对象的多个实例添加到Set中,那么它会阻止这种现象。

    Set 最常被使用的是测试归属性,你可以很容易的询问某个对象是否在某个Set里面

    查找是Set最重要的操作 使用contains()测试Set的归属性

    HashSet对快速查找进行了优化(散列)

    如何相对结果排序,一种方式是使用TreeSet来替代HashSet(默认是按照字典排序,给TreeSet构造器传入String.CASE_INSENTIVE_ORDER比较器就是按照字母排序)

    HashSet所维护的顺序于TreeSet或者LinkedHashSet都不同,它们的实现具有不同的元素存储方式

    • TreeSet将元素存储在红-黑树数据结构中
    • HashSet使用的是散列函数
    • LinkedHashSet也使用散列函数,但是它使用了链表来维护元素的插入顺序

    11.10 Map

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

    例如对落入不同范围的随机数进行计数,Map可以解决问题,键是Random产生的数字,值是出现的次数

    Map于数组和其他Collection一样,可以扩展到多维,只需要将其值设置为Map(这些Map的值可以是其他容器,甚至是其他map)

    练习17

    /* Take the Gerbil class in Exercise 1 and put it into a Map instead,
     * associating each Gerbil's name (e.g. "Fuzzy" or "Spot") as a String (the
     * key) for each Gerbil (the value) you put in the table. Get an Iterator for
     * the keySet() and use it to move through the Map, looking up the Gerbil for
     * each key and printing out the key and telling the Gerbil to hop().
     */
    import java.util.*;
    
    class Gerbil {
        private int gerbilNumber;
        public Gerbil(int i) {
            gerbilNumber = i;
        }
        public void hop() {
            System.out.println("gerbil " + gerbilNumber + " hops");
        }
    }
    
    public class No17Gerbils {
        public static void main(String[] args) {
            Map<String, Gerbil> gerbils = new HashMap<String, Gerbil>();
            gerbils.put("Fuzzy", new Gerbil(0));
            gerbils.put("Spot", new Gerbil(1));
            gerbils.put("Speedy", new Gerbil(2));
            gerbils.put("Dopey", new Gerbil(3));
            gerbils.put("Sleepy", new Gerbil(4));
            gerbils.put("Happy", new Gerbil(5));
            Iterator<String> it = gerbils.keySet().iterator();
            while(it.hasNext()) {
                String s = it.next();
                System.out.print(s + ": ");
                gerbils.get(s).hop();
            }
        }
    }
    =====================================================================
    Sleepy: gerbil 4 hops
    Happy: gerbil 5 hops
    Spot: gerbil 1 hops
    Speedy: gerbil 2 hops
    Fuzzy: gerbil 0 hops
    Dopey: gerbil 3 hops
    

    练习18

    /* Fill a HashMap with key-value pairs. Print the results to show ordering
    * by hash code. Extract the pairs, sort by key, and place the result into a 
    * LinkedHashMap. Show that the insertion order is maintained. 
    */
    import java.util.*;
    
    class Gerbil {
        private int gerbilNumber;
        public Gerbil(int i) {
            gerbilNumber = i;
        }
        public void hop() {
            System.out.println("gerbil " + gerbilNumber + " hops");
        }
    }
    public class No18Ex {
        public static void main(String[] args) {
            Map<String, Gerbil> gerbils = new HashMap<String, Gerbil>();
            gerbils.put("Fuzzy", new Gerbil(0));
            gerbils.put("Spot", new Gerbil(1));
            gerbils.put("Speedy", new Gerbil(2));
            gerbils.put("Dopey", new Gerbil(3));
            System.out.println(gerbils);
            System.out.println();
            Set<String> sortedKeys =
                    new TreeSet<String>(gerbils.keySet());
            System.out.println(sortedKeys);
            System.out.println();
            Map<String, Gerbil> sortedGerbils =
                    new LinkedHashMap<String, Gerbil>();
            for(String s : sortedKeys) {
                System.out.print("Adding " + s + ", ");
                sortedGerbils.put(s, gerbils.get(s));
            }
            System.out.println();
            System.out.println();
            System.out.println(sortedGerbils);
            System.out.println();
            // or, just:
            Map<String, Gerbil> sortedGerbils2 =
                    new TreeMap<String, Gerbil>(gerbils);
            System.out.println(sortedGerbils2);
        }
    }
    =================================================================
    {Spot=第十一章持有对象.Gerbil@74a14482, Speedy=第十一章持有对象.Gerbil@1540e19d, Fuzzy=第十一章持有对象.Gerbil@677327b6, Dopey=第十一章持有对象.Gerbil@14ae5a5}
    
    [Dopey, Fuzzy, Speedy, Spot]
    
    Adding Dopey, Adding Fuzzy, Adding Speedy, Adding Spot, 
    
    {Dopey=第十一章持有对象.Gerbil@14ae5a5, Fuzzy=第十一章持有对象.Gerbil@677327b6, Speedy=第十一章持有对象.Gerbil@1540e19d, Spot=第十一章持有对象.Gerbil@74a14482}
    
    {Dopey=第十一章持有对象.Gerbil@14ae5a5, Fuzzy=第十一章持有对象.Gerbil@677327b6, Speedy=第十一章持有对象.Gerbil@1540e19d, Spot=第十一章持有对象.Gerbil@74a14482}
    

    练习19

    HashMap

    {Spot=第十一章持有对象.Gerbil@74a14482, Speedy=第十一章持有对象.Gerbil@1540e19d, Fuzzy=第十一章持有对象.Gerbil@677327b6, Dopey=第十一章持有对象.Gerbil@14ae5a5}
    
    [Spot, Speedy, Fuzzy, Dopey]
    
    Adding Spot, Adding Speedy, Adding Fuzzy, Adding Dopey, 
    
    {Spot=第十一章持有对象.Gerbil@74a14482, Speedy=第十一章持有对象.Gerbil@1540e19d, Fuzzy=第十一章持有对象.Gerbil@677327b6, Dopey=第十一章持有对象.Gerbil@14ae5a5}
    
    {Dopey=第十一章持有对象.Gerbil@14ae5a5, Fuzzy=第十一章持有对象.Gerbil@677327b6, Speedy=第十一章持有对象.Gerbil@1540e19d, Spot=第十一章持有对象.Gerbil@74a14482}
    

    LinkedHashMap

    {Spot=第十一章持有对象.Gerbil@74a14482, Speedy=第十一章持有对象.Gerbil@1540e19d, Fuzzy=第十一章持有对象.Gerbil@677327b6, Dopey=第十一章持有对象.Gerbil@14ae5a5}
    
    [Spot, Speedy, Fuzzy, Dopey]
    
    Adding Spot, Adding Speedy, Adding Fuzzy, Adding Dopey, 
    
    {Spot=第十一章持有对象.Gerbil@74a14482, Speedy=第十一章持有对象.Gerbil@1540e19d, Fuzzy=第十一章持有对象.Gerbil@677327b6, Dopey=第十一章持有对象.Gerbil@14ae5a5}
    
    {Dopey=第十一章持有对象.Gerbil@14ae5a5, Fuzzy=第十一章持有对象.Gerbil@677327b6, Speedy=第十一章持有对象.Gerbil@1540e19d, Spot=第十一章持有对象.Gerbil@74a14482}
    

    练习20

    import java.util.*;
    import net.mindview.util.*;
    
    public class Vowels16 {
    	static void vowelCounter(Set<String> st) {
    		Set<Character> vowels = new TreeSet<Character>();
    		Collections.addAll(vowels, 
    			'A', 'E', 'I', 'O', 'U', 'a', 'e', 'i', 'o', 'u');
    		int allVowels = 0;
    		for(String s : st) {
    			int count = 0;
    			for(Character v : s.toCharArray()) {		
    				if(vowels.contains(v)) {
    					count++;
    					allVowels++; 
    				}
    			}
    			System.out.print(s + ": " + count + ", ");		
    		}
    		System.out.println();	
    		System.out.print("Total vowels: " + allVowels);
    	}
    	public static void main(String[] args) {
    		Set<String> words = new TreeSet<String>(
    			new TextFile("SetOperations.java", "\W+"));
    		System.out.println(words);
    		System.out.println();
    		vowelCounter(words);		
    	}		
    }
    

    练习21

    /* Using a Map<String,Integer>, follow the form of UniqueWords.java to create a
    * program that counts the occurrence of words in a file. Sort the results using 
    * Collections.sort() with a second argument of String.CASE_INSENSITIVE_ORDER (to
    * produce an alphabetic sort), and display the result.
    */
    import java.util.*;
    import net.mindview.util.*;
    
    public class UniqueWords21 {
    	public static void main(String[] args) {
    		List<String> words = new ArrayList<String>(
    			new TextFile("SetOperations.java", "\W+"));
    		System.out.println("Words to count: " + words);
    		Collections.sort(words, String.CASE_INSENSITIVE_ORDER);
    		Map<String,Integer> wordCount =
    			new LinkedHashMap<String,Integer>();
    		Iterator it = words.iterator();
    		int totalWords = 0;
    		while(it.hasNext()) {
    			String s = (String)it.next();
    			if(words.contains(s)) {
    				Integer count = wordCount.get(s);
    				wordCount.put(s,
    					count == null ? 1 : count + 1);
    				totalWords++;
    			}
    		}
    		System.out.println();
    		System.out.println("Word count: " + wordCount);
    		System.out.println();
    		System.out.println("Total words: " + totalWords);		
    	}		
    }
    

    练习24

    /* Fill a LinkedHashMap with String keys and objects of your choice.
     * Now extract the pairs, sort them based on the keys, and reinsert
     * them into the Map.
     */
    // see also solution - holding/Ex24b.java
    import java.util.*;
    
    
    public class No24Ex{
        public static void main(String[] args) {
            Map<String,Integer> m =
                    new LinkedHashMap<String,Integer>();
            m.put("ten", 10);
            m.put("nine", 9);
            m.put("eight", 8);
            m.put("seven", 7);
            m.put("six", 6);
            m.put("five", 5);
            m.put("four", 4);
            m.put("three", 3);
            m.put("two", 2);
            m.put("one", 1);
            m.put("zero", 0);
            System.out.println("Map to sort: " + m);
            // temporary map to hold entrys:
            Map<String,Integer> mTemp =
                    new LinkedHashMap<String,Integer>();
            // use TreeSet to sort the keySet():
            Set<String> ss = new TreeSet<String>(m.keySet());
            // mover sorted keys to temp map:
            Iterator<String> itss = ss.iterator();
            while(itss.hasNext()) {
                String s = (String)itss.next();
                Integer i = m.get(s);
                m.remove(s);
                mTemp.put(s, i);
            }
            // get sorted list of temp keys:
            Set<String> ssTemp =
                    new TreeSet<String>(mTemp.keySet());
            // move sorted entrys back to map:
            Iterator<String> itssTemp = ssTemp.iterator();
            while(itssTemp.hasNext()) {
                String s = (String)itssTemp.next();
                Integer i = mTemp.get(s);
                mTemp.remove(s);
                m.put(s, i);
            }
            // done with temp:
            mTemp.clear();
            System.out.println("Sorted map: " + m);
        }
    }
    ============================================================
    Map to sort: {ten=10, nine=9, eight=8, seven=7, six=6, five=5, four=4, three=3, two=2, one=1, zero=0}
    Sorted map: {eight=8, five=5, four=4, nine=9, one=1, seven=7, six=6, ten=10, three=3, two=2, zero=0}
    

    11.11 Quene

    ​ 队列是一个典型的“先进先出(FIFO)”容器

    放入顺序与取出顺序是相同的

    可靠的将对象从程序的某个区域传输到另一个区域的途径。并发编程中非常重要

    LinkedList 提供了方法以支持队列的行为,为它实现了Quene接口。

    offer将元素插到队尾。

    peek()和element()都将在不移除的情况下返回队头,peek()方法在队列为空返回null

    poll()和remove()方法将移除并返回队头。

    练习27

    /* Write a class called Command that contains a String and has a method operation()
     * that displays the String. Write a second class with a method that fills a Queue
     * with Command objects and returns it. Pass the filled Queue to a method in a third
     * class that consumes the objects in the Queue and calls their operation() methods.
     */
    import java.util.*;
    
    class Command {
        String s;
        Command(String s) { this.s = s; }
        void operation() { System.out.print(s); }
    }
    
    class Build {
        Queue<Command> makeQ() {
            Queue<Command> q = new LinkedList<Command>();
            for(int i = 0; i < 10; i++)
                q.offer(new Command(i + " "));
            return q;
        }
    }
    
    public class No27Ex {
        public static void commandEater(Queue<Command> qc) {
            while(qc.peek() != null)
                qc.poll().operation();
        }
        public static void main(String[] args) {
            Build b = new Build();
            commandEater(b.makeQ());
        }
    }
    ===================================
    0 1 2 3 4 5 6 7 8 9 
    

    11.11.1 PriorityQueue

    队列规则。在给定一组队列中的元素的情况下,确定下一个弹出队列的元素规则。

    先进先出的规则是下一个元素是等待时间最长的元素

    优先级队列声明下一个弹出元素具有最高的优先级

    调用offer()方法插入一个对象,该对象会被排序(默认自然排序,但是你可以通过自己的Compartor来修改这个顺序)Priority保证你调用peak()poll()和remove()方法时,获取元素是从优先级最高的元素。

    • 允许重复
    • 最小值拥有最高的优先级(String,空格也可以算作值,并且比字母的优先级高)
    • 可以通过自己的Comparator对象来改变顺序
    • 可以和Integer String Character 一起工作,内建了自然排序

    练习28

    /* Fill a PriorityQueue (using offer()) with Double values created using
     * java.util.Random, then remove the elements using poll() and display them.
     */
    import java.util.*;
    
    public class No28Ex {
        public static void main(String[] args) {
            Random rand = new Random();
            PriorityQueue<Double> d = new PriorityQueue<Double>();
            for(int i = 0; i < 5; i++)
                d.offer(rand.nextDouble() * i);
            while(d.peek() != null)
                System.out.print(d.poll() + " ");
        }
    }
    ========================================================================
    0.0 0.05564526212163001 0.22883970018625355 0.4734305790195257 1.1013640026675637 
    

    练习29

    /* Fill a PriorityQueue (using offer()) with Double values created using
     * java.util.Random, then remove the elements using poll() and display them.
     */
    import java.util.*;
    
    class Simple extends Object {}
    
    public class No29Ex {
        public static void main(String[] args) {
            PriorityQueue<Simple> s = new PriorityQueue<Simple>();
            // OK to add one Simple:
            s.offer(new Simple());
            // but no more allowed; get runtime exception:
            // Simple cannot be cast to Comparable:
           //  s.offer(new Simple());
        }
    }
    

    11.12 Collection 和 Iterator

    ​ Collection是描述所有序列容器的共性的根接口,为了表示其他接口的共性而出现的接口。

    如果你实现Collection你就必须实现iterator(),只实现Iterator()与继承AbstractCollection相比,继承更简单。但是你的类如果继承了其他类,那么你就不能继承AbstractCollection了,这种情况要实现Collection就必须实现该接口的所有方法。此外,继承并提供常见迭代器的能力就显得容易很多。

    练习30

    // holding/CollectionSequence30.java
    
    import typeinfo.pets.*;
    
    import java.util.Collection;
    import java.util.Iterator;
    
    public class No30CollectionSequence implements Collection<Pet> {
        private Pet[] pets = Pets.createArray(8);
        public int size() { return pets.length; }
        public Iterator<Pet> iterator() {
            return new Iterator<Pet>() {
                private int index = 0;
                public boolean hasNext() {
                    return index < pets.length;
                }
                public Pet next() { return pets[index++]; }
                public void remove() { // not implemented
                    throw new UnsupportedOperationException();
                }
            };
        }
        public void clear() {
            if(this.size() != 0)
                for(Pet p : pets)
                    p = null;
        }
        public boolean retainAll(Collection<?> c) {
            throw new UnsupportedOperationException();
        }
        public boolean removeAll(Collection<?> c) {
            throw new UnsupportedOperationException();
        }
        public boolean addAll(Collection<? extends Pet> c) {
            throw new UnsupportedOperationException();
        }
        public boolean contains(Object o) {
            throw new UnsupportedOperationException();
        }
        public boolean isEmpty() {
            return (this.size() == 0) ? true : false;
        }
        public boolean containsAll(Collection<?> c) {
            throw new UnsupportedOperationException();
        }
        public boolean remove(Object o) {
            throw new UnsupportedOperationException();
        }
        public boolean add(Pet p) {
            throw new UnsupportedOperationException();
        }
        public Object[] toArray() {
            return pets;
        }
        public <T> T[] toArray(T[] a) {
            throw new UnsupportedOperationException();
        }
        public static void main(String[] args) {
            CollectionSequence30 c = new CollectionSequence30();
            InterfaceVsIterator.display(c);
            InterfaceVsIterator.display(c.iterator());
        }
    }
    

    11.13 Foreach与迭代器

    Foreach 用于数组应用于任何Collection对象。

    因为Java SE5 引进了Iterable接口,该接口能够产生Iterator的iterator()方法,并且Iterable接口被foreach用来再序列中移动。

    练习31

    /* Modify polymorphism/shape/RandomShapeGenerator.java to make it
    * Iterable. You'll need to add a constructor that takes the number of
    * elements that you want the iterator to produce before stopping. Verify
    * that it works.
    */
    /* Solution includes, in same package: 
    * public class Shape {
    *	public void draw() {}
    *	public void erase() {}
    *	public void amend() { System.out.println("Shape.amend()"); }
    *	@Override public String toString() { return "Shape"; }
    * }
    * public class Circle extends Shape {
    *	@Override public void draw() { print("Circle.draw()"); }
    *	@Override public void erase() { print("Circle.erase()"); }
    *	@Override public void amend() { print("Circle.amend()"); }
    *	@Override public String toString() { return "Circle"; }
    * }
    * public class Square extends Shape {
    *	@Override public void draw() { print("Square.draw()"); }
    *	@Override public void erase() { print("Square.erase()"); }
    *	@Override public void amend() { print("Square.amend()"); }
    *	@Override public String toString() { return "Square"; }
    * }
    * public class Triangle extends Shape {
    *	@Override public void draw() { print("Triangle.draw()"); }
    *	@Override public void erase() { print("Triangle.erase()"); }
    *	@Override public void amend() { print("Triangle.amend()"); }
    *	@Override public String toString() { return "Triangle"; }
    * }
    */
    package holding.shape;
    import java.util.*;
    
    public class RandomShapeGenerator31 implements Iterable<Shape> {
    	private Random rand = new Random();
    	public Shape make() {		
    		switch(rand.nextInt(3)) {
    			default:
    			case 0: return new Circle();
    			case 1: return new Square();
    			case 2: return new Triangle();
    		}
    	}
    	private Shape[] shapes;
    	RandomShapeGenerator31(int n) {
    		shapes = new Shape[n];
    		for(int i = 0; i < n; i++)
    			shapes[i] = make();
    				 
    	}
    	public Iterator<Shape> iterator() {
    		return new Iterator<Shape>() {
    			private int index = 0;
    			public boolean hasNext() {
    				return index < shapes.length;
    			}
    			public Shape next() {
    				return shapes[index++];
    			}
    			public void remove() {
    				throw new UnsupportedOperationException();
    			}			
    		};
    	}
    	public static void main(String[] args) {
    		RandomShapeGenerator31 rsg = new RandomShapeGenerator31(20);
    		for(Shape s : rsg)
    			System.out.println(s);
    	}
    }
    

    11.13.1 适配器方法惯用法

    适配器:

    当你有一个接口并需要另一个接口的时候,编写适配器就可以解决问题

    11.14 总结

    Java 提供了大量持有对象的方式

    • 数组将数字和对象联系起来,其容量不能改变,多维保存类型明确的对象,查询不需要对结果进行类型转换
    • Collection 保存单一元素,Map保存相关联的键值对。有了Java的泛型,你就可以指定容器中存放的对象类型,因此就不会将错误对象放置到容器中,并且从容器中获取元素时,不必进行类型转换。各种你就不会将错误类型的对象放置到容器中,并且再从容器中获取元素时,自动调整其尺寸。容器不能持有基本类型,但是自动包装机制会仔细地执行基本类型到容器中所持有地包装器类型之间地双向转换。
    • 像数组一样 ,List也建立数字索引与对象的关联,因此数字和list也是排好序的容器。list能自动扩充容量
    • 如果要进行大量随机访问,就是要ArrayList,如果要经常从表中间插入或删除元素应使用Linkedlist
    • 各种Queue以及栈行为,由LinkedList提供支持
    • HashMap 快速访问 TreeMap 保持“键“始终处于排序状态,所以没有HashMap快,LinkedHashMap保持元素插入的顺序,也是通过散列提供快速访问
    • Set不接受重复元素,HashSet保持最快的查询速度,TreeSet保持元素处于排序状态,LinkedHashMap以插入顺序保存元素

  • 相关阅读:
    [leetcode]Combination SumII
    NSum小结
    image 与 canvas 的相互转化
    $_SERVER 相关变量
    最近做的项目,总结一下。
    开始在博客园,写博客,先写点东西。
    Codeforces Round #584 C. Paint the Digits
    Codeforces Round #584 B. Koala and Lights
    Codeforces Round #584 A. Paint the Numbers
    HDU-2296-Ring(AC自动机, DP)
  • 原文地址:https://www.cnblogs.com/AronJudge/p/14405942.html
Copyright © 2020-2023  润新知