JAVA编程思想——读书笔记 对象的容纳

对象的容纳

  1. 数组
  2. 集合类

1.数组(array)

  • 有两方面的问题将数组与其他集合类型区分开:效率和类型
  • 为保存和访问一系列对象(实际是对象的句柄)数组,最有效的方法是数组。
  • 数组实际代表一个简单的线性序列。它使得元素的访问速度非常快,但也要为它的这种速度付出代价:创建一个数组对象时,它的大小是固定的,而且不可在那个数组的“存在时间”内发生改变。
  • java中无论是数组还是集合,都会进行范围检查——若超出边界,就会抛出RuntimeException,所以对性能会有一定的影响。(C++中不不进行范围检查)

数组和第一类对象

无论使用的数组属于什么类型,数组标识符实际都是指向真实对象的一个句柄。那些对象本身是在内存
“堆”里创建的。堆对象既可“隐式”创建(即默认产生),亦可“显式”创建(即明确指定,用一个 new
表达式)。

Array.length 只告诉我们可将多少元素置入那个数
组。换言之,我们只知道数组对象的大小或容量,不知其实际容纳了多少个元素

基本数据类型集合
  • 集合类只能容纳对象句柄,但可以同过Integer、Double等封装类把基本数据类型置入集合
  • 数组即可容纳对象句柄又可以容纳基本数据类型

数组的返回

  • 在c/c++中,不能返回数组,只能返回一个指向数组的一个指针,因为难以控制数组的“存在时间”,它很容易造成内存“漏洞”的出现,
  • 在java中,可以返回数组,当然,此时实际返回的依然是指向数组的指针,但是不需要担心那个数组是否可用——只要需要,它就会自动存在。而且垃圾回收器会在我完成后自动将其清除
/**
 *Returning arrays from methods
 */
public class IceCream {
    static String[] flav = {
            "Chocolate","Strawberry",
            "Vanilla Fudge Swirl","Mint Chip",
            "Mocha Almond Fudge","Run Raisin",
            "Praline Cream","Mud Pie"
    };
    static String[] flavorSet(int n){ //8
        //Force it to be positive & within bounds:
        n=Math.abs(n)%(flav.length+1);
        String [] results=new String[n];
        int[] picks =new int[n]; //8
        for(int i =0; i<picks.length;i++){
            picks[i]=-1;
        }

        for (int i=0;i<picks.length;i++){
            retry:
            while(true){
                int t=(int)(Math.random()*flav.length); //0-8
                for (int j=0;j<i;j++) {
                    if(picks[j]==t) {
                        continue  retry;
                    }
                }
                    picks[i]=t;
                    results[i]=flav[t];
                    break;
            }
        }
        return results;
    }

    public static void main(String[] args) {

        for (int i=0;i<20;i++){
            System.out.println(
                    "flavorSet("+i+")="
            );
            String[] fl=flavorSet(flav.length);
            for (int j=0; j<fl.length;j++){
                System.out.println("\t"+fl[j]);
            }
        }
    }
}

2.集合(collection)

为容纳一组对象,最适宜的选择是数组。假如容纳的是一系列基本数据类型,则更应该使用数组。
**通常我们并不能知道最终需要多少个对象,有时甚至会用到更复杂的方试来保存对象。**为此java提供了以下四种“集合类”:

  1. Vector(矢量,向量)
  2. BitSet(位集,BitMap位图)
  3. Stack(堆栈)
  4. Hashtable(散列表)

集合的缺点:类型未知

它不适用于一下场合:
  1. 将一个对象句柄置入一个集合时,其类型信息会被抛弃,所以任何类型的对象都会进入我们的集合,比如:虽然指示它只能容纳猫,但实际上谁都能仍一直狗进来。
  2. 由于类型信息丢失,所以集合唯一能肯定的事情就是:将自己的容纳指向一个对象句柄,在使用它之前,必须对其进行造型,使其具有正确的类型。

值得欣慰的是,java不允许滥用置入集合的对象。假如一个狗进入猫的集合,那么仍会将集合内的所有东西都看成猫,所以再使用那条狗时会得到一个“违例”错误。同样,将一个狗的句柄,“造型”到一只猫,那么在运行期间也会得到一个“违例”错误
例子如下:

import javax.swing.plaf.synth.ColorType;
import java.util.Vector;
/**
 * @author: ly
 * @date: 2021/8/11 12:12
 */
class Cat {
    private final int catNumber;

    Cat(int i) {
        catNumber = i;
    }

    void print() {
        System.out.println("Cat #" + catNumber);
    }
}
class Dog {
    private int dogNumber;

    Dog(int i) {
        dogNumber = i;
    }

    void print() {
        System.out.println("Dog #" + dogNumber);
    }
}
public class CatsAndDogs {
    public static void main(String[] args) {
        Vector cats=new Vector();
        for (int i=0; i<7;i++){
            cats.addElement(new Cat(i));
        }
       //Not a problem to add a dog to cats 向猫集合中加一个狗是没有问题的
        cats.addElement(new Dog(7));
        for (int i=0; i<cats.size();i++){
            ((Cat)cats.elementAt(i)).print();
            //Dog is detected only at run-time  但是它会在运行期间发生违例
        }
        Vector dogs=new Vector();
        dogs.addElement(new Dog(10));
        //由于Vector会丢失类型所以,当他需要指向一个Dog句柄时需要造型回Dog
        Dog dog= (Dog) dogs.elementAt(0);
        dog.print();
    }
}///:~
1.错误有时显露不出来

在某些情况下,不造行回原来的类型,程序似乎能正确的工作。这种情况是相当特殊的:String 类从编
译器获得了额外的帮助,使其能够正常工作。只要编译器期待的是一个String 对象,但它没有得到一个,就
会自动调用在Object 里定义、并且能够由任何Java 类覆盖的 toString()方法。这个方法能生成满足要求的
String 对象,然后在我们需要的时候使用。
因此,为了让自己类的对象能显示出来,要做的全部事情就是覆盖toString()方法,如下例所示:

import java.util.Vector;

/**
 * WorksAnyway.java
 * In special cases,things just seem to work correctly
 * @author: ly
 * @date: 2021/8/11 15:22
 */
class Mouse{
    private int mouseNumber;
    Mouse(int i){
       mouseNumber=i;
    }

    /**
     * Magic method
     */
    @Override
    public String toString(){
        return "This is Mouse #"+ mouseNumber;
    }
    void print(String msg){
        if(msg!=null) {
            System.out.println(msg);
        }
        System.out.println("Mouse number "+mouseNumber);
    }
}

class MouseTrap{
    static void caughtYa(Object m){
        //Cast from Object
        Mouse mouse=(Mouse)m;
        mouse.print("Caught one!");
    }
}

public class WorksAnyway {
    public static void main(String[] args) {
        Vector mice=new Vector();
        for (int i = 0; i < 3; i++) {
            mice.addElement(new Mouse(i));
        }

        for (int i = 0; i < mice.size(); i++) {
            //No cast necessary,automatic call to Object.toString():
            System.out.println("Free mouse: " + mice.elementAt(i) );
            MouseTrap.caughtYa(mice.elementAt(i));
        }
    }
}
生成能自动判断类型的Vector
import java.util.Vector;

/**
 * GopherVector.java
 * @author: ly
 * @date: 2021/8/11 16:14
 */
class Gopher{
    private int gopherNumber;
    Gopher(int i){
        gopherNumber=i;
    }
    void print(String msg){
        if(msg!=null){
            System.out.println(msg);
        }
        System.out.println("Gopher number"+gopherNumber);
    }
}

class GopherTrap{
    static void caughtYa(Gopher g){
        g.print("Caught one!");
    }
}

public class GopherVertor {
    private Vector v=new Vector();
    /**
     * 创建一个addElement方法,在其中添加Gopher对象,防止其他对象被加入集合
     */
    public void addElement(Gopher m){
        v.addElement(m);
    }
    public Gopher elementAt(int index){
        return (Gopher) v.elementAt(index);
    }
    public int size(){
        return v.size();
    }

    public static void main(String[] args) {
        GopherVertor gophers=new GopherVertor();
        for (int i = 0; i < 3; i++) {
            gophers.addElement(new Gopher(i));
        }
        for (int i = 0; i < gophers.size(); i++) {
            GopherTrap.caughtYa(gophers.elementAt(i));
        }
    }
}

3.枚举器 (反复器 Enumeration)

Enumeration 它属于反复器一种简单的实现方式
(1) 用nextElement() 获得下一个对象。
(2) 用hasMoreElements()检查序列中是否还有更多的对象。

import java.util.Enumeration;
import java.util.Vector;

/**
 * CatAdnfDog2.java
 * Simple collection with Enumeration
 * @author: ly
 * @date: 2021/8/16 15:27
 */
class Cat2{
    private int catNumber;
    Cat2(int i){
        catNumber=i;
    }
    void print(){
        System.out.println("Cat number "+catNumber);
    }
}

class Dog2{
    private int dogNumber;
    Dog2(int i){
        dogNumber=i;
    }
    void print(){
        System.out.println("Dog number "+ dogNumber);
    }
}
public class CatAdnfDog2 {
    public static void main(String[] args) {
        Vector cats=new Vector();
        for (int i =0;i<7;i++){
            cats.addElement(new Cat2(i));
        }
        //Not a problem to add a dog to cats:
        cats.addElement(new Dog2(7));
        Enumeration e=cats.elements();
        while(e.hasMoreElements()){
            ((Cat2)e.nextElement()).print();
            //Dog is detected only at run-time
        }
    }
}
import java.util.Enumeration;
import java.util.Vector;

/**
 * HasmsterMaze.java
 * Using an Enumeration
 * @author: ly
 * @date: 2021/8/16 16:05
 */
class Hamster{
    private int hamsterNumber;
    Hamster(int i){
        hamsterNumber=i;
    }

    @Override
    public String toString(){
        return "This is Hamster #"+ hamsterNumber;
    }
}

class Printer{
    static void printAll(Enumeration e) {
        while(e.hasMoreElements()){
            System.out.println(e.nextElement().toString());
        }
    }
}
public class HamsterMaze {
    public static void main(String[] args) {
        Vector v=new Vector();
        for (int i=0;i<3;i++){
            v.addElement(new Hamster(i));
        }
        
        Printer.printAll(v.elements());
    }
}

4.集合的类型

标准Java 1.0 和 1.1 库配套提供了非常少的一系列集合类。但对于自己的大多数编程要求,它们基本上都能
胜任。正如大家到本章末尾会看到的,Java 1.2 提供的是一套重新设计过的大型集合库。

Vector(向量)

  • addElement()差入一个对象
  • elementAt()提取一个对象
  • elements()获得对序列的一个枚举

BitSet(位集合,位图)

  • BitSet实际是由“二进制位”构成的Vector.如果希望高效率地保存大量“开-关”信息,就应使用
    BitSet。它只有从尺寸的角度看才有意义;如果希望的高效率的访问,那么它的速度会比使用一些固有类型
    的数组慢一些。
  • BitSet的最小长度为一个Long的长度(64位)

Stack(堆、“后入先出”LIFO)

Stack 有时也可以称为“后入先出”(LIFO)集合。换言之,我们在堆栈里最后“压入”的东西将是以后第
一个“弹出”的。和其他所有 Java 集合一样,我们压入和弹出的都是“对象”,所以必须对自己弹出的东西
进行“造型”。–弹夹

Stack"属于一种Vector所以Vector的方法Stack也有 除外它还以一下两种方法:"

  1. push() 将元素压入堆栈
  2. pop() 从堆栈的顶部弹出一个元素
import java.util.Stack;

/**
 * last in first out
 * Demonstration of Stack Class
 * @author: ly
 * @date: 2021/8/17 9:45
 */
public class Stacks {
    static String[] months={
        "January","February","March","April","May",
            "June","July","August","September",
            "October","November","December"
    };

    public static void main(String[] args) {
        Stack stk=new Stack();
        for (int i =0;i<months.length;i++){
            stk.push(months[i]+" ");
        }
        System.out.println("stk = "+stk);
        //Treating a stack as Vector
        stk.addElement("The last line");
        System.out.println("element 5= "+ stk.elementAt(5));
        System.out.println("popping elements:");
        while(!stk.empty()){
            System.out.println(stk.pop());
        }
        System.out.println(stk
        );
    }
}

Hashtable(哈希表,散列表)

Dictionary
//: AssocArray.java
// Simple version of a Dictionary
import java.util.*;
public class AssocArray extends Dictionary {
 private Vector keys = new Vector();
 private Vector values = new Vector();
 public int size() { return keys.size(); }
 public boolean isEmpty() {
 return keys.isEmpty();
 }
 public Object put(Object key, Object value) {
 keys.addElement(key);
 values.addElement(value);
 return key;
 }
 public Object get(Object key) {
 int index = keys.indexOf(key);
 // indexOf() Returns -1 if key not found:
 if(index == -1) return null;
 return values.elementAt(index);
 }
 public Object remove(Object key) {
 int index = keys.indexOf(key);
 if(index == -1) return null;
 keys.removeElementAt(index);
 Object returnval = values.elementAt(index);
 values.removeElementAt(index);
 return returnval;
 }
 public Enumeration keys() {
 return keys.elements();
 }
 public Enumeration elements() {
 return values.elements();
 }
 // Test it:
 public static void main(String[] args) {
 AssocArray aa = new AssocArray();
 for(char c = 'a'; c <= 'z'; c++)
 aa.put(String.valueOf(c),
 String.valueOf(c)
 .toUpperCase());
 char[] ca = { 'a', 'e', 'i', 'o', 'u' };
 for(int i = 0; i < ca.length; i++)
 System.out.println("Uppercase: " +
 aa.get(String.valueOf(ca[i])));
 }
} ///:~
Hashtable是一种快速的Dictiondry 它继承自Dictionary
类作为Key

要在Hashtable中把类做为key,必须同时覆盖Hashcode()和equals()

import java.util.Hashtable;

/**
 * SpringDetector2.java
 * If you crete a class that's used as a key in
 * a Hashtable ,you must override hashCode()
 * and equals().
 * @author: ly
 * @date: 2021/8/17 15:38
 */
class Groundhog2{
    int ghNumber;

    Groundhog2(int i) {
        ghNumber=i;
    }

    @Override
    public int hashCode(){
        return ghNumber;
    }
    @Override
    public boolean equals(Object o){
        return(o instanceof Groundhog2)&&(ghNumber==((Groundhog2)o).ghNumber);
    }
}
public class SpringDetector2 {
    public static void main(String[] args) {
        Hashtable ht=new Hashtable();
        for (int i = 0; i < 10; i++) {
            ht.put(new Groundhog2(i),new Prediction());
        }
        System.out.println("ht= "+ht+"\n");
        System.out.println(
                "Looking up prediction for groundhog #3:");
        Groundhog2 gh = new Groundhog2(3);
        if(ht.containsKey(gh))
            System.out.println((Prediction)ht.get(gh));
    }
}

在论枚举器

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

/**
 * @author: ly
 * @date: 2021/8/17 17:01
 */
class PrintData{
    static void print(Enumeration enumeration) {
        while(enumeration.hasMoreElements()){
            System.out.println(enumeration.nextElement().toString());
        }
    }
}

public class Enumerators2 {
    public static void main(String[] args) {
        Vector v=new Vector();
        for (int i = 0; i < 5; i++) {
            v.addElement(new Mouse(i));
        }

        Hashtable h=new Hashtable();
        for (int i = 0; i < 5; i++) {
            h.put(new Integer(i),new Hamster(i));
        }

        System.out.println("Vector");
        PrintData.print(v.elements());
        System.out.println("Hashtable");
        PrintData.print(h.elements());
    }
}

5. 排序

Java 1.0 和1.1 库都缺少的一样东西是算术运算,甚至没有最简单的排序运算方法。因此,我们最好创建一
个Vector,利用经典的Quicksort(快速排序)方法对其自身进行排序。
Skip

6. 新集合

  1. 集合(Collection):一组单独的元素,通常应用了某种规则。List(列表)必须按照特定的顺序容纳元素,Set(集)不可包含任何重复的元素
  2. 映射(Map):一系列“key-value”对。从表面看 ,这似乎该成为一个“key-value”集合,但是按照这种方式来实现它,就会发现实现过程相当笨拙。所以应该分离成单独的概念。另一方面,可以方便的查看Map的某个部分。只需创建一个集合,然后用它表示那一部分即可。这样一来,Map就可以返回自己Key的一个Set,Value的一个List或者一个包含“key-value”对的List。Map可以简单的扩充到多个“维”,只需在Map中包含其他的Map。
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

/**
 * SimpleCollection.java
 * A simple example using the new Collections
 * @author: ly
 * @date: 2021/8/18 10:58
 */
public class SimpleCollection {
    public static void main(String[] args) {
        Collection c=new ArrayList();
        for (int i=0;i<10;i++){
            c.add(Integer.toString(i));
        }
        //创建遍历器
        Iterator iterator=c.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
            
        }
        iterator.remove();
        System.out.println(c);
    }
}

使用Collections(集合)

下面列出了Collection能做的所有事情,由于List、Set继承自它,所以可以做同样的事情,尽管List有有些额外的功能。Map不是从Collection中继承的,所以要单独对待。

  1. boolean add(Object) *保证集合内包含了自变量。如果它没有添加自变量,就返回 false(假)
  2. boolean addAll(Collection) *添加自变量内的所有元素。如果没有添加元素,则返回 true(真)
  3. void clear() *删除集合内的所有元素
  4. boolean contains(Object) 若集合包含自变量,就返回“真”
  5. boolean containsAll(Collection) 若集合包含了自变量内的所有元素,就返回“真”
  6. boolean isEmpty() 若集合内没有元素,就返回“真”
  7. Iterator iterator() 返回一个反复器,以用它遍历集合的各元素
  8. boolean remove(Object) *如自变量在集合里,就删除那个元素的一个实例。如果已进行了删除,就返回“真”
  9. boolean removeAll(Collection) *删除自变量里的所有元素。如果已进行了任何删除,就返回“真”
  10. boolean retainAll(Collection) *只保留包含在一个自变量里的元素(一个理论的“交集”)。如果已进行了任何改变,就返回“真”
  11. int size() 返回集合内的元素数量
  12. Object[] toArray() 返回包含了集合内所有元素的一个数组
    1.*这是一个“可选的”方法,有的集合可能并未实现它。若确实如此,该方法就会遇到一个UnsupportedOperatiionException,即一个“操作不支持”违例,详见第9 章。

使用Lists

  • List(interface):顺序是List最重要的特性。List为Collection添加了大量方法,以便在List中插入和删除元素(只推荐对LinkedList使用)。List也会生成一个ListIterator,可利用它在一个List里朝两个方向遍历,同时插入和删除List中的元素(同样的,只建议给LinkedList使用)
  • ArrayList:由一个数组得到的List。作为一个常规的对象容器使用,用来替换原先的Vector。允许我们快速的访问元素,但在从列表中部插入和删除元素时,速度却稍慢。一般用ListInterator对一个ArrayList进行向前和向后遍历,不要用它删除和插入元素;
  • LinkedList:它比ArrayList效率要高,LinkeList提供优化的顺序访问性能,同时可以高效的在List中部插入和删除。但是在随机访问时,速度却相当慢,此时换用ArrayList
import java.util.*;

/**
 * Things you can do with Lists
 * @author: ly
 * @date: 2021/9/30 9:01
 */
public class List1 {
    /**
     * Wrap Collection1.fill() for convenience
     */
    public static List fill(List a){
        return (List)Collection1.fill(a);
    }

    /**
     * You can use an Iterator,just as with a Collection ,
     * but you can also use random access with get();
     */
    public static void print(List a) {
        for (int i = 0; i < a.size(); i++) {
            System.out.print(a.get(i)+" ");
        }
        System.out.println();
    }

    static boolean b;
    static Object o;
    static int i;
    static Iterator it;
    static ListIterator lit;

    public static void basicTest(List a) {
        //Add at location 1;
        a.add(1,"x");
        //Add at end
        a.add("x");
        //Add a Collection:
        a.addAll(fill(new ArrayList()));
        //Add a collection starting at location 3:
        a.addAll(3,fill(new ArrayList()));
        //Is it in there?
        b=a.contains("1");
        //Is the entire collection in there?
        b=a.containsAll(fill(new ArrayList()));
        //Lists allow random access, which is cheap
        //for ArrayList,expensive for LinkedList:
        //ArrayList随机访问性能比LinkedList高出很多

        //Get object at location 1;
        o=a.get(1);
        //Tell  index of object
        i=a.indexOf("1");
        //indexOf,starting search at location 2:
        //!i=a.indexOf("1", 2);
        //Any elements inside?
        b=a.isEmpty();
        //Ordinary Iterator
        it=a.iterator();
        //ListIterator
        lit=a.listIterator();
        //Start at loc 3
        lit = a.listIterator(3);
        //Last match
        i=a.lastIndexOf("1");
        //...after loc 2
        //!i=a.lastIndexOf("1",2);
        //Remove location 1
        a.remove(1);
        //Remove this object
        a.remove("3");
        //Set location 1 to "y"
        a.set(1,"y");
        //Keep everything that's in the argument
        //(the intersection of the two sets):
        a.retainAll(fill(new ArrayList()));
        //Remove elements in this range:
        //!a.removeRange(0,2);
        //Remove everything that's in the argument(实参)
        a.removeAll(fill(new ArrayList()));
        //How big is it?
        i=a.size();
        //Remove all elements
        a.clear();
    }

    public static void iterMotion(List a) {
        ListIterator it =a.listIterator();
        b=it.hasNext();
        b=it.hasPrevious();
        o=it.next();
        i=it.nextIndex();
        o=it.previous();
        i=it.previousIndex();
    }

    public static void iterManipulation(List a) {
        ListIterator it=a.listIterator();
        it.add("47");
        //Must move to an element after add():
        it.next();
        //Remove the element that was just produced
        it.remove();
        //Must move to an element after remove():
        it.next();
        //Change the element that was just produced
        it.set("47");
    }

    public static void testVisual(List a){
        print(a);
        List b = new ArrayList();
        fill(b);
        System.out.println("b = ");
        print(b);
        a.addAll(b);
        a.addAll(fill(new ArrayList()));
        print(a);
        //Shrink the list by removing all the elements beyond the first 1/2 of the list
        System.out.println(a.size());
        System.out.println(a.size() / 2);
        //a.removeRange(a.size()/2,a,size()/2+2);
        print(a);
        //Insert,remove,and replace elements
        //using a ListIterator
        ListIterator x=a.listIterator(a.size()/2);
        x.add("one");
        print(a);
        System.out.println(x.next());
        x.remove();
        System.out.println(x.next());
        x.set("47");
        print(a);
        //Traverse the list backwards:
        x=a.listIterator(a.size());
        while (x.hasPrevious()){
            System.out.println(x.previous());
        }
            System.out.println();
            System.out.println("testVisual finished");
    }
    /**
     * There are some things that only LinkedList can do:
     */
    public static void testLinkedList(){
        LinkedList ll = new LinkedList();
        Collection1.fill(ll,5);
        print(ll);
        //Treat it like a stack ,pushing:
        ll.addFirst("one");
        ll.addFirst("two");
        print(ll);
        //Like "peeking" at the top a stack;
        System.out.println(ll.getFirst());
        //Like popping a stack
        System.out.println(ll.removeFirst());
        System.out.println(ll.removeFirst());
        //Treat it like a queue,pulling elements off the tail end:
        System.out.println(ll.removeLast());
        //With the above operations,it's a dequeue!
        print(ll);
    }

    public static void main(String[] args) {
        //Make and fill a new list each time:
        basicTest(fill(new LinkedList()));
        basicTest(fill(new ArrayList()));
        iterMotion(fill(new LinkedList()));
        iterMotion(fill(new ArrayList()));
        iterManipulation(fill(new ArrayList()));
        iterManipulation(fill(new LinkedList()));
        testVisual(fill(new LinkedList()));
        testLinkedList();
    }
}///:~

使用Sets

  1. Set拥有与Collection完全相同的接口,与List不同,它没有什么额外的功能。
  2. Set完全是一个Collectoion,只是具有不同的行为(这是实力和多态性最为理想 的应用:用于表达不同的行为)
  3. 一个Set只允许每个对象存在一个实例。

** 特性:**

  1. Set(接口)添加到Set的每个元素都必须是独一无二的
  2. 添加到Set的对象必须定义equals(),从而定义对象的唯一性
  3. Set拥有与Collection完全相同的接口,所有Set是无须的
  4. HashSet用于除了非常小的以外的所有Set,对象必须定义HashCode()
  5. TreeSet由一个数组后推得到的Set。面向非常小的Set设计,特别是哪些需要频创建和删除的
  6. 对于小Set,与HashSet相比TreeSet创建和反复所需付出的代价都要小得多,但是随这Set的增大,它的性能也会大大折扣。
  7. TreeSet 不需要HashCode(),它是由一个“红黑树”后推得到的顺序Set,所以TreeSet是有序的Set。
  8. HashSet使用一个散列函数(为了快速检索而设计的)
import sun.reflect.generics.tree.Tree;

import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;

/**
 * Putting your own type in a set
 * @author: ly
 * @date: 2021/10/11 9:22
 */
class MyType implements Comparable{
    private int i;
    public MyType(int n){
        i=n;
    }

    @Override
    public boolean equals(Object o) {
        //instanceof二元操作符 判断左边的对象是否是右边对象的实例
        return(o instanceof MyType)&&(i==((MyType)o).i);
    }

    @Override
    public int hashCode() {
        return i;
    }

    @Override
    public String toString() {
        return i+" ";
    }

    @Override
    public int compareTo(Object o) {
        int i2=((MyType)o).i;
        return (i2<i ? -1:(i2==i?0:1));
    }
}

public class Set2 {
    public static Set fill(Set a, int size) {
        for (int i = 0; i < size; i++) {
            a.add(new MyType(i));
        }
        return a;
    }

    public static Set fill(Set a) {
        return fill(a, 10);
    }

    public static void test(Set a) {
        fill(a);
        //Try to add duplicates
        fill(a);
        fill(a);
        a.addAll(fill(new TreeSet()));
        System.out.println(a);
    }

    public static void main(String[] args) {
        test(new HashSet());
        test(new TreeSet());

    }

}///:~

使用Maps

  1. Map(接口)维持“键-值”对应关系(对)。
  2. HashMap基于一个散列表实现(用它替代Hashtable)。针对“键-值”对的插入和检索,这种形式具有最稳定的性能。可通过构建器对这一性能进行调整,以便于设置散列表的“能力”和“装载因子”。
  3. HreeMap由一个ArrayList后推得到的Map。对遍历和顺序提供了精确的控制。面向非常小的Map设计,特别是哪些需要经常创建的删除的,对于非常小的Map,创建和反复所付出的代价要比HashMap低得多。但是在Map变大以后,性能也会相应的大幅度降低。
  4. HreeMap在一个“红-黑”树的基础上实现。查看键或者“键-值”对时,它们会按照固定顺序排列(取决与Compaiable或Comparator)。TreeMap最大的好处就是我们得到的已是排序好的结果。
  5. TreeMap是含有subMap()方法的唯一一种Map。利用它可以返回树的一部分

决定实施方案

  1. 若一个列表中要进行大量插入和删除操作,则使用LinkedList,否则使用ArrayList,它的速度可能要快一点
  2. TreeSet,支持少量元素,特别合适要求创建和删除大量Set对象的场合使用,一旦需要在自己的Set中容纳大量元素试,TreeSet的性能就会大大折扣。
  3. 在写一个需要Set的程序时,应默认选择HashSet。而且只有在某种特殊情况下(对性能的提升有迫切的需求时),才应该切换到TreeSet。
1. 决定使用何种List
TypeGetIterationInsetRemove
ArratList11049037908730
LinkedList1980220110110
  • ArrayList:对随机访问(即Get)以及循环遍历是最划算的;但是对于LinkedList却是不小的开销。
  • LinkedList:对列表中插入和删除LinkedList是最划算的。
  • 所以我们首先选择一个ArrayList作为自己的默认起点,以后若有大量的插入和删除造成性能下降时,再考虑换成LinkedList也不迟
2. 决定使用何种Set

可在TreeSet以及HashSet间做出选择,具体取决于Set的大小(如果需要一个顺序Set,请使用TreeSet)

  • 进行add()以及contatins()操作时,HashSet显然要比ArraySet(Treeset)出色的多,erqie性能明显与元素的多寡关系不打,一般编写程序的时候,几乎用不到Arrayset(TreeSet)
3. 决定使用何种Map

选择不同的Map实施方案时,Map的大小对于性能的影响时最大的

  • 即使大小为10,TreeMap的性能也要比HashMap差——除遍历循环以外
  • 而在使用Map时,遍历的作用通常不重要(get()通常是我们时间花费最多的地方)
  • TreeMap提供了出色的Put()及遍历性能,但get()的性能并不佳,我们可以不把它当成Map使用,而是把它当作创建顺序的一种途径。树的本质在于它总是顺序排列的
  • 快速检索TreeMap技巧: 可以调用keySet()来获取一个Set。然后通过toArray()生成包含了哪些key的一个数组,随后,可用static方法Array.binarySearch()快速查找排序好的数组中的内容,只有再HashMap的行为不可接受的时候,才需要采用这种做法
  • 当我们使用Map的时候,首先选择的应该是HashMap,只有再及少数的情况下才需要考虑其他的方法。
    最优方案:创建和填充时使用TreeMaP,当检索量增大时将TreeMao转换成HashMap——使用HashMap(Map)构建器。同样地,只有在性能遇到确实遇到瓶颈后,才应关系这些方面的问题————先用起来,再根据需要加快速度。

未支持的操作

排序和搜索
1.数组

Arrays类为所有数据类型的数组提供了一个过载的sort()和binarySearch(),它们也可以用于String和Object
注意:在使用binarySearch前必须对数组进行排序(使用sort())

import java.util.Arrays;
import java.util.Random;

/**
 * @author: ly
 * @date: 2021/10/30 9:58
 */
public class Array11 {
    static Random r = new Random();
    static String ssource =
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
                    "abcdefghijklmnopqrstuvwxyz";
    static char[] src = ssource.toCharArray();

    //Create a random String
    public static String randString(int length) {
        char[] buf = new char[length];
        int rnd;
        for (int i = 0; i < length; i++) {
            //余数的取值范围为:0到除数之间(不包括除数)
            rnd = Math.abs(r.nextInt()) % src.length;
            buf[i] = src[rnd];
        }
        return new String(buf);
    }

    //Create a random array of Strings:
    public static String[] randStrings(int length, int size) {
        String[] s=new String[size];
        for (int i = 0; i < size; i++) {
            s[i]=randString(length);
        }
        return s;
    }
    public static void print(byte[] b) {
        for (int i = 0; i < b.length; i++) {
            System.out.print(b[i]+" ");
        }
        System.out.println();
    }
    public static void print(String [] s) {
        for (int i = 0; i < s.length; i++) {
            System.out.print(s[i]+" ");
        }
        System.out.println();
    }
    public static void main(String[] args) {
       byte[] b=new byte[15];
       //Fill with random bytes
       r.nextBytes(b);
       print(b);
       //sort arrays
       Arrays.sort(b);
       print(b);
       //使用binarySearch是必须先对数组进行排序
       int loc=Arrays.binarySearch(b,b[10]);
       System.out.println("Location of "+b[4]+" = "+loc);
       //Test String sort & search
        String [] s=randStrings(4,10);
        print(s);
        Arrays.sort(s);
        print(s);
        loc = Arrays.binarySearch(s, s[4]);
        System.out.println("Location "+s[4]+" = "+loc);
    }

}
2.可比较和比较器(Object数组排序)

若想对一个Object数组进行排序,必须解决一个问题。根据什么来判断两个Object得顺序呢?但是java的设计这并不认为这是一个重要问题,否则就已经在Object中定义它。所以我们必须在外部进行Object排序,而且新的集合库中提供了实现这一操作的标准方式(当然,最理想的是在Object中定义它)

针对Object数组,可以使用一个sort(),并令其接纳另一个参数:实现了Compartor接口(即比较器)的一个对象,并用它的单个compare()方法进行比较。
compare()方法把两个准备比较的对象作为自己的参数使用————若第一个参数小于第二个,返回一个负整数;若相等返回零;若第一个参数大于第二个,则返回正整数。

import java.util.Arrays;
import java.util.Comparator;

/**
 * Using Comparator to perform an alphabetic sort
 * @author: ly
 * @date: 2021/10/30 11:33
 */
public class AlphaComp implements Comparator {
    @Override
    //覆盖比较方法
    public int compare(Object o1, Object o2) {
        //Assume it‘s used only for Strings...
        //转小写
        String s1=((String) o1).toLowerCase();
        String s2=((String) o2).toLowerCase();
        return s1.compareTo(s2);
    }

    public static void main(String[] args) {
        String[] s=Array11.randStrings(4,10);
        Array11.print(s);
        //创建比较器对象
        AlphaComp ac=new AlphaComp();
        Arrays.sort(s,ac);
        Array11.print(s);
        //Must use the Comparator to search,also:
        int loc=Arrays.binarySearch(s,s[3],ac);
        System.out.println("Location of "+ s[3]+" = "+loc);

    }
}///:~

Arrays类提供了另一个sort()方法,它会采用单个自变量:一个Object数组,但没有Comparator。这个sort()方法也必须用同样的方式
来比较两个Object。通过实现Comparable接口,它采用了赋予一个类的“自然比较方法”。这个接口含有单独一个方法——compareTo(),能分别根据它小于、等于、大于参数而返回负数、零或者正数,从而实现对象的比较。

import java.sql.SQLOutput;
import java.util.Arrays;

/**
 *A class that implements Comparable
 * @author: ly
 * @date: 2021/10/31 12:04
 */
public class CompClass implements Comparable {
    private int i;
    public CompClass(int ii){
        i=ii;
    }

    @Override
    public int compareTo(Object o) {
        //Implicitly tests for correct type:
        int argi=((CompClass)o).i;
        if (i==argi){
            return 0;
        }
        if (i < argi) {
            return  -1;
        }
        return 1;
    }
    public static void print(Object[] a){
        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i]+" ");
        }
        System.out.println();
    }

    @Override
    public String toString(){
        return i+"";
    }

    public static void main(String[] args) {
        CompClass [] a=new CompClass[20];
        for (int i = 0; i < a.length; i++) {
            a[i]=new CompClass((int)(Math.random()*100));
        }
        print(a);
        Arrays.sort(a);
        print(a);
        int loc=Arrays.binarySearch(a,a[3]);
        System.out.println("Location of "+a[3]+" = "+loc);
    }
}///:~

当然,我们的CompareTo()方法亦可根据实际情况增大复杂度。

3.列表

可用与数组相同的形式排序和搜索一个List。用于排序和搜索的静态方法包含在类Collections中,但它们拥有与Arrays中差不多的签名:

  • sort(List)用于对一个实现了Comparable的对象列表进行排序;
  • binarySearch(List,Object)用于查找列表中的某个对象;
  • sort(List,Comparator)利用“比较器”对一个列表进行排序
  • binarySearch(List,Object,Comparator)用于查找那个对象中的一个对象
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
 * Sorting and searching lists with 'collections'
 *
 * @author: ly
 * @date: 2021/10/31 14:30
 */
public class ListSort {
    public static void main(String[] args) {
        final int SZ = 20;
        //Using " natural comparison method "
        List a = new ArrayList();
        for (int i=0;i<SZ;i++){
            a.add(new CompClass((int)(Math.random()*100)));
        }
        Collection1.print(a);
        Collections.sort(a);
        Collection1.print(a);
        Object find=a.get(SZ/2);
        int loc=Collections.binarySearch(a,find);
        System.out.println("location of "+ find+" = "+loc);
        //Using a Comparator:
        List b=new ArrayList();
        for (int i = 0; i < SZ; i++) {
            b.add(Array1.randString(4));
        }
        Collection1.print(b);
        AlphaComp ac=new AlphaComp();
        Collections.sort(b,ac);
        Collection1.print(b);
        find =b.get(SZ/2);
        //Must use the Comparator to search,also:
        loc=Collections.binarySearch(b,find,ac);
        System.out.println("Location of "+find +" = "+loc);
    }
}///:~

这些方法的用法与在Arrays中的用法是完全一致的,只是用一个列表代替数组。

实用工具
  • enumeration(Collection)为自变量产生原始风格的Enumeration(枚举)
  • max(Collection),min(Collection)在自变量中用集合对象的自然比较方法产生最大或最小值
  • max(Collecyionm,Comparator),min(Collectiob,Comparator)在集合内用比较器产生最大或最小值
  • nCopies(int n,Object o)返回长度为n的一个不可变,它所有的句柄均指向o
  • subList(List,int,min,int,max)返回由指定参数列表后推得到的一个新列表。可将这个列表想象成一个“窗口”,它自索引为min的地方开始,正好结束于max的前面

注意:min()和max()都是随同Collection对象工作的,而非List,所以不必担心Collection是否需要排序(就像早先指出的那样,在执行一次binarySearch()————即二进制搜索之前,必须对一个List或者Arrays执行Sort())。

1.是Collection或Map不可修改

通常,创建Collection或者Map的一个“只读”版本显得更有利一些,Collection类允许我们达到这个目标,方法是将原始容器传递进入一个方法,并令其返回一个只读版本。

import java.util.*;

/**
 * USing the Collection.unmodifiable methods
 *
 * @author: ly
 * @date: 2021/10/31 16:05
 */
public class ReadOnly {
    public static void main(String[] args) {
        Collection c = new ArrayList();
        //Insert useful data
        Collection1.fill(c);
        //set readOnly
        c = Collections.unmodifiableCollection(c);
        //Reading is OK
        Collection1.print(c);
        //!c.add("one");  //Can't change it

        List a = new ArrayList();
        Collection1.fill(a);
        a = Collections.unmodifiableList(a);
        ListIterator lit = a.listIterator();
        //Reading OK
        System.out.println(lit.next());
        //Can't change it
        //!lit.add("one");

        Set s = new HashSet();
        Collection1.fill(s);
        s = Collections.unmodifiableSet(s);
        //Reading OK
        Collection1.print(s);
        //Can't change it
        //!s.add("One");

        Map m = new HashMap();
        Map1.fill(m, Map1.testData1);
        m = Collections.unmodifiableMap(m);
        //Reading OK
        Map1.print(m);
        //!m.put("Ralph","Howdy!");
    }
}

对于每种情况,在将其正式变为只读前,必须有有效的数据填充容器。一旦载入成功,最佳的做法就是用“不可修改”调用产生的句柄替换现有的句柄。这样做可有效避免将其变成不可修改后不慎改变其中内容。在另一方面,该工具也允许我们在一个类中将能狗修改的容器保持为private状态,并可从一个方法调用中返回指向那个容器的一个只读句柄,这样一来,虽然我们可以在类里修改它,但是其他任何人都只能读。为特定类型调用“不可修改”的方法不会造成编译期间的检查,但一旦发生任何变化,对修改特定容器的方法的调用便会产生一个UnsupportedOperationException违例

2.Collection或Map的同步

synchronized关键字是“多线程”机制的一个非常重要的部分。在这里大家只需要注意到Coolection类提供了对整个容器进行自动同步的一种途径,它的语法与“不可修改”的方法是类似的:

import java.util.*;

/**
 * Using the Collections.synchronized method
 *
 * @author: ly
 * @date: 2021/10/31 16:57
 */
public class Synchronization {
    public static void main(String[] args) {
        Collection a = new ArrayList();
        Collection collection = new ArrayList();
        Collection1.fill(collection);
        Collection1.print(collection);
        a = Collections.synchronizedCollection(collection);
        Collection1.print(a);

        Collection c =
                Collections.synchronizedCollection(
                        new ArrayList());
        List list = Collections.synchronizedList(
                new ArrayList());
        Set s = Collections.synchronizedSet(
                new HashSet());
        Map m = Collections.synchronizedMap(
                new HashMap());
    }
}

在这种情况下,我们通过适当的“同步”方法直接传递新容器;这样做可避免不慎暴露出未同步的版本。新集合也提供了能防止多个进程同时修改一个容器内容的机制。若在一个容器里反复,同时另一些进程介入,并在那个容器中插入、删除或者修改一个对象,便会面临冲突的危险。我们可能已传递了那个对象,可能它位于我们前面,可能容器的大小在我们调用size()后已发生了收缩————我们面临各种各样的危险,针对这个问题,新的集合库集成了一套解决机制,能够查出除我们的进程自己需要负责的之外的,对容器的其他任何修改。若探测到有其他方面也准备修改容器,便会立即产生一个ConCurrentModificationException(并发修改违例)。我们将这一机制称之为“立即失败”————它并不用更复杂的算法在“之后”侦测问题,而是“立即”产生违例。

7.总结

  1. 数组包含了对象的数字化索引。它容纳的是一种已知类型的对象,所以在查找一个对象时,不必对结果进行造型处理。数组是可以多维的,而且能够容纳基本类型。但是,一旦把它创建好以后,大小便不能改变了。
  2. Vector(矢量)也包含了对象的数字索引————可将数组和Vectortor想象成随机访问集合。当我们加入更多的元素时,Vector能够自动改变自身的大小。但Vector只能容纳对象的句柄,所以它不可包含基本数据类型;而其将一个对象句柄从集合中取出来的时候,必须对结果进行造型处理。
  3. Hashtable(散列表)属于Dictionart(字典)类的一种类型,是一种将对象(而不是数字)同其他对象关联到一起的方式。散列表也支持对象的随机访问,事实上,它的整个设计方案都在突出访问的“高速度”。
  4. Stack(堆栈)是一种“后入先出”(LIFO)的List。

若你曾经熟悉数据结构,可能会疑惑为何没看到一套更大的集合。从功能角度出发,你真的需要一套更大的集合吗?对于Hashable,可将任何东西置入其中,并以非常快的速度检索;对于Enumeration(枚举),可遍历一个序列,并对其中的每个元素都采取一个特定的操作。那是一种功能足够强劲的工具。
但Hashtable没有“顺序”的概念。Vector和数组为我们提供了一种线性顺序,但若要把一个元素插入它们任何一个的中部,一般都要付出‘惨重’的代价。除此之外,list ,拆散列队(??)、优先级列队以及Tree都涉及到元素的“排序”————并非仅仅将它们置入,以便以后能按线性顺序查找或移动它们。这些数据结构也非常有用,这也正是标准C++中包含了它们的原因。考虑到这个原因,只应将标准的java库的集合看作自己的一个起点。而且倘若必须使用Java1.0或1.1,则可在需要超越它们的时候使用JGL。
如果可以使用Java1.2,那么只使用新集合即可,它一般能满足我们的所以需求。注意本书在java1.1身上花了大量篇幅,所以书中用到的大量集合只能在java1.1中用到的哪些Vector和Hashtable。就目前来看这是个不得以而为之的做法,这是一个不得已而为之的做法。但是,这样处理亦可提供与老Java代码更出色的项后兼容能力。若要用Java1.2写新代码,新的集合往往能更好的为你服务。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值