Thinking in java 第11章 持有对象 笔记+习题

Thinking in java 第11章 持有对象

学习目录


11.1 泛型和类型安全的容器

1. 当你制定了某个类型作为泛型参数时,你并不仅限于只能将该确切类型的对象放置到容器中。向上转型也可一样作用于泛型。

 

11.2 基本概念

1. Java容器被划分为两个概念:

  • Collection:一个独立元素的序列,这些元素都服从一条或多条规则。
  • Map:一组成对的“键值对”对象,允许你使用键来查找值。

 

11.3 添加一元素

1. Arrays.asList() 方法接收一个数组或是一个用逗号分隔的元素列表,并将其转换为一个List对象。

2. Collections.addAll() 方法接收一个Collection对象,以及一个数组或是一个用逗号分隔的列表。

Collections<Integer> collection = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5));
Integer[] moreInts = {6,7,8,9,10};
collection.addAll(Array.asList(moreInts));

Collections.addAll(collection, 11, 12, 13, 14, 15);
Collections.addAll(collection, moreInts);

List<Integer> list = Arrays.asList(16,17,18,19,20);
list.set(1,99);
//! list.add(21); underlying array cannot be resized

3. 方法2只能接受Collection,因此没有方法1灵活;而方法1返回的底层是数组,不能改变大小(即不能add或delete)

 

11.4 容器的打印

1. Set类型:

  • HashSet使用相当复杂的方式来存储元素,这种技术是最快的获取元素方式,因此顺序无实际意义。
  • TreeSet按照比较结果的升序保存对象。
  • LinkedHashSet按照被添加的顺序保存对象。

2. Map类型(Map.put(key, value); Map.get(key))

  • HashMap提供了最快的查找技术,没有按照任何明显顺序来保存。
  • TreeMap按照比较结果的升序保存键。
  • LinkedHashMap按照插入顺序保存键,同时还留下了HashMap的查询速度。

 

11.5 List

1. 分为ArrayListLinkedList,区别即顺序表和链表的区别。

2. 用contains()方法判断某个对象是否在列表中。

3. 用remove()方法移除某个对象。

4. 用indexOf()方法查询索引编号。

5. 比较要用到equals()方法,最好都重写。

6. 用subList()方法获取子集。

7. containsAll()、Collections.shuffle()、Collections.sort()、retainAll() 交集、removeAll()、set()、isEmpty()、clear()、toArray()

 

11.6 迭代器

1. 迭代器通常被称为轻量级对象:创建它的代价小。因此迭代器有很多奇怪的限制,例如Java的Iterator只能单向移动,只能用来:

  • 使用iterator()要求容器返回一个Iterator。Iterator将准备好返回序列的第一个元素。
  • 使用next()获得下一个元素。
  • 使用hasNext()检查是否还有元素。
  • 使用remove()将迭代器将最新的元素删除,所以必须先调用next()再用remove()。

2. 用Iterator<T> it 就能不管T的容器类型。

3. ListIteratorIterator的子类,可以双向移动,还可以返回指向当前元素的前一个和后一个元素的索引,且可使用set()替换访问过的最后一个元素。 方法为:hasNext()、hasPrevious()、next()、previous()、nextIndex()、previousIndex()、set()

 

11.7 LinkedList

1. LinkedList添加了可以使其用作栈、队列或双端队列的方法,这些方法中有些彼此之间只是名字差异或只有轻微差异。如:

  • getFirst()和element()完全一样,返回表头元素,若空抛出异常,而peek()方法在空时返回null;
  • removeFirst()和remove()完全一样,一处表头,若空抛出异常,而poll()方法在空时返回null;
  • addFirst();add()和addLast()相同;removeLast();

 

11.8 Stack

1. push(T v)、peek()、pop()、empty()、toString()

 

11.9 Set

1. Set具有与Collection完全一样的接口,因此没有任何额外功能,不像前面有两个不同的List。

2. HashSet使用的是散列数组;TreeSet使用的是红黑树;LinkedHashSet也使用了散列,并用链表维护插入顺序。

3. 在TreeSet中如果想不分大小写进行从小到大排序,则用 Set<String> words = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);

4. 方法为 add() 、 remove() 等。

 

11.10 Map

1. 添加时如下:

Integer freq = m.get(r);
m.put(r, freq == null? 1 : freq+1);

2. 可扩展到多维,即value可以使其他容器。

3. 遍历时如下:

for(T t : map.keySet())
    print(t + ": " + map.get(t));

 

11.11 Queue

1. 可以将LinkedList向上转型为Queue,因为其实现了Queue接口。故可写成 Queue<Integer> q = new LinkedList<Integer>();

2. 方法:offer() 插入队尾、peek()/element()、poll()/remove() 等。

3. PriorityQueue<T>,Collections.reverseOrder()产生反序的Comparator。

4. 如果你想在优先队列中使用自己的类,就必须包括额外的功能以产生自然排序,或者必须提供自己的Comparator。

 

11.12 Collection和Iterator

1. 在基于两个接口都能实现时(例如遍历输出),用Collection方便一些,因为它是Iterable的,用Foreach语句更加清晰。

2. 当要实现一个不是Colletion的外部类时,用Iterator更好。因为实现Collection必须要实现iterator(),而且还必须要实现其他一些方法。

 

11.13 Foreach迭代器

1. 如果你创建了任何实现Iterable的类,都可以将它用于Foreach语句中。

class A implements Iterable<T> {
    protected T[] words = ...;
    public Iterable<T> iterator() {
        return new Iterable<T>() {
            private int index = 0;
            public boolean hasNext() { return index < words.length; }
            public T next() { return words[index++]; }
            public void remove() {...}
        }
    }
}

2. Map的Foreach遍历如下:

for(Map.Entry entry : m.entrySet()) {
    print(entry.getKey(), entry.getValue());
}

3. 可以通过适配器方法,在类中添加用于不同作用的迭代方法。

 


习题

练习1:创建一个新类Gerbil(沙鼠),包含int gerbilNumber,在构造器中初始化它.添加一个方法hop(),用以打印沙鼠的号码以及它正在跳跃的信息。创建一个ArrayList,并向其中添加一串Gerbil对象。使用get()遍历List,并且对每个Gerbil调用hop()。

package Chapter11;

import java.util.ArrayList;
import java.util.Arrays;

public class E1 {
    public static void main(String[] args) {
        ArrayList<E1Gerbil> list = new ArrayList<E1Gerbil>(Arrays.asList(new E1Gerbil(1), new E1Gerbil(2),
        new E1Gerbil(3), new E1Gerbil(4)));
        for(E1Gerbil e : list) {
            e.hop();
        }
    }
}

class E1Gerbil {
    private int gerbilNumber;

    public E1Gerbil(int gerbilNumber) {
        this.gerbilNumber = gerbilNumber;
    }

    public int getGerbilNumber() {
        return gerbilNumber;
    }

    public void setGerbilNumber(int gerbilNumber) {
        this.gerbilNumber = gerbilNumber;
    }

    public void hop() {
        System.out.println("" + gerbilNumber + " is jumping");
    }
}

/*
1 is jumping
2 is jumping
3 is jumping
4 is jumping
*/

练习2:修改SimpleCollection.java,使用Set来表示c。

略。

练习3:修改innerclasses/Sequence.java,使你可以向其中添加任意数量的元素。

略。把Object[] 改为ArrayList<Object> ,并用相应的方法替换即可。(P192)

练习4:创建一个生成器类,它可以在每次调用其next()方法时,产生你最喜欢的电影的名字。在电影名列表的名字用完之后,循环到该列表的开始处。使用这个生成器来填充数组、ArrayList、LinkedList、HashSet、LinkedHashSet和TreeSet,然后打印每个容器。

package Chapter11;

import java.util.*;

public class E4 {
    public static void main(String[] args) {
        System.out.println("ArrayList " + new E4Generator().fill(new ArrayList<String>()));
        System.out.println("LinkedList " + new E4Generator().fill(new LinkedList<String>()));
        System.out.println("HashSet " + new E4Generator().fill(new HashSet<String>()));
        System.out.println("LinkedHashSet " + new E4Generator().fill(new LinkedHashSet<String>()));
        System.out.println("TreeSet " + new E4Generator().fill(new TreeSet<String>()));
    }
}

class E4Generator {
    private String[] movies = {"ZZ", "BB", "CC", "AA", "FF", "EE"};
    private int maxLength = 6;
    private int i = 0;

    public String next() { return movies[(i++)%maxLength]; }

    public Collection<String> fill(Collection<String> c) {
        for(int j = 0; j < 8; j++) {
            c.add(next());
        }
        return c;
    }
}

/*
ArrayList [ZZ, BB, CC, AA, FF, EE, ZZ, BB]
LinkedList [ZZ, BB, CC, AA, FF, EE, ZZ, BB]
HashSet [ZZ, BB, CC, AA, FF, EE]
LinkedHashSet [ZZ, BB, CC, AA, FF, EE]
TreeSet [AA, BB, CC, EE, FF, ZZ]
*/

练习5-练习6:修改LIstFeatures.java,让它用Integer/String而不是Pet,并解释在结果上有何不同。

略。

练习7:创建一个类,然后创建一个用你的类的对象进行过初始化的数组。通过使用subList()方法,创建你的List子集,然后在你的List中移除这个子集。

package Chapter11;

import java.util.*;

public class E7 {
    public static void main(String[] args) {
        E7A[] es = new E7A[] {new E7A(1), new E7A(2), new E7A(3), new E7A(4)};
        List<E7A> list = new ArrayList<E7A>(Arrays.asList(es));
        List<E7A> list2 =  list.subList(1, 2);
        System.out.println(list2);
        list.removeAll(list2);
        System.out.println(list);
    }
}

class E7A {
    int a;

    public E7A(int a) {
        this.a = a;
    }

    @Override
    public String toString() {
        return Integer.toString(a);
    }
    
}

/*
[2]
[1, 3, 4]
*/

练习8:修改练习题1,以便调用hop()时使用Iterator遍历List。

package Chapter11;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;

public class E1 {
    public static void main(String[] args) {
        ArrayList<E1Gerbil> list = new ArrayList<E1Gerbil>(Arrays.asList(new E1Gerbil(1), new E1Gerbil(2),
        new E1Gerbil(3), new E1Gerbil(4)));
        Iterator<E1Gerbil> iter = list.iterator();
        while(iter.hasNext()) {
            iter.next().hop();
        }
    }
}

class E1Gerbil {
    private int gerbilNumber;

    public E1Gerbil(int gerbilNumber) {
        this.gerbilNumber = gerbilNumber;
    }

    public int getGerbilNumber() {
        return gerbilNumber;
    }

    public void setGerbilNumber(int gerbilNumber) {
        this.gerbilNumber = gerbilNumber;
    }

    public void hop() {
        System.out.println("" + gerbilNumber + " is jumping");
    }
}

/*
1 is jumping
2 is jumping
3 is jumping
4 is jumping
*/

练习9:修改innerclasses/Sequence.java,使得在Sequence中,用Iterator取代Selector。

略。用个方法return collection.iterator()即可。

练习10:修改第8章中的练习9,使其使用一个ArrayList来存放Rodents,并使用一个Iterator来访问Rodent序列。

略。

练习11:写一个方法,使用Iterator遍历Collection,并打印容器中每个对象的toString()。填充各种类型的Collection,然后对其使用此方法。

同P227代码。略。

练习12:创建并组装一个List<Integer>,然后创建第二个具有相同尺寸的List<Integer>,并使用ListIterator读取第一个List中的元素,然后再将它们以反序插入到第二个列表中。

package Chapter11;

import java.util.*;

public class E12 {
    public static void main(String[] args) {
        List<Integer> list1 = new ArrayList<>(Arrays.asList(1,2,3,4,5,6));
        List<Integer> list2 = new ArrayList<>();
        ListIterator<Integer> iter = list1.listIterator();
        while(iter.hasNext()) iter.next();
        while(iter.hasPrevious()) list2.add(iter.previous());
        System.out.println(list2);
    }
}

/*
[6, 5, 4, 3, 2, 1]
*/

直接写 ListIterator<Integer> iter = list1.listIterator(list1.size()); 更好。

练习13:在innerclasses/GreenhouseController.java示例中,Controller类使用的是ArrayList,修改代码,用LinkedList替换之,并使用Iterator来循环遍历事件集。

略。简单应用。

练习14:创建一个空的LinkedList<Integer>,通过使用ListIterator,将若干个Integer插入这个List中,插入时,总是将它们插入到List的中间。

package Chapter11;

import java.util.*;

public class E14 {
    public static void main(String[] args) {
        List<Integer> list = new LinkedList<Integer>();
        for(int i = 0; i < 10; i++) {
            ListIterator iter = list.listIterator(list.size()/2);
            iter.add(i);
        }
        System.out.println(list);
    }
}

/*
[1, 3, 5, 7, 9, 8, 6, 4, 2, 0]
*/

练习15:栈在编程语言中经常用来对表达式求值。请使用net.mindview.util.Stack对下面表达式求值,其中“+”表示“将后面的字母压进栈”,而“-”表示“弹出栈顶字母并打印它”:“+U+n+c---+e+r+t---+a-+i-+n+t+y---+ -+r+u--+l+e+s---”。

package Chapter11;

import java.util.Stack;

public class E15 {
    public static void main(String[] args) {
        String s = "+U+n+c---+e+r+t---+a-+i-+n+t+y---+ -+r+u--+l+e+s---";
        Stack<Character> stack = new Stack<>();
        for(int i = 0; i < s.length(); i++) {
            if(s.charAt(i) == '+') stack.push(s.charAt(++i));
            else System.out.print(stack.pop());
        }
        System.out.println();
    }
}

/*
cnUtreaiytn ursel
*/

练习16:创建一个元音字母Set。对UniqueWords.java操作,计数并显示在每一个输入单词中的元音字母数量,并显示输入文件中的所有元音字母的数量总和。

没有文件,简单应用。略。

练习17:使用练习1中的Gerbil类,将其放入Map中,将每个Gerbil的名字String(键)与每个Gerbil(值)关联起来。为keySet()获取Iteratror,使用它遍历Map,针对每个“键”查询Gerbil,然后打印出“键”,并让gerbil执行hop()。

package Chapter11;

import java.util.*;
import Chapter11.E1Gerbil;

public class E17 {
    public static void main(String[] args) {
        ArrayList<E1Gerbil> list = new ArrayList<E1Gerbil>(Arrays.asList(new E1Gerbil(1), new E1Gerbil(2),
                new E1Gerbil(3), new E1Gerbil(4)));
        String name = "GG";
        Map<String, E1Gerbil> m = new HashMap<>();
        for(int i = 1; i < 5; i++) m.put(name+i, list.get(i-1));
        for(String s : m.keySet()) m.get(s).hop();
    }
}

/*
1 is jumping
3 is jumping
2 is jumping
4 is jumping
*/

练习18:用键值对填充一个HashMap。打印结果,通过散列码来展示其排序。抽取这些键值对,按照键进行排序,并将结果置于一个LinkedHashMap中。展示其所维护的插入排序。

package Chapter11;

import java.util.*;

public class E18 {
    public static void main(String[] args) {
        Map<String, Integer> m = new HashMap<>();
        m.put("asd",1);
        m.put("zsdf",2);
        m.put("bgfd",3);
        System.out.println(m);
        List<String> strings = new LinkedList<>(m.keySet());
        Collections.sort(strings);
        Map<String, Integer> m2 = new LinkedHashMap<>();
        for(String s : strings) {
            m2.put(s, m.get(s));
        }
        System.out.println(m2);
    }
}


/*
{zsdf=2, asd=1, bgfd=3}
{asd=1, bgfd=3, zsdf=2}
*/

练习19:使用HashSet和LinkedHashSet重复前一个练习。

这也能重复?略。

练习20:修改练习16,使得你可以跟踪每一个元音字母出现的次数。

略。

练习21:通过使用Map<String,Integer>,遵循UniqueWords.java的形式来创建一个程序,它可以对一个文件中出现的单词计数。使用带有第二个参数**String.CASE_INSENSITIVE_OREDER的Collection.sort()方法对结果进行排序(将产生字母序),然后显示结果。

package Chapter11;

import java.util.*;

public class E21 {
    public static void main(String[] args) {
        String[] s = "Can you can can a can?".split(" ");
        Map<String, Integer> map = new HashMap<>();
        for(String i : s) {
            Integer temp = map.get(i);
            map.put(i, (temp==null?1:temp+1));
        }
        List<String> list = new ArrayList<>(map.keySet());
        Collections.sort(list, String.CASE_INSENSITIVE_ORDER);
        for(String i : list) {
            System.out.println(i + " : " + map.get(i));
        }
    }
}

/*
a : 1
Can : 1
can : 2
can? : 1
you : 1
*/

练习22:修改前一个练习,使其用一个包含有一个String域和一个计数域的类来存储每一个不同的单词,并使用一个由这些对象构成的Set来维护单词列表。

package Chapter11;

import java.util.Objects;
import java.util.*;

public class E22 {
    public static void main(String[] args) {
        String[] s = "Can you can can a can?".split(" ");
        Set<E22A> set = new HashSet<>();
        for(String i : s) {
            E22A e = new E22A(i);
            if(set.contains(e)) {
                Iterator<E22A> iter = set.iterator();
                while(iter.hasNext()) {
                    E22A tempe = iter.next();
                    if(tempe.equals(e)) tempe.add();
                }
            }
            else {
                e.setA(1);
                set.add(e);
            }
        }
        for(E22A e : set) {
            System.out.println(e.getS() + " : " + e.getA());
        }
    }
}

class E22A {
    private String s;
    private int a;

    public void add() { a++; }

    public E22A(String s) {
        this.s = s;
    }

    public String getS() {
        return s;
    }

    public void setS(String s) {
        this.s = s;
    }

    public int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        E22A e22A = (E22A) o;
        return Objects.equals(s, e22A.s);
    }

    @Override
    public int hashCode() {
        return Objects.hash(s);
    }
}

/*
a : 1
can? : 1
Can : 1
can : 2
you : 1
*/

要重写equals再调用contains()。

练习23:从Statistics.java开始,写一个程序,让它重复做测试,观察是否某个数字比别的数字出现的次数多。

package Chapter11;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class E23 {
    public static void main(String[] args) {
        Random r = new Random();
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < 10000; i++) {
            int temp = r.nextInt();
            Integer in = map.get(temp);
            map.put(temp, (in == null ? 1 : in+1));
        }
        for(Integer i : map.keySet()) System.out.println(""+i+" : "+map.get(i));
    }
}

....

都只出现一次的样子。

练习24:使用String“键”和你选择的对象填充LinkedHashMap。然后从中提取键值对,以键排序,然后重新插入此Map。

略。和前面差不多。

练习25:创建一个Map<String,ArrayList<Integer>>,使用net.mindview.TextFile来打开一个文本文件,并一次读入一个单词。在读入单词时对它们进行计数,并且对于文件中的每一个单词,都在ArrayList<Integer>中记录下与这个词相关联的单词计数。实际上,它记录的是该单词在文件中被发现的位置。

略。计数就是ArraList.size()

练习26:拿到前一个练习中所产生的Map,并按照它们在最初的文件中出现的顺序重新创建单词顺序。

略。最后再遍历一遍用LinkedHashSet存储。

练习27:写一个称为Command的类,它包含一个String域和一个显示该String的operation()方法。写第二类,它具有一个使用Command对象来填充一个Queue并返回这个对象的方法。将填充后的Queue传递给第三个类的一个方法,该方法消耗掉Queue中的对象,并调用它们的operation()方法。

package Chapter11;

import java.util.*;

public class E27 {
    public static void main(String[] args) {
        E27C.func2();
    }
}

class E27A {
    private String s;

    public void operation() {
        System.out.println("this is " + s);
    }

    public E27A(String s) {
        this.s = s;
    }
}

class E27B {
    public static Queue<E27A> func1() {
        return new LinkedList<>(Arrays.asList(new E27A("aaa"), new E27A("bbb"), new E27A("ccc")));
    }
}

class E27C {
    public static void func2() {
        Queue<E27A> q = E27B.func1();
        while(!q.isEmpty()) {
            q.poll().operation();
        }
    }
}

/*
this is aaa
this is bbb
this is ccc
*/

练习28:用由java.util.Random创建的Double值填充一个PriorityQueue(用offer())方法,然后使用poll()移除并显示它们。

略。同上。

练习29:创建一个继承自Object的简单类,它不包含任何成员,展示你不能将这个类的多个示例成功地添加到一个PriorityQueue中。这个问题将在第17章中详细解释。

问号。看完17章再来看看能不能答。

练习30:修改CollectionSequeuece.java,使其不要继承AbstractCollection,而是实现Collection。

略。直接实现Collection<Pet>接口要同时实现里面的多余的方法。

练习31:修改polymorphism/shape/RandomShapeGenerator.java,使其成为一个Iterable。你需要添加一个接受元素数量为参数的构造器,这个数量是指在停止之前,你想用迭代器生成的元素的数量。验证这个程序可以工作。

略。

练习32:按照MultiIterableClass示例,在NonCollectionSequence.java中添加reversed()和randomized()方法,并让NonCollectionSequence实现Iterable。然后在foreach语句中展示所有的使用方式。

略。同P244。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章