Thinking in Java 读书笔记

本文深入探讨Java集合框架,包括数组、List、Set、Map等核心概念及其实现原理,介绍了不同容器的特点和应用场景,并提供了丰富的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  1. 对象的集合:
    1. 数组:
      1. 数组若越界,则会抛出RunTimeException异常。
      2. Arrays用于数组,由于Java不支持模板,所以我们需要对其进行类型检查。典型的应用如下:
        Arrays.fill(a1,true);
        System.out.println("a1="+Arrays.asList(a1));
        若我们想要对类型敏感,我们可以采用Composition的模式,产生自己的容器。
      3. 当我们需要复制一个数组的时候,我们可以使用System.arraycopy的static方法:
        int[] i=new int[7];
        int[] k=new int[5];
        Arrays.fill(k,103);
        System.arraycopy(i,0,k,0,k.length);
      4. 数组的比较:使用Arrays.equals来对数组进行比较;但是因为没有了泛型算法,所以当我们要提供自有的比较方法的时候,我们应该实现java.lang.Comparable接口,如下:
        public class CompType implements Comparable {
           int i;
           int j;
           public CompType(int n1,int n2) {
               i=n1;
               j=n2;
           }
           public int compareTo(Object rv) {
               return (i<rv?-1:(i==rv?0:1));
           }
           private static Random r=new Random();
           public static Generator generator() {
               return new Generator() {
                  public Object next() {
                      return new CompType(r.nextInt(100),r.nextInt(100));
                  }
               };
           }
          public static void main(String[] args) {
             CompType[] a=new CompType[10];
             Arrays2.fill(a,generator());
             Arrays.sort(a);
          }
        }
        这 里我们只用到了i的值。若我们没有提供compareTo方法,那么当调用到sort方法时,就回抛出ClassCastException异常。若我们 想要逆序排序,我们可以通过设置sort的第二个参数为Collections.reverseOrder方法。同样我们可以象C++一样提供一个 Comparator接口,来提供比较的方法:
        class CompTypeComparator implements Comparator {
          public int compare(Object v1,Object v2) {
              int j1=((CompType)v1).j;
              int j2=((CompType)v2).j;
              return (j1<j2?-1:(j1==j2?0:1));
          }
        }
        然后设置sort的第二个参数为new CompTypeComparator()
      5. 一旦数组排序好后,我们可以使用Arrays.binarySearch来进行快速查询。
    2. 迭代器:
      1. 用iterator()方法传递一个Iterator对象,第一次调用next()方法时,会传给序列中的第一个元素。
      2. next()方法返回下一个元素。
      3. hasNext()方法查询是否还有其他元素。
      4. remove()方法删除迭代器最后一个元素。
    3. List:通常用add方法加对象,用get方法取对象,用iterator方法获取迭代器。
      1. List:最重要的特征是有序,它在Collections基础上添加了大量的方法。它可以产生ListIterator对象,用它除了可以在中间插入和删除对象外,还可以沿两个方向遍历List。
      2. ArrayList:用数组实现,能快速访问,但在中间插入和删除对象效率较低,ListIterator只能用于反向遍历ArrayList的场合,不要用于插入和删除对象。
      3. LinkedList:对顺序访问进行优化,在中间插入和删除对象效率最高,但随机访问较慢,此外还有addFirst、addLast、getFirst、getLast、removeFirst和removeLast等方法,可以把它当作栈,队列和双向队列来使用。
    4. Set:
      1. Set:加入Set的元素必须唯一,这和C++ STL中是一样的,要想加入Set,元素必须实现equals,这样才能表明唯一性。它的接口和Collection的一样,不保证会采用什么顺序存储元素。
      2. HashSet:为优化查询速度而设计的Set,元素还要额外定义hashCode方法。
      3. TreeSet:有序的Set,其底层是一棵树。这样就可以从Set里面提取一个有序序列。
      4. LinkedHashSet:在内部使用链表的Set,既有HashSet的查询速度,又能保存元素被加进去的顺序。用Iterator迭代器遍历Set的时候,保持插入时的顺序。
      5. SortedSet:里面的元素一定是有序的,comparator()方法返回所用的Comparator对象,或者用null表示 它使用Object自有的排序方法;first()返回最小的元素;last()返回最大的元素;subSet(fromElement, toElement)返回Set的子集;headSet(toElement)返回子集,其中的元素都小于toElement;tailSet (fromElement)返回子集,其中的元素都大于fromElement。
    5. Map:put(key,value)方法放入一个值,并且与key关联起来;get(key)会返回该值;containsKey()和containsValue()测试该Map是否含有某个键或值。
      1. Map:维持键—值的关系。
      2. HashMap:基于Hash的实现,提供恒定时间的查询和插入。在构造函数中可以设置Hash表的capacity和load factor,通过构造函数来调节性能。
      3. LinkedHashMap:类似于HashMap,但因为内部采用链表的形式,所以用Iterator遍历的时候,会按插入的顺序或最先使用的顺序进行访问。除了用Iterator外,其它情况比HashMap稍慢。
      4. TreeMap:基于红黑树数据结构的实现,它是按顺序(根据Comparable或者Comparator)排列的,它的特点是有序,并且唯一实现了subMap方法,该方法能获取树的一部分。
      5. WeakHashMap:一个weak key的Map,为某些特殊问题设计,它能让Map释放所持有的对象。若某个对象只在Map当中充当键,那它将被回收。
      6. IdentityHashMap:用==替代equals,用于解决特殊问题。
      7. SortedMap,只有TreeMap这一实现,它的键肯定是有序的,comparator()返回所用的Comparator对 象,null代表自有的比较方法;firstKey()返回第一个键;lastKey()返回最后一个键;subMap(fromKey,toKey)返 回一个子集,包括前者,不包括后者;headMap(toKey)返回一个子集,其键小于toKey;tailMap(fromKey)返回一个子集,其 键均大于fromKey;
    6. Collections含有的工具:相当于STL中的算法。
      1. [max/min](Collection):用自然对象内置的算法进行比较,返回最大和最小的元素。
      2. [max/min](Collection,Comparator):用Comparator对象进行比较,返回最大和最小的元素。
      3. [indexOfSubList/lastIndexOfSubList](List source,List target):获取target第一次和最后一次出现在source中的位置。
      4. replaceAll(List list,Object oldVal,Object newVal):将所有的oldVal替换成newVal。
      5. reverse():颠倒List的顺序。
      6. rotate(List list,int distance):把所有的元素循环向后移动distance位。
      7. copy(List dest,List src):将src的元素拷贝到dest。
      8. swap(List list,int i,int j):互换i和j位置上的元素。
      9. fill(List list,Object o):把list里面的全部元素替换成o。
      10. nCopies(int n,Object o):返回一个有n个元素的不可变的List,而且这个List中的所有元素全都指向o。
      11. enumeration(Collection):返回一个老式的Enumeration。
      12. list(Enumeration e):用这个Enumeration生成一个ArrayList,并且返回这个ArrayList。
    7. 可以用Arrays.asList把数组改造成List。
    8. BitSet:同STL一样,我们可以使用BitSet来存储"是非"信息。
  2. I/O类:
    1. File类:用于处理文件和目录的类
      1. 目录列表器:通过不带参数的list方法,来返回String数组;或者通过传递一个实现了FilenameFilter接口的对象来过滤。举例:
        import java.io.*;
        import java.util.*;
        import java.util.regex.*;
        import com.bruceeckel.util.*;
        public class DirList {
          public static void main(String[] args) {
            File path = new File(".");
            String[] list;
            if(args.length == 0)
              list = path.list();
            else
              list = path.list(new DirFilter(args[0]));
            Arrays.sort(list, new AlphabeticComparator());
            for(int i = 0; i < list.length; i++)
              System.out.println(list[i]);
          }
        }
        class DirFilter implements FilenameFilter {
          private Pattern pattern;
          public DirFilter(String regex) {
            pattern = Pattern.compile(regex);
          }
          public boolean accept(File dir, String name) {
            return pattern.matcher(
              new File(name).getName()).matches();
          }
        }
        当然我们也可以通过匿名类的方法,减少无关的类的使用。
      2. 查看与创建目录:getAbsolutePath方法获取绝对路径;还有canRead、canWrite、getName、 getParent、getPath、length、lastModified、isFile、isDirectory等方法。renameTo、 delete、mkdirs方法来重命名、删除和创建。
    2. 输入与输出:I/O类库常使用“流”这种抽象。
      1. InputStream:代表那些能从各种输入源获取数据的类,这些源包括byte数组、String对象、文件、管道、流序列、其他源(例如: Internet)这些源都有与之对应的子类。ByteArrayInputStream、StringBufferInputStream、 FileInputStream、PipedInputStream、SequenceInputStream、FilterInputStream。其 中FilterInputStream又包含有:DataInputStream、BufferedInputStream、 LineNumberInputStream、PushbackInputStream。
      2. OutputStream:ByteArrayOutputStream、FileOutputStream、 PipedOutputStream、FilterOutputStream。其中FilterOutputStream又包含有: DataOutputStream、PrintStream、BufferedOutputStream。
    3. Reader和Writer:主要是针对国际化问题,这样I/O就能支持Unicode了。
    4. 常见的I/O流的使用方法,举例:
      import com.bruceeckel.simpletest.*;
      import java.io.*;
      public class IOStreamDemo {
        private static Test monitor = new Test();
        // Throw exceptions to console:
        public static void main(String[] args)
        throws IOException {
          // 1. Reading input by lines:
          BufferedReader in = new BufferedReader(
            new FileReader("IOStreamDemo.java"));
          String s, s2 = new String();
          while((s = in.readLine())!= null)
            s2 += s + "/n";
          in.close();
          // 1b. Reading standard input:
          BufferedReader stdin = new BufferedReader(
            new InputStreamReader(System.in));
          System.out.print("Enter a line:");
          System.out.println(stdin.readLine());
          // 2. Input from memory
          StringReader in2 = new StringReader(s2);
          int c;
          while((c = in2.read()) != -1)
            System.out.print((char)c);
          // 3. Formatted memory input
          try {
            DataInputStream in3 = new DataInputStream(
              new ByteArrayInputStream(s2.getBytes()));
            while(true)
              System.out.print((char)in3.readByte());
          } catch(EOFException e) {
            System.err.println("End of stream");
          }
          // 4. File output
          try {
            BufferedReader in4 = new BufferedReader(
              new StringReader(s2));
            PrintWriter out1 = new PrintWriter(
              new BufferedWriter(new FileWriter("IODemo.out")));
            int lineCount = 1;
            while((s = in4.readLine()) != null )
              out1.println(lineCount++ + ": " + s);
            out1.close();
          } catch(EOFException e) {
            System.err.println("End of stream");
          }
          // 5. Storing & recovering data
          try {
            DataOutputStream out2 = new DataOutputStream(
              new BufferedOutputStream(
                new FileOutputStream("Data.txt")));
            out2.writeDouble(3.14159);
            out2.writeUTF("That was pi");
            out2.writeDouble(1.41413);
            out2.writeUTF("Square root of 2");
            out2.close();
            DataInputStream in5 = new DataInputStream(
              new BufferedInputStream(
                new FileInputStream("Data.txt")));
            // Must use DataInputStream for data:
            System.out.println(in5.readDouble());
            // Only readUTF() will recover the
            // Java-UTF String properly:
            System.out.println(in5.readUTF());
            // Read the following double and String:
            System.out.println(in5.readDouble());
            System.out.println(in5.readUTF());
          } catch(EOFException e) {
            throw new RuntimeException(e);
          }
          // 6. Reading/writing random access files
          RandomAccessFile rf =
            new RandomAccessFile("rtest.dat", "rw");
          for(int i = 0; i < 10; i++)
            rf.writeDouble(i*1.414);
          rf.close();
          rf = new RandomAccessFile("rtest.dat", "rw");
          rf.seek(5*8);
          rf.writeDouble(47.0001);
          rf.close();
          rf = new RandomAccessFile("rtest.dat", "r");
          for(int i = 0; i < 10; i++)
            System.out.println("Value " + i + ": " +
              rf.readDouble());
          rf.close();
          monitor.expect("IOStreamDemo.out");
        }
      }
    5. 标准I/O:来自于Unix的概念,例如:键盘和屏幕。
      1. 读取标准输入:System.in、System.out、System.err
      2. 标准I/O的重定向:System类提供几个重定向的方法,setIn(InputStream)、setOut(PrintStream)、setErr(PrintStream)。
    6. New I/O:为了提高速度,Java 1.4引入了nio。
      1. ByteBuffer:存储byte数据的Buffer,并且是唯一能直接同channel打交道的buffer。
      2. MappedByteBuffer:内存映射文件,可以把它当作全部读进内存,然后当成数组来访问。
      3. 文件锁:允许以文件为共享资源,对访问进行同步化处理。要获取整个文件的锁,可以用FileChannel的tryLock或lock 方法,最后用FileLock.release()释放锁。还可以通过tryLock(long position,long size,boolean shared)来锁住文件的。
    7. 压缩:Java I/O还提供能读写。它提供CheckedInputStream、CheckedOutputStream用于返回checksum, DeflaterOutputStream是压缩类的基类,ZipOutputStream把数据压缩成Zip文件,GZIPOutputStream把 数据压缩成GZip文件,InflaterInputStream是解压缩类的基类,有ZipInputStream和GZIPInputStream两 个子类。举例:
      1. GZip:
        import com.bruceeckel.simpletest.*;
        import java.io.*;
        import java.util.zip.*;
        public class GZIPcompress {
          private static Test monitor = new Test();
          // Throw exceptions to console:
          public static void main(String[] args)
          throws IOException {
            if(args.length == 0) {
              System.out.println(
                "Usage: /nGZIPcompress file/n" +
                "/tUses GZIP compression to compress " +
                "the file to test.gz");
              System.exit(1);
            }
            BufferedReader in = new BufferedReader(
              new FileReader(args[0]));
            BufferedOutputStream out = new BufferedOutputStream(
              new GZIPOutputStream(
                new FileOutputStream("test.gz")));
            System.out.println("Writing file");
            int c;
            while((c = in.read()) != -1)
              out.write(c);
            in.close();
            out.close();
            System.out.println("Reading file");
            BufferedReader in2 = new BufferedReader(
              new InputStreamReader(new GZIPInputStream(
                new FileInputStream("test.gz"))));
            String s;
            while((s = in2.readLine()) != null)
              System.out.println(s);
            monitor.expect(new String[] {
              "Writing file",
              "Reading file"
            }, args[0]);
          }
        }
      2. Zip:
        import com.bruceeckel.simpletest.*;
        import java.io.*;
        import java.util.*;
        import java.util.zip.*;
        public class ZipCompress {
          private static Test monitor = new Test();
          // Throw exceptions to console:
          public static void main(String[] args)
          throws IOException {
            FileOutputStream f = new FileOutputStream("test.zip");
            CheckedOutputStream csum =
              new CheckedOutputStream(f, new Adler32());
             ZipOutputStream zos = new ZipOutputStream(csum);
             BufferedOutputStream out =
              new BufferedOutputStream(zos);
            zos.setComment("A test of Java Zipping");
            // No corresponding getComment(), though.
            for(int i = 0; i < args.length; i++) {
              System.out.println("Writing file " + args[i]);
              BufferedReader in =
                new BufferedReader(new FileReader(args[i]));
              zos.putNextEntry(new ZipEntry(args[i]));
              int c;
              while((c = in.read()) != -1)
                out.write(c);
              in.close();
            }
            out.close();
            // Checksum valid only after the file has been closed!
            System.out.println("Checksum: " +
              csum.getChecksum().getValue());
            // Now extract the files:
            System.out.println("Reading file");
            FileInputStream fi = new FileInputStream("test.zip");
            CheckedInputStream csumi =
              new CheckedInputStream(fi, new Adler32());
            ZipInputStream in2 = new ZipInputStream(csumi);
            BufferedInputStream bis = new BufferedInputStream(in2);
            ZipEntry ze;
            while((ze = in2.getNextEntry()) != null) {
              System.out.println("Reading file " + ze);
              int x;
              while((x = bis.read()) != -1)
                System.out.write(x);
            }
            if(args.length == 1)
              monitor.expect(new String[] {
                "Writing file " + args[0],
                "%% Checksum: //d+",
                "Reading file",
                "Reading file " + args[0]}, args[0]);
            System.out.println("Checksum: " +
              csumi.getChecksum().getValue());
            bis.close();
            // Alternative way to open and read zip files:
            ZipFile zf = new ZipFile("test.zip");
            Enumeration e = zf.entries();
            while(e.hasMoreElements()) {
              ZipEntry ze2 = (ZipEntry)e.nextElement();
              System.out.println("File: " + ze2);
              // ... and extract the data as before
            }
            if(args.length == 1)
              monitor.expect(new String[] {
                "%% Checksum: //d+",
                "File: " + args[0]
              });
          }
        }
      3. 对象序列化:能将一个实现了Serializable接口的对象转换成一组byte,这样日后要使用这个对象可以直接从byte数据恢 复出来;我们可以让对象去实现Externalizable来控制序列化的过程(创建新的子对象)。同时在序列化和恢复的时候会调用 writeExternal和readExternal方法。若我们在一个对象中不希望某个变量被序列化,我们应该使用transient关键字。
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值