41.容器集合的选择
List容器无非分为两大种,基于数组和基于链表:数组有索引,所以查找快,链表插入删除不需要移动全部元素,插入删除快。
ArrayList/Vector查找快,LinkedList插入删除元素快。而Queue什么都快,但是队列先入先出,没有其他集合灵活。
Set也是大体分为两种,基于红黑树的Tree和基于hash算法的Hash(Set的元素是不能重复的,List可以重复)。
HashSet各方面的速度稍微比TreeSet快。但TreeSet元素的排列是经过排序的。
Map的情况与set类似(HashMap和TreeMap),一般来讲,程序选择基于Hash的多。
42.回收容器元素的引用
如果遇到容器过于庞大或者硬件配置太低而需要回收元素的地址,可以选择使用更加利于回收机制的容器。SoftReference,weakReference,PhantomReference。
具体用法看下面的例子:
import java.lang.ref.*;
import java.util.*;
class VeryBig{
private static final int SIZE=10000;
private long[] la=new long[SIZE];
private String ident;
public VeryBig(String id){ident=id;}
public String toString(){return ident;}
protected void finalize(){System.out.println("Finalizing"+ident);}
}
public class References{
private static ReferenceQueue<VeryBig> rq=new ReferenceQueue<VeryBig>();
public static void checkQueue(){
Reference<? extends VeryBig> inq=rq.poll();
if(inq!=null)
System.out.println("In queue:"+ inq.get());
}
public static void main(String[] args){
int size=10;
if(args.length>0)
size=new Integer(args[0]);
LinkedList<SoftReference<VeryBig>> sa=new LinkedList<SoftReference<VeryBig>>();
for(int i=0;i<size;i++){
sa.add(new SoftReference<VeryBig>(new VeryBig("Soft "+i),rq));
System.out.println("Just created:"+sa.getLast());
checkQueue();
}
LinkedList<WeakReference<VeryBig>> wa=new LinkedList<WeakReference<VeryBig>>();
for(int i=0;i<size;i++){
wa.add(new WeakReference<VeryBig>(new VeryBig("weak "+i),rq));
System.out.println("Just created:"+wa.getLast());
checkQueue();
}
SoftReference<VeryBig> s=new SoftReference<VeryBig>(new VeryBig("Soft"));
WeakReference<VeryBig> w=new WeakReference<VeryBig>(new VeryBig("Weak"));
System.gc();
LinkedList<PhantomReference<VeryBig>> pa=new LinkedList<PhantomReference<VeryBig>>();
for(int i=0;i<size;i++){
pa.add(new PhantomReference<VeryBig>(new VeryBig("Phantom "+i),rq));
System.out.println("Just created:"+pa.getLast());
checkQueue();
}
}
}/*Output
Just created:java.lang.ref.SoftReference@76ea7776
Just created:java.lang.ref.SoftReference@1b4920f8
Just created:java.lang.ref.SoftReference@5e1387c6
Just created:java.lang.ref.SoftReference@5437086a
Just created:java.lang.ref.SoftReference@69099257
Just created:java.lang.ref.SoftReference@7366c3a0
Just created:java.lang.ref.SoftReference@5fb57890
Just created:java.lang.ref.SoftReference@2fe6707
Just created:java.lang.ref.SoftReference@c1dfe1a
Just created:java.lang.ref.SoftReference@515632d
Just created:java.lang.ref.WeakReference@3f2221f6
Just created:java.lang.ref.WeakReference@59e3bddb
Just created:java.lang.ref.WeakReference@76c5a2f7
Just created:java.lang.ref.WeakReference@294e430c
Just created:java.lang.ref.WeakReference@5113de03
Just created:java.lang.ref.WeakReference@6f54c08a
Just created:java.lang.ref.WeakReference@252cdd20
Just created:java.lang.ref.WeakReference@246972f1
Just created:java.lang.ref.WeakReference@6f93ee4
Just created:java.lang.ref.WeakReference@558fee4f
Just created:java.lang.ref.PhantomReference@2f995c9a
In queue:null
FinalizingWeak
Just created:java.lang.ref.PhantomReference@7d8e9adf
In queue:null
Finalizingweak 9
Finalizingweak 8
Finalizingweak 7
Finalizingweak 6
Finalizingweak 5
Finalizingweak 4
Finalizingweak 3
Finalizingweak 2
Finalizingweak 1
Finalizingweak 0
Just created:java.lang.ref.PhantomReference@1d59e6df
In queue:null
Just created:java.lang.ref.PhantomReference@79444986
In queue:null
Just created:java.lang.ref.PhantomReference@72373a9c
In queue:null
Just created:java.lang.ref.PhantomReference@7e91259
In queue:null
Just created:java.lang.ref.PhantomReference@56b3951d
In queue:null
Just created:java.lang.ref.PhantomReference@2802cf63
In queue:null
Just created:java.lang.ref.PhantomReference@507d811a
In queue:null
Just created:java.lang.ref.PhantomReference@5fa6fb3e
In queue:null
*///~
43.Collection的特性
Collection为List,Set,Queue的始祖,有一些不错的特性,比如翻转元素,替换元素,移动元素…
看例子吧:
import java.util.*;
public class Utilities{
static List<String> list=Arrays.asList("one two three Four five six one".split(" "));
public static void main(String[] args){
System.out.println(list);
System.out.println("'list' disjoint (Four)?:"+Collections.disjoint(list,Collections.singletonList("Four")));
System.out.println("max: "+Collections.max(list));
System.out.println("min: "+Collections.min(list));
System.out.println("max a/ comparator: "+Collections.max(list,String.CASE_INSENSITIVE_ORDER));
System.out.println("min a/ comparator: "+Collections.min(list,String.CASE_INSENSITIVE_ORDER));
List<String> sublist=Arrays.asList("Four five six".split(" "));
System.out.println("indexOfSubList: "+Collections.indexOfSubList(list,sublist));
System.out.println("lastIndexofList: "+Collections.lastIndexOfSubList(list,sublist));
Collections.replaceAll(list,"one","Yo");
System.out.println("replaceAll: "+list);
Collections.reverse(list);
System.out.println("reverse: "+list);
Collections.rotate(list,3);
System.out.println("rotate: "+list);
List<String> source=Arrays.asList("in the matrix".split(" "));
Collections.copy(list,source);
System.out.println("copy: "+list);
Collections.swap(list,0,list.size()-1);
System.out.println("swap: "+list);
Collections.shuffle(list,new Random(47));
System.out.println("shuffled: "+list);
Collections.fill(list,"pop");
System.out.println("fill: "+list);
System.out.println("frequency of 'pop': "+Collections.frequency(list,"pop"));
List<String> dups=Collections.nCopies(3,"snap");
System.out.println("dups: "+dups);
System.out.println("'list' disjoint 'dups'?: "+Collections.disjoint(list,dups));
Enumeration<String> e=Collections.enumeration(dups);
Vector<String> v=new Vector<String>();
while(e.hasMoreElements())
v.addElement(e.nextElement());
ArrayList<String> arrayList=Collections.list(v.elements());
System.out.println("arrayList: "+arrayList);
}
}/*Output
[one, two, three, Four, five, six, one]
'list' disjoint (Four)?:false
max: two
min: Four
max a/ comparator: two
min a/ comparator: five
indexOfSubList: 3
lastIndexofList: 3
replaceAll: [Yo, two, three, Four, five, six, Yo]
reverse: [Yo, six, five, Four, three, two, Yo]
rotate: [three, two, Yo, Yo, six, five, Four]
copy: [in, the, matrix, Yo, six, five, Four]
swap: [Four, the, matrix, Yo, six, five, in]
shuffled: [six, matrix, the, Four, Yo, five, in]
fill: [pop, pop, pop, pop, pop, pop, pop]
frequency of 'pop': 7
dups: [snap, snap, snap]
'list' disjoint 'dups'?: true
arrayList: [snap, snap, snap]
*///~
43.重写hashCode()方法
为什么要重写hashCode():如果现有的集合满足不了你,想自己写一个或者修改现有集合,就必须要重写hashCode(),因为像Map的key都要有一个hashCode()。
看代码,理解思路就好:
import java.util.*;
public class CountedString{
private static List<String> created=new ArrayList<String>();
private String s;
private int id=0;
public CountedString(String str){
s=str;
created.add(s);
for(String s2 : created)
if(s2.equals(s))
id++;
}
public String toString(){
return "String:"+s+"id:"+id+"hasCode():"+hashCode();
}
public int hashCode(){
int result=17;
result=37*result+s.hashCode();
result=37*result+id;
return result;
}
public boolean equals(Object o){
return o instanceof CountedString && s.equals(((CountedString)o).s)&&id==((CountedString)o).id;
}
public static void main(String[] args){
Map<CountedString,Integer> map=new HashMap<CountedString,Integer>();
CountedString[] cs=new CountedString[5];
for(int i=0;i<cs.length;i++){
cs[i]=new CountedString("hi");
map.put(cs[i],i);
}
System.out.println(map);
for(CountedString cstring:cs){
System.out.println("Looking up"+cstring);
System.out.println(map.get(cstring));
}
}
}/*Output
{String:hiid:1hasCode():146447=0, String:hiid:2hasCode():146448=1, String:hiid:3hasCode():146449=2, String:hiid:4hasCode():146450=3, String:hiid:5hasCode():146451=4}
Looking upString:hiid:1hasCode():146447
0
Looking upString:hiid:2hasCode():146448
1
Looking upString:hiid:3hasCode():146449
2
Looking upString:hiid:4hasCode():146450
3
Looking upString:hiid:5hasCode():146451
4
*///~
44.如何把类写成只读形式
final关键字:
public class Pair<K,V>{
public final K key;
public final V value;
public Pair(K k,V v){
key=k;
value=v;
}
}
45.Map的底层实现
Map其实是基于List的,看代码吧:
import java.util.*;
public class SlowMap<K,V> extends AbstractMap<K,V>{
private List<K> keys=new ArrayList<K>();
private List<V> values=new ArrayList<V>();
public V put(K key,V value){
V oldValue=get(key);
if(!key.contains(key)){
keys.add(key);
values.add(value);
}else{
values.set(keys.indexOf(key).value);
}
return oldValue;
}
public V get(Object key){
if(!keys.contains(key))
return null;
return values.get(keys.indexOf(key));
}
public Set<Map.Entry<K,V>> entrySet(){
Set<Map.Entry<K,V>> set=new HashSet<Map.Entry<K,V>>();
Iterator<K> ki=keys.iterator();
iterator<V> vi=values.iterator();
while(ki.hasNext())
set.add(new MapEntry<K,V>(ki.next(),vi.next()));
return set;
}
public static void main(String[] args){
SlowMap<String,String> m=new SlowMap<String,String>();
m.putAll(Countries.capitals(15));
System.out.println(m);
System.out.println(m.get('BULGARIA'));
System.out.println(m.entrySet());
}
}/*Output
*///~
46.io流反映基本类型的长度
在书上有个蛮有趣的demo,io流把byte转化为其他基本类型的:
import java.nio.*;
public class ViewBuffers{
public static void main(String[] args){
ByteBuffer bb=ByteBuffer.wrap(
new byte[]{0,0,0,0,0,0,0,'a'});
bb.rewind();
System.out.println("Byte Buffer");
while(bb.hasRemaining())
System.out.print(bb.position()+" ->"+bb.get()+", ");
System.out.println();
CharBuffer cb=((ByteBuffer)bb.rewind()).asCharBuffer();
System.out.println("Char Buffer");
while(cb.hasRemaining())
System.out.print(cb.position()+" ->"+cb.get()+", ");
System.out.println();
FloatBuffer fb=((ByteBuffer)bb.rewind()).asFloatBuffer();
System.out.println("Float Buffer");
while(fb.hasRemaining())
System.out.print(fb.position()+" ->"+fb.get()+", ");
System.out.println();
IntBuffer ib=((ByteBuffer)bb.rewind()).asIntBuffer();
System.out.println("Int Buffer");
while(ib.hasRemaining())
System.out.print(ib.position()+" ->"+ib.get()+", ");
System.out.println();
LongBuffer lb=((ByteBuffer)bb.rewind()).asLongBuffer();
System.out.println("Long Buffer");
while(lb.hasRemaining())
System.out.print(lb.position()+" ->"+lb.get()+", ");
System.out.println();
ShortBuffer sb=((ByteBuffer)bb.rewind()).asShortBuffer();
System.out.println("Short Buffer");
while(sb.hasRemaining())
System.out.print(sb.position()+" ->"+sb.get()+", ");
System.out.println();
DoubleBuffer db=((ByteBuffer)bb.rewind()).asDoubleBuffer();
System.out.println("Double Buffer");
while(db.hasRemaining())
System.out.print(db.position()+" ->"+db.get()+", ");
System.out.println();
}
}/*Output
har Buffer
0 ->, 1 ->, 2 ->, 3 ->a,
Float Buffer
0 ->0.0, 1 ->1.36E-43,
Int Buffer
0 ->0, 1 ->97,
Long Buffer
0 ->97,
Short Buffer
0 ->0, 1 ->0, 2 ->0, 3 ->97,
Double Buffer
0 ->4.8E-322,
*///~
47.io流不同类型读写文件的速度
io流采用装饰者模式,层层装饰。java发展到现在,有”old” io 和”new” io。书里有个测试平台,基于模板方法模式写的,用来测试MappedIO和边准io流DataInputStream/DataOutputStream的速度,结果的比例是差不多的,但是时间根据硬件和操作系统会有所不同:
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
public class MappedIO{
private static int numOfInts=4000000;
private static int numOfUbuffInts=200000;
private abstract static class Tester{
private String name;
public Tester(String name){this.name=name;}
public void runTest(){
System.out.println(name+": ");
try{
long start=System.nanoTime();
test();
double duration=System.nanoTime()-start;
System.out.format("%.2f\n",duration/1.0e9);
}catch(IOException e){
throw new RuntimeException(e);
}
}
public abstract void test() throws IOException;
}
private static Tester[] tests={
new Tester("Stream Write"){
public void test() throws IOException{
DataOutputStream dos=new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream(new File("temp.tmp"))));
for(int i=0;i<numOfInts;i++)
dos.writeInt(i);
dos.close();
}
},
new Tester("Mapped Write"){
public void test() throws IOException{
FileChannel fc=new RandomAccessFile("temp.tmp","rw").getChannel();
IntBuffer ib=fc.map(FileChannel.MapMode.READ_WRITE,0,fc.size()).asIntBuffer();
for(int i=0;i<numOfInts;i++)
ib.put(i);
fc.close();
}
},
new Tester("Stream Read"){
public void test() throws IOException{
DataInputStream dis=new DataInputStream(
new BufferedInputStream(
new FileInputStream("temp.tmp")));
for(int i=0;i<numOfInts;i++)
dis.readInt();
dis.close();
}
},
new Tester("Mapped Read"){
public void test() throws IOException{
FileChannel fc=new FileInputStream(
new File("temp.tmp")).getChannel();
IntBuffer ib=fc.map(FileChannel.MapMode.READ_ONLY,0,fc.size()).asIntBuffer();
while(ib.hasRemaining())
ib.get();
fc.close();
}
},
new Tester("Stream Read/Write"){
public void test() throws IOException{
RandomAccessFile raf=new RandomAccessFile(
new File("temp.tmp"),"rw");
raf.writeInt(1);
for(int i=0;i<numOfUbuffInts;i++){
raf.seek(raf.length()-4);
raf.writeInt(raf.readInt());
}
raf.close();
}
},
new Tester("Mapped Read/Write"){
public void test() throws IOException{
FileChannel fc=new RandomAccessFile(
new File("temp.tmp"),"rw").getChannel();
IntBuffer ib=fc.map(FileChannel.MapMode.READ_WRITE,0,fc.size()).asIntBuffer();
ib.put(0);
for(int i=1;i<numOfUbuffInts;i++)
ib.put(ib.get(i-1));
fc.close();
}
}
};
public static void main(String[] args){
for(Tester test:tests)
test.runTest();
}
}/*Output
Stream Write:
0.92
Mapped Write:
0.05
Stream Read:
0.83
Mapped Read:
0.02
Stream Read/Write:
2.70
Mapped Read/Write:
0.03
*///~
48.文件锁
对于一些共享文件,在多线程中需要保证一致性(类似与jdbc事务)或者不想文件被其他人访问,就需要对文件上锁。下面的例子是锁住部分文件内容的:
import java.nio.*;
import java.nio.channels.*;
import java.io.*;
public class LockingMappedFiles{
static final int LENGTH =0x8FFFFFF;//128M
static FileChannel fc;
public static void main(String[] args) throws IOException{
fc=new RandomAccessFile("test.dat","rw").getChannel();
MappedByteBuffer out=fc.map(FileChannel.MapMode.READ_WRITE,0,LENGTH);
for(int i=0;i<LENGTH;i++)
out.put((byte)'x');
new LockAndModify(out,0,0+LENGTH/3);
new LockAndModify(out,LENGTH/2,LENGTH/2+LENGTH/4);
}
private static class LockAndModify extends Thread{
private ByteBuffer buff;
private int start,end;
LockAndModify(ByteBuffer mbb,int start,int end){
this.start=start;
this.end=end;
mbb.limit(end);
mbb.position(start);
buff=mbb.slice();
start();
}
public void run(){
try{
FileLock fl=fc.lock(start,end,false);
System.out.println("Locked: "+start+" to "+end);
while(buff.position()<buff.limit()-1)
buff.put((byte)(buff.get()+1));
fl.release();
System.out.println("Release: "+start+" to "+end);
}catch(IOException e){
throw new RuntimeException(e);
}
}
}
}/*Output
Locked: 0 to 50331647
Locked: 75497471 to 113246206
Release: 75497471 to 113246206
Release: 0 to 50331647
*///~
49.文件压缩
io流可以将文件压缩,在网络传输中尤为重要,io流可以将文件压缩为GZIP,ZIP,Jar…
1).压缩为jar:
jar cf myJarFile.jar *.class //把该目录下所有的.class文件打包压缩进一个叫myJarFile.jar的jar包里面
jar tf myJarFile.jar //打印出myJarFile.jar文件的所有子文件(.class)
jar tvf myJarFile.jar //与上一句一样,多打印出 myJarFile.jar所有子文件的详细信息(日期,大小,用户等信息)
2).压缩为GZIP/ZIP,下面以GZIP为例
import java.util.zip.*;
import java.io.*;
public class GZIPcompress{
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);
}
}
50.序列化
io流在网络传输中的作用很重要,有些时候,为了减少创建类的开销加快传输速度,需要将对象转化为持久态(类似与J2EE中Hibernate的持久态session)。下面的例子是读取文件时将对象序列化,往后的对文件对该对象就可以直接操作:
import java.util.*;
import java.io.*;
class Data implements Serializable{
private int n;
public Data(int n){this.n=n;}
public String toString(){return Integer.toString(n);}
}
public class Worm implements Serializable{
private static Random rand=new Random(47);
private Data[] d={
new Data(rand.nextInt(10)),
new Data(rand.nextInt(10)),
new Data(rand.nextInt(10))
};
private Worm next;
private char c;
public Worm(int i,char x){
System.out.println("Worm constructor: "+i);
c=x;
if(--i>0)
next=new Worm(i,(char)(x+1));
}
public Worm(){
System.out.println("Default constructor");
}
public String toString(){
StringBuilder result=new StringBuilder(":");
result.append(c);
result.append("(");
for(Data dat : d)
result.append(dat);
result.append(")");
if(next!=null)
result.append(next);
return result.toString();
}
public static void main(String[] args) throws ClassNotFoundException,IOException{
Worm w=new Worm(6,'a');
System.out.println("w= "+w);
ObjectOutputStream out=new ObjectOutputStream(
new FileOutputStream("worm.out"));
out.writeObject("Worm storage\n");
out.writeObject(w);
out.close();
ObjectInputStream in=new ObjectInputStream(
new FileInputStream("worm.out"));
String s=(String)in.readObject();
Worm w2=(Worm)in.readObject();
System.out.println(s+" w2= "+w2);
ByteArrayOutputStream bout=new ByteArrayOutputStream();
ObjectOutputStream out2=new ObjectOutputStream(bout);
out2.writeObject("Worm storage\n");
out2.writeObject(w);
out2.flush();
ObjectInputStream in2=new ObjectInputStream(
new ByteArrayInputStream(bout.toByteArray()));
s=(String)in2.readObject();
Worm w3=(Worm)in2.readObject();
System.out.println(s+" w3= "+w3);
}
}/*Output
Worm constructor: 6
Worm constructor: 5
Worm constructor: 4
Worm constructor: 3
Worm constructor: 2
Worm constructor: 1
w= :a(853):b(119):c(802):d(788):e(199):f(881)
Worm storage
w2= :a(853):b(119):c(802):d(788):e(199):f(881)
Worm storage
w3= :a(853):b(119):c(802):d(788):e(199):f(881)
*///~
但是一些时候,有些机密信息不想轻易被其他文件读取修改,可以通过transient关键字指明不被序列化,比如登录中的密码:
import java.util.concurrent.*;
import java.io.*;
import java.util.*;
public class Logon implements Serializable{
private Date date=new Date();
private String username;
private transient String password;
public Logon(String name,String pwd){
username=name;
password=pwd;
}
public String toString(){
return "logon info: \n username: "+username+
"\n date: "+date+"\n password: "+password;
}
public static void main(String[] args) throws Exception{
Logon a=new Logon("Hulk","myLittlePony");
System.out.println("Logon a="+a);
ObjectOutputStream o=new ObjectOutputStream(
new FileOutputStream("Logon.out"));
o.writeObject(a);
o.close();
TimeUnit.SECONDS.sleep(1);
ObjectInputStream in=new ObjectInputStream(
new FileInputStream("Logon.out"));
a=(Logon)in.readObject();
System.out.println("Logon a="+a);
}
}/*Output
Logon a=logon info:
username: Hulk
date: Mon Aug 08 17:14:34 CST 2016
password: myLittlePony
Logon a=logon info:
username: Hulk
date: Mon Aug 08 17:14:34 CST 2016
password: null
*///~