四.(上)常用包,引用类型,克隆
基本内容:
Java的常用包
“==”和“equals”的用法
基本数据类型与引用类型
String和StringBuffer
对象的克隆(clone)
数组的相关操作
封装类
Runtime类与Process类
Class类
设计模式
1。JAVA的常用包:
java.applet:包含一些用于创建Java小应用程序(浏览器中的小程序)的类。
java.awt:包含一些用于编写与平台无关的图形界面(GUI)应用程序的类。
java.io:包含一些用作输入输出(I/O)处理的类。
java.lang:包含一些Java语言的基本类与核心类,如String、Math、Integer、System和Runtime,提供常用的功能,这个包中的所有类是被隐式导入的(自动导入,不需要Import)。
java.net:包含用于建立网络连接的类,与java.io同时使用完成与网络有关的读写。
java.util:包含一些实用工具类和数据结构类。
2.== 和equals的区别
在Java中,boolean、byte、short、int、long、char、float、double这八种是基本数据类型,其余的都是引用类型。 --- String 和数组都是引用类型!
“==”是比较两个变量的值是否相等(对于引用类型,即比较引用的变量是否是同一个),“equals”是比较两个对象变量所代表的对象的内容是否相等。
string是引用类型,所以==比的是变量中 所存的地址是否相同;
String str1 = new String("abc");
String str2 = new String("abc");
注:凡是用new的地方都是在堆内存中分配空间
str1 == str2 // false
str1.equals(str2) //true
当我们声明一个引用类型变量时,系统只为该变量分配了引用空间,并未创建
一个具体的对象;当用new为对象分配空间后,将对象的引用赋值给引用变量。
String str=“abc”;
int i=3;
float f=4.5f;
char ch='a';
boolean b=true;
System.out.println(str + i + f + ch + b);
针对String的“+”和“+=”,是Java中唯一被重载的操作符;在Java中,不允许程序员重载操作符。
String可以与int、char、bool等相加
String类对象一个常量对象。
String str=“abc”;
str=“def”;
在处理大量字符串的程序中,我们通常用StringBuffer来替代String。
3.StringBuffer
String是个常量,the value can not be changed.
StringBuffer -- 可以对同一块内存进行操作,可以被修改,不会造成内存浪费!
int i=3;
float f=1.5f;
char ch='f';
boolean b=false;
System.out.println(str1+i+f+ch+b);
StringBuffer sb=new StringBuffer();
sb.append(str1).append(i).append(f).append(ch).append(b);
//System.out.println(sb.toString());
System.out.println(sb); //在打印语句中系统会自动调用toString()转换成string
StringBuffer()默认容量是16 characters,超过了之后系统会自动增加;
StringBuffer(int n)
StringBuffer.Delete(4,8); //删除部分串,位置:4<=substr<8, 所以删除4,5,6,7 四个位置的字符;
4.数组
int[] num=new int[3];
for(int i=0;i<num.length;i++)
{
System.out.println(num[i]);
}
num=null; //使引用失效,变成垃圾内存,让jvm回收;
类对象数组,二层引用的关系;初始值为三个null;
/*Student[] students; //声明数组
students=new Student[3]; //三个对象
for(int i=0;i<students.length;i++)
{
System.out.println(students[i]);
}*/
Student s1=new Student("zhangsan",18,p);
Student s2=(Student)s1.clone();
/*s2.name="lisi";
s2.age=20;*/
s2.p.name="lisi";
s2.p.age=30;
//System.out.println("name="+s1.name+","+"age="+s1.age);
System.out.println("name="+s1.p.name+","+"age="+s1.p.age);
}
}
5.main 函数
main由JVM调用,所以是public的;调用它的时候不用产生任何对象,所以是static的;
JVM也无需返回值;
参数是个String[]类型的;
args 是用来接收命令行参数的; 如 java StringTest weixin mybole
6.函数调用:
不用第三个变量,交换两个变量值:
public static void change(int x,int y)
{
x=x+y;
y=x-y;
x=x-y;
} //调用后并没有改变;
在Java中,传参时,都是以传值的方式进行。
对于基本数据类型,传递的是数据的拷贝;对于引用类型,传递的引用的拷贝。
所以数组和对象作为参数进行change操作能够成功改变实际内容!
Point pt(3,4); //定义对象
System.out.println(pt); //直接打印对象(会调用toString方法);打印出类名+@+一个哈西值;
所以想直接打印pt对象,可以重新定义Object的toString方法,
class Point
{
int x,y;
public String toString()
{
return "x="+x+","+"y="+y;
}
}
再使用System.out.println(pt);的时候就可以直接打印出对象成员的内容了! --- 好方法!
7.对象的克隆(clone)
使用方法时如果不想改变引用对象本身时,可以用clone
为了获取对象的一份拷贝,我们可以利用Object类的clone()方法。
在派生类中覆盖基类的clone()方法,并声明为public。
在派生类的clone()方法中,调用super.clone()。
在派生类中实现Cloneable接口。
Cloneable --- 表示接口,没有抽象方法,仅仅是为了告诉该对象可以克隆了。
浅拷贝:对象中基本数据类型得到拷贝,而引用数据类型并未拷贝。
class Student implements Cloneable //空实现该接口,代表该对象可以克隆;
{
String name;
int age;
public Object clone()
{
Object o=null;
try
{
o=super.clone();
}
catch(CloneNotSupportedException e) //会抛出异常
{
System.out.println(e.toString());
}
return o;
}
} //浅克隆 ---- 无成员对象! 只进行值拷贝!
但是,当克隆时候,只copy对象成员的值,但是对引用成员(如成员对象),它只copy一个引用;所以对clone成员修改的时候也会将源对象的成员对象内容修改了!!
String对象是一个常量对象,所以进行s2.name="lisi"时,是先new了一个“lisi”然后赋的值;
深克隆:实现对象中基本数据类型和引用数据类型的拷贝。
为什么我们在派生类中覆盖Object的clone()方法时,一定要调用super.clone()呢?在运行时刻,Object中的clone()识别出你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。
Student 成员对像有个professor
class Professor implements Cloneable
{
String name;
int age;
Professor(String name,int age)
{
this.name=name;
this.age=age;
}
public Object clone()
{
Object o=null;
try
{
o=super.clone();
}
catch(CloneNotSupportedException e)
{
System.out.println(e.toString());
}
return o;
}
}
class Student implements Cloneable
{
String name;
int age;
Professor p;
Student(String name,int age,Professor p)
{
this.name=name;
this.age=age;
this.p=p;
}
public Object clone()
{
//Object o=null;
Student o=null;
try
{
o=(Student)super.clone();
}
catch(CloneNotSupportedException e)
{
System.out.println(e.toString());
}
o.p=(Professor)p.clone();
return o;
}
}
四.(下)数组Runtime类与Process类
1. 数组的相关操作
在Java中,所有的数组都有一个缺省的属性length,用于获取数组中元素的个数。
数组的复制:System.arraycopy()。
数组的排序:Arrays.sort()。 --- java.util包之中!
在已排序的数组中查找某个元素:Arrays.binarySearch()。
import java.util.Arrays;
length是一个属性,不是方法;不要加()!!
arr1.length
对象数组的初始化方法:
Point[] pts1 = new Point[]{new Point(1,1),new Point(2,2), new Point (3,3)};
Point[] pts2 = new Point[3];
arraycopy对与对象数组的copy实际上是一个引用copy,数组里面存的是对象的地址,所以对第二个数组对象进行修改操作时还是会改变第一个数组的元素(操作的是同一组对象)!
b.排序
int num[] = new int[]{3,1,2};
Arrays.sort(num); //直接调用,直接传参数;无返回值
c.查找 (返回所查找元素的位置)
int index = Array.binarySearch(num,3);
d.对象的排序
return num>s.num ? 1 : (num==s.num ? 0 : -1); //问号表达式,大于返回1,=返0,<返-1;
对于Arrays.sort()对类对象数组的排序,该类必须实现Comparable接口,
在类中重写compareTo()方法,实现对象之间的比较,如:
class Student implements Comparable
{
int num;
String name;
Student(int num,String name)
{
this.num=num;
this.name=name;
}
public String toString() //用来打印对象
{
return "number="+num+","+"name="+name;
}
public int compareTo(Object o) //接口函数的实现
{
Student s=(Student)o; //转换成该对象
return num>s.num ? 1 : (num==s.num ? 0 : -1); //!!!
}
}
方法2: 当序号相同时再按名字排序:
public int compareTo(Object o)
{
Student s=(Student)o;
//return num>s.num ? 1 : (num==s.num ? 0 : -1);
int result=num>s.num ? 1 : (num==s.num ? 0 : -1);
if(0==result) //序号相同,则比较name;String类已经实现了compareTo
{
result=name.compareTo(s.name);
}
return result;
}
e.数组对象的搜索:
int index=Arrays.binarySearch(ss,new Student(2,"lisi"));
System.out.println("index="+index);
System.out.println(ss[index]);
2.封装类:
调用函数时,参数是引用类型,但我们要传递的又是基本类型;
java.lang中提供了八种基本类型的引用类型--封装类!
基本数据类型 封装类
boolean Boolean
byte Byte
short Short
int Integer
long Long
char Character
float Float
double Double
class Test
{
public static void main(String[] args)
{
int i=3;
Integer in=new Integer(i); //取得封装类
int j=in.intValue(); //取回整形值
System.out.println("j="+j);
String str=in.toString(); //Integer.toString()可以将Integer转换为String
System.out.println("str="+str);
String str2="134";
System.out.println(Integer.valueOf(str2)); //Integer.valusOf()将String转换成Integer
}
}
parseInt(String s) -- 转换为基本数据类型int
toString 也支持基本数据类型int!
其它基本类型也都有类似的;
所有的封装类都是只读类!
3.Class类 -- java.lang
在Java中,每个class都有一个相应的Class对象。也就是说,当我们编写一个类,编译完成后,在生成的.class文件中,就会产生一个Class对象,用于表示这个类的类型信息。
获取Class实例的三种方式:
(1)利用对象调用getClass()方法获取该对象的Class实例; -- Object的方法!
Point pt=new Point();
Class c1=pt.getClass();
System.out.println(c1.getName()); //getName得到一个String
(2)使用Class类的静态方法forName(),用类的名字获取一个Class实例;
Class c2=Class.forName("Point"); //会抛出异常
System.out.println(c2.getName());
(3)运用.class的方式来获取Class实例,对于基本数据类型的封装类,还可以采用.TYPE来获取相对应的基本数据类型的Class实例。
Class c3=Point.class;
System.out.println(c3.getName());
基本数据类型:
Class c4=int.class;
System.out.println(c4.getName());
封装类:
Class c5=Integer.TYPE;
System.out.println(c5.getName());
在运行期间,如果我们要产生某个类的对象,Java虚拟机(JVM)会检查该类型的Class对象是否已被加载。如果没有被加载,JVM会根据类的名称找到.class文件并加载它。一旦某个类型的Class对象已被加载到内存,就可以用它来产生该类型的所有对象。
newInstance() 调用类中缺省的构造方法。 -- 可以用来在不知道一个类的名字的时候创建它的实例!
if(args.length!=1)
{
return;
}
try
{
Class c=Class.forName(args[0]);
Point pt=(Point)c.newInstance();
pt.output();
}
catch(Exception e)
{
e.printStackTrace();
}
4.反射API
用来表示或反射类、接口和对象在当前的JVM上;
import java.lang.*; //不能导入lang的子包,必须增加:
import java.lang.reflect.*;
java.lang.class.isPrimitive()用来判断是否是基本数据类型!
反射API 动态地创建了类的实例,动态地使用类的方法!
尽量不要用反射API当能够以别的工具实现时。
调试器,类的浏览器,图形界面的类的构建器的时候可能会用到反射API;
5.runtime类
每一个Java程序都有一个Runtime类的单一实例。
通过Runtime.getRuntime()获取Runtime类的实例。 //无构造方法!
Runtime类是使用单例模式的一个例子。
成为了一个程序和JVM的接口,提供了应用程序和环境之间的接口!
public static void main(String[] args)
{
Runtime rt=Runtime.getRuntime(); //得到一个runtime实例
System.out.println(rt.freeMemory()); //查看当前空闲内存和总内存!
System.out.println(rt.totalMemory());
}
exec()方法可以来执行外部程序;会throw exception
try
{
rt.exec("notepad"); //成为当前进程的子进程
}
catch(Exception e)
{
e.printStackTrace();
}
也可以用来编译源文件:
rt.exec("javac ArrayTest.java");
为了更好看到执行过程: 定义一个Process类:
Process类: 抽象类
Process p=rt.exec("javac ArrayTest.java"); //获取了这个子进程;
InputStream is=p.getInputStream();
int data;
while((data=is.read())!=-1)
{
System.out.print((char)data);
}
Runtime类是使用单例模式的一个例子。
6.设计模式
在我们进行程序设计时,逐渐形成了一些典型问题和问题的解决方案,这就是软件模式。
每一个模式描述了一个在我们程序设计中经常发生的问题,以及该问题的解决方案。
当我们碰到模式所描述的问题,就可以直接用相应的解决方法去解决这个问题,这就是设计模式。
对于JAVA语言的学习:
1.语法规则和它本身的优越性(相对于其它语言)
2.解决方法时的一些前人的解法的经验
a.单例模式
(1)一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类。
(2)单例类的一个最重要的特点是类的构造方法是私有的,从而避免了外部利用构造方法直接创建多个实例。
实现:
class Singleton
{
private static final Singleton st=new Singleton();
private Singleton(){} //必须自己写了私有构造,才是单例模式
public static Singleton getInstance()
{
return st;
}
}
主要用于解决什么问题呢?
只需要一个实例时...
设计模式推荐书:《java与模式》阎宏 电子工业
五. 程序,进程,线程
1.程序,进程,线程
程序是计算机指令的集合,它以文件的形式存储在磁盘上。
进程:是一个程序在其自身的地址空间中的一次执行活动。
进程是资源申请、调度和独立运行的单位,因此,它使用系统中的运行资源;而程序不能申请系统资源,不能被系统调度,也不能作为独立运行的单位,因此,它不占用系统的运行资源。
线程:是进程中的一个单一的连续控制流程。一个进程可以拥有多个线程。
线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信远较进程简单。
操作系统来控制多线程的时间片轮询执行;
当程序被移植到多CPU的平台下,就能做到真正的多线程并发执行!
当需要完成多任务的程序时,采用多线程要比多进程高效的多!
2.JAVA对多线程的支持
Java在语言级提供了对多线程程序设计的支持。
实现多线程程序的两种方式:
(1)从Thread类继承;
(2)实现Runnable接口。
java.lang.thread
main方法默认是单线程非后台的;
Thread.currentThread().getName(); //获取当前线程的名字
There are two ways to create a new thread of execution. One is to declare a class to be a subclass of Thread. This subclass should override the run() method of class Thread.
启动线程start();
run方法可以理解为线程的入口函数;
class MultiThread
{
public static void main(String[] args)
{
MyThread mt = new MyThread();
mt.start();
System.out.println("main:"+Thread.currentThread().getName();
}
}
class MyThread extends Thread
{
public void run()
{
System.out.println(getName());
}
}
setDaemon() //设为后台进程,在调用之前使用
yield() //暂停当前线程,让其它线程执行(一个时间片)
setPriority() //设置线程的优先级; 常量MAX/MIN/NORM_PRIORITY: 10/1/5
getPriority()
class MultiThread
{
public static void main(String[] args)
{
MyThread mt=new MyThread();
//mt.setDaemon(true); //设置为后台线程
//mt.setPriority(Thread.MAX_PRIORITY); //设为高优先级,也可以在它运行之后设置
mt.start();
int index=0;
while(true)
{
if(index++==1000)
break;
System.out.println("main:"+Thread.currentThread().getName());
}
}
}
class MyThread extends Thread
{
public void run()
{
while(true)
{
System.out.println(getName());
//yield(); //让其它线程先执行
}
}
}
3.Java对多线程的支持
Java运行时系统实现了一个用于调度线程执行的线程调度器,用于确定某一时刻由哪一个线程在CPU上运行。
在java技术中,线程通常是抢占式的而不需要时间片分配进程(分配给每个线程相等的CPU时间的进程)。抢占式调度模型就是许多线程处于可以运行状态(等待状态),但实际上只有一个线程在运行。该线程一直运行到它终止进入可运行状态(等待状态),或者另一个具有更高优先级的线程变成可运行状态。在后一种情况下,低优先级的线程被高优先级的线程抢占,高优先级的线程获得运行的机会。
Java线程调度器支持不同优先级线程的抢先方式,但其本身不支持相同优先级线程的时间片轮换。
Java运行时系统所在的操作系统(例如:Windows2000)支持时间片的轮换,则线程调度器就支持相同优先级线程的时间片轮换。
4.线程的第二种实现方法就是实现runable接口;
.runable接口只有一个方法就是run()
class MultiThread
{
public static void main(String[] args)
{
MyThread mt = new MyThread();
new Thread(mt).start(); //good way!!!
int index=0;
while(true)
{
System.out.println("main:"+Thread.currentThread().getName());
}
}
}
class MyThread implements Runnable
{
public void run()
{
while(true)
{
System.out.println(Thread.currentThread().getName());
} //不是继承的Thread类,所以不能直接用getName()方法了
}
}
什么时候用那个方法实现线程?
--如果说我们并不需要修改Thread的除了run之外的其它方法时,最好用Runable接口的方法;
两个好处:
--不允许多继承,所以子类用Runable好
--Runable接口可以共享一个变量;
new Thread(mt).start();
new Thread(mt).start();
new Thread(mt).start();
new Thread(mt).start();
除了Runable外,也可以用内部类来实现(外部类是普通类):
int index = 0; // 成员变量;
private class InnerThread extends Thread
{
public void run()
{
while(true)
{
System.out.println(Thread.currentThread ().getName());
}
Thread getThread() //该类成员方法用来生成内部类
{
new InnerThread();
}
}
}
主函数中用
来多线程共享一个对象变量:
mt.getThread().start();
mt.getThread().start();
mt.getThread().start();
mt.getThread().start(); //启动多个线程共享变量;
5.线程的同步
The code segments within a program that access the same object from separate, concurrent threads are called “critical sections”。
同步的两种方式:同步块和同步方法
每一个对象都有一个监视器,或者叫做锁。
同步方法利用的是this所代表的对象的锁。
每个class也有一个锁,是这个class所对应的Class对象的锁。
a.同步块用synchronized实现:
联网售票系统的实现: --- 设定了同步保护块
class Ticket
{
public static void main(String[] args)
{
SellThread st = new SellThread();
new Thread(st).start(); // Thread(Runnable target) Allocates a new Thread object.
new Thread(st).start();
new Thread(st).start();
new Thread(st).start(); //不能new 4个对象,只能new 4个线程;
}
}
class SellThread implements Runnable
{
int tickets = 100;
Object bj = new Object(); //for synchronized use
public void run()
{
while(true)
{
synchronized(obj) //进程进入后给obj加锁,退出后再解锁
{
if(tickets>0)
{
//try{ Thread.sleep(10); } //让线程睡眠10毫秒,造成资源的重复读取;
//catch(Exception e){ e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+ " sell tickets:"+tickets); //获取当前线程
tickets--;
}
}
}
}
}
b.同步方法:
public synchronized void sell(){....}
不需要object,是给this对象加锁!
如果是静态的同步方法,则用的是class类的锁;
6. 线程的死锁:
哲学家进餐的问题
线程1锁住了对象A的监视器,等待对象B的监视器,线程2锁住了对象B的监视器,等待对象A的监视器,就造成了死锁。
Thread的suspend() 和 resume()方法容易造成死锁;
7.生产者,消费者问题:
wait、notify、notifyAll
每一个对象除了有一个锁之外,还有一个等待队列(wait set),当一个对象刚创建的时候,它的对待队列是空的。
我们应该在当前线程锁住对象的锁后,去调用该对象的wait方法。
当调用对象的notify方法时,将从该对象的等待队列中删除一个任意选择的线程,这个线程将再次成为可运行的线程。
当调用对象的notifyAll方法时,将从该对象的等待队列中删除所有等待的线程,这些线程将成为可运行的线程。
wait和notify主要用于producer-consumer这种关系中。 -- 必须放在同步方法或者同步块之中!
-- 此wait和notify方法必须调用的是同一个同步对象!
class Test
{
public static void main(String[] args)
{
Queue q = new Queue();
Producer p= new Producer(q);
Consumer c=new Consumer(q);
p.start();
c.start();
}
}
class Producer extends Thread
{
Queue q;
Producer(Queue q)
{
this.q = q;
}
public void run()
{
for (int i=0;i<10;i++)
{
q.put(i);
System.out.println("Producer put "+i);
}
}
}
class Consumer extends Thread
{
Queue q;
Consumer(Queue q)
{
this.q=q;
}
public void run()
{
while(true)
{
System.out.println("Consumer get "+q.get());
}
}
}
class Queue
{
int value;
boolean bFull=false;
public synchronized void put (int i)
{
if(!bFull)
{
value = i;
bFull = true;
notify(); //通知消费者
}
try{wait();} //等待消费
catch(Exception e){e.printStackTrace();}
}
public synchronized int get()
{
if(!bFull)
{
try{wait();} //等待生产者
catch(Exception e){e.printStackTrace();}
}
bFull=false;
notify(); //通知生产
return value;
}
}
线程的状态:
调用了wait()的线程必须由 notify()来唤醒! 或者由interrupt()方法来终止!
线程的终止:
a。设置一个flag变量;
b。结合interrupt()方法;
六.集合类;栈、对列、链表
1.集合类
集合框架 --- JDK doc:guide
Introduction of collections framework:
The Java 2 platform. includes a collections framework. A collection is an object that represents a group of objects (such as the classic Vector class). A collections framework is a unified architecture for representing and manipulating collections, allowing them to be manipulated independently of the details of their representation.
a.集合框架中的接口:
Collection:集合层次中的根接口,JDK没有提供这个接口直接的实现类。
Set:不能包含重复的元素。SortedSet是一个按照升序排列元素的Set。
List:是一个有序(不是排序,而是指元素按照一定的次序摆放)的集合,可以包含重复的元素。提供了按索引访问的方式。
Map:包含了key-value对。Map不能包含重复的key。SortedMap是一个按照升序排列key的Map。
以上几个接口都在util包中:JAVA.util.
篮筐为接口,白框为实现类;
2.ArrayList -- 后台是用对象数组实现的!
jdk/src.zip --解压缩后看源代码 java/util/ArrayList.java
ArrayList:我们可以将其看作是能够自动增长容量的数组。-- 与对象数组的区别!
成员方法有: add(),size()--元素数目,get();
例子:
import java.util.*;
class ArrayListTest
{
public static void main(String[] args)
{
ArrayList al=new ArrayList();
al.add("winsun"); //成员方法add增加成员;
al.add("weixin");
al.add("mybole");
for(int i=0;i<al.size();i++) //size()相当于数组的length属性;
{
System.out.println(al.get(i));
}
System.out.println(al); //自动调用toString方法;
}
}
利用ArrayList的toArray()返回一个对象数组。 --- 所以它们二者是不同的!
例子:
import java.util.*;
class ArrayListTest
{
public static void main(String[] args)
{
ArrayList al=new ArrayList();
al.add(new Point(1,1));
al.add(new Point(2,2));
al.add(new Point(3,3));
System.out.println(al); //自动调用toString方法;
Object[] bjs=al.toArray(); //toArray()返回一个对象数组
for(int i=0;i<objs.length;i++)
{
System.out.println(objs[i]);
}
}
}
class Point
{
int x,y;
Point(int x,int y)
{
this.x=x;
this.y=y;
}
public String toString()
{
return "x= "+x+","+" y= "+y;
}
}
Arrays.asList()返回一个列表。
List l=Arrays.asList(objs); //列表大小是确定了的;
System.out.println(l);
3.迭代器
迭代器(Iterator) 给我们提供了一种通用的方式来访问集合中的元素。 Collection.iterator()
有三个方法:hasNext,next,remove();
next()返回一个值,remove删除上一个返回的值;
Iterator it=al.iterator();
while(it.hasNext())
{System.out.println(it.next());}
remove()使用方法:
Iterator it=al.iterator();
it.next(); //必须先返回一个再删除
it.remove();
while(it.hasNext())
System.out.println(it.next());
集合框架中的实现类支持迭代器的所有方法;
但是asList返回的列表没有实现迭代器的remove方法!
迭代器的作用:
1.有些对象没有实现get方法;
2.提供了返回一个元素的一种通用的操作方法;
定义统一函数:
public static void printElements(Collection c) //参数类型是Collection,所有它的子类、实现类都可以用此函数;
{
Iterator it = c.iterator(); //!! 用方法iterator()生成迭代器对象
while(it.hasNext())
{
System.out.println(it.next())
}
}
调用的时候可以传递一个ArrayList对象;
printElements(al); 或者其它集合对象;
4.Collections 类; 不是Collection接口,主要对列表进行操作
它提供了一系列有用的静态方法,如排序! 所以可以直接拿来用,不用定义对象!
排序:Collections.sort()
(1)自然排序(natural ordering );
(2)实现比较器(Comparator)接口。
取最大和最小的元素:Collections.max()、Collections.min()。
在已排序的List中搜索指定的元素:Collectons.binarySearch()。
排序必须实现Comparable接口,该接口中的函数为:
int compareTo(object o); 返回1,0, -1
使用Collection类排序方法对数组列表排序并使用迭代器统一输出的例子如下:
import java.util.*;
class Test
{
public static void printElements(Collection c)
{
Iterator it = c.iterator(); //!! 用方法iterator()生成迭代器对象
while(it.hasNext())
{
System.out.println(it.next());
}
}
public static void main(String[] args)
{
Student s1=new Student(2,"zhangsan");
Student s2=new Student(4,"lisi");
Student s3=new Student(1,"wangwu");
Student s4=new Student(3,"zhaoliu");
ArrayList al = new ArrayList();
al.add(s1);
al.add(s2);
al.add(s3);
al.add(s4);
Collections.sort(al); //静态类方法直接调用!
printElements(al); //形参为Collection;自动调用类的toString
}
}
class Student implements Comparable
{
int num;
String name;
Student(int i,String name)
{
num=i;
this.name=name;
}
public int compareTo(Object o) //实现接口中的public函数,需加public
{
Student st=(Student) o;
return num>st.num ? 1 :(num==st.num ? 0 : -1);
}
public String toString()
{
return num+":"+name;
}
}
5.比较器排序
在对列表排序的时候可以传递一个比较器comparator;
sort(List ls, Comparator cp)
比较器总是和一个类相关;
比较器接口有两个方法:
compare()
equals()
所以也可以用内部类实现:StudentComparator
static class StudentComparator implements Comparator
{
public int compare(Object o1,Object o2)
{
Student s1=(Student)o1;
Student s1=(Student)o1;
return s1.num>s2.num ? 1 : (s1.num==s2.num ? 0 : -1);
}
}
String类型已经实现了compareTo方法:
result = s1.name.comareTo(s2.name); //0,1,-1;
6.Collections类的反序排序 -- static Comparator reverseOrder()
返回的就是比较器对象;
Collections.sort(al, Collections.reverseOrder());
排序:Collections.sort()
(1)自然排寻(natural ordering );
(2)实现比较器(Comparator)接口。
取最大和最小的元素:Collections.max()、Collections.min()。
在已排序的List中搜索指定的元素:Collectons.binarySearch()。
7.LinkedList
LinkedList是采用双向循环链表实现的。
利用LinkedList实现栈(stack)、队列(queue)、双向队列(double-ended queue )。
数据结构:
一般将数据结构分为两大类:线性数据结构和非线性数据结构。线性数据结构有线性表、栈、队列、串、数组和文件;非线性数据结构有树和图。
8.线性表
线性表的逻辑结构是n个数据元素的有限序列:
(a1, a2 ,a3,…an)
n为线性表的长度(n≥0),n=0的表称为空表。
数据元素呈线性关系。必存在唯一的称为“第一个”的数据元素;必存在唯一的称为“最后一个”的数据元素;除第一个元素外,每个元素都有且只有一个前驱元素; 除最后一个元素外,每个元素都有且只有一个后继元素。
所有数据元素在同一个线性表中必须是相同的数据类型。
线性表按其存储结构可分为顺序表和链表。用顺序存储结构存储的线性表称为顺序表;用链式存储结构存储的线性表称为链表。
将线性表中的数据元素依次存放在某个存储区域中,所形成的表称为顺序表。一维数组就是用顺序方式存储的线性表。
9.链表:单向链表,循环链表,双向循环链表
链表中一个节点删除后,是让前一个节点指向了后一个节点,该节点就变成了一块垃圾内存.
10.栈 -- 后进先出
栈(Stack)也是一种特殊的线性表,是一种后进先出(LIFO)的结构。
栈是限定仅在表尾进行插入和删除运算的线性表,表尾称为栈顶(top),表头称为栈底(bottom)。
栈的物理存储可以用顺序存储结构,也可以用链式存储结构
LinkedList有getfirst,getlast, addfirst,addlast等方法
用LinkedList来实现栈:
import java.util.*;
class MyStack //一个LinkedList成员加出入栈方法等
{
private LinkedList ll = new LinkedList();
public void push(Object o) //形参元素用Object对象,实参可以任意对象
{
ll.addFirst(o);
}
public Object pop()
{
return ll.removeFirst();
}
public Object peek() //查看栈定元素
{
return ll.getFirst();
}
public boolean empty() //栈是否空
{
return ll.isEmpty(); //Collection方法
}
public static void main(String[] args)
{
MyStack ms = new MyStack();
ms.push("one");
ms.push("two");
ms.push("three");
System.out.println(ms.pop()); //three
System.out.println(ms.peek()); //two
System.out.println(ms.pop()); //two
System.out.println(ms.empty()); //false
}
}
11.队列(Queue) -- 先进先出FIFO
队列(Queue)是限定所有的插入只能在表的一端进行,而所有的删除都在表的另一端进行的线性表。
表中允许插入的一端称为队尾(Rear),允许删除的一端称为队头(Front)。
队列的操作是按先进先出(FIFO)的原则进行的。
队列的物理存储可以用顺序存储结构,也可以用链式存储结构。
用LinkedList可类似实现队列。
12.ArrayList和LinkedList的比较
ArrayList底层采用数组完成,而LinkedList则是以一般的双向链表(double-linked list)完成,其内每个对象除了数据本身外,还有两个 引用,分别指向前一个元素和后一个元素。
如果我们经常在List的开始处增加元素,或者在List中进行插入和删除操作,我们应该使用LinkedList,否则的话,使用ArrayList将更加快速。
如果在数组开头插入一个数据,则需要移动整个数组,删除操作也需要移动其后所有数据;
所以,常插入和删除用LinkedList,常随机访问则用ArrayList!
13.HashSet
实现Set接口的hash table(哈希表),依靠HashMap来实现的。-- set中没有重复元素!
我们应该为要存放到散列表的各个对象定义hashCode()和equals()。
14.散列表(哈希表)
散列表又称为哈希表。散列表算法的基本思想是:
以结点的关键字为自变量,通过一定的函数关系(散列函数)计算出对应的函数值,以这个值作为该结点存储在散列表中的地址。
当散列表中的元素存放太满,就必须进行再散列,将产生一个新的散列表,所有元素存放到新的散列表中,原先的散列表将被删除。在Java语言中,通过负载因子(load factor)来决定何时对散列表进行再散列。例如:如果负载因子是0.75,当散列表中已经有75%的位置已经放满,那么将进行再散列。
负载因子越高(越接近1.0),内存的使用效率越高,元素的寻找时间越长。负载因子越低(越接近0.0),元素的寻找时间越短,内存浪费越多。
HashSet类的缺省负载因子是0.75。
HashSetTest.JAVA:
import java.util.*;
class HashSetTest
{
public static void main(String[] args)
{
HashSet hs = new HashSet();
hs.add("one"); //add方法来增加元素
hs.add("two");
hs.add("three");
hs.add("one"); //failed,实现了Set接口,所以不能有重复元素
//HashSet没有get方法,所以用Iterator来实现
Iterator it = hs.iterator();
while(it.hasNext())
{
System.out.println(it.next()); //三个成员
}
}
}
散列地址是由Object.hashCode()方法实现的,而它是取对象的存储地址来计算的;所以当new了两个相同对象时,HashSet是把它们当作不同的值了;
public int hashCode() //equals也得同时自己定义
{
return num*name.hashCode(); //String实现了hashCode
}
15.TreeSet
TreeSet是依靠TreeMap来实现的。
TreeSet是一个有序集合,TreeSet中元素将按照升序排列,缺省是按照自然顺序进行排列,意味着TreeSet中元素要实现Comparable接口。
我们可以在构造TreeSet对象时,传递实现了Comparator接口的比较器对象。
Sample for TreeSet:
import java.util.*;
class TreeSetTest
{
public static void main(String[] args)
{
TreeSet ts = new TreeSet();
ts.add("winsun");
ts.add("weixin");
ts.add("mybole");
Iterator it = ts.iterator();
while(it.hasNext())
{
System.out.println(it.next());
}
}
} //如果成员是类对象,则该类必须实现了Comparable接口;
HashSet和TreeSet的比较
HashSet是基于Hash算法实现的,其性能通常都优于TreeSet。我们通常都应该使用HashSet,在我们需要排序的功能时,我们才使用TreeSet。
16.HashMap
.HashMap就是实现了Map接口的Hash表; key,value
import java.util.*;
class HashMapTest
{
public static void main(String[] args)
{
HashMap hm=new HashMap();
hm.put("one","zhangsan"); //Key, value;
hm.put("two","lisi");
hm.put("three","wangwu");
System.out.println(hm.get("one"));
System.out.println(hm.get("two"));
System.out.println(hm.get("three"));
}
}
HashMap的方法
keySet() -- 返回键
valueS(),-- 返回值
entrySet() -- 返回键值对;
Set keys=hm.keySet();
System.out.println("Key:");
printElements(keys);
Collection values=hm.values();
System.out.println("Value:");
printElements(values);
Set entry=hm.entrySet();
printElements(entry);
或者将entrySet()返回的对象(类型是Map.Entry)进行单独处理:
Set entry=hm.entrySet();
Iterator it=entry.iterator();
while(it.hasNext())
{
Map.Entry me=(Map.Entry)it.next();
System.out.println(me.getKey()+":"+me.getValue());
}
17. TreeMap
TreeMap按照key进行排序。
HashMap和TreeMap的比较
和Set类似,HashMap的速度通常都比TreeMap快,只有在需要排序的功能的时候,才使用TreeMap。
18.Java1.0/1.1的集合类
Vector:用ArrayList代替Vector。-- 单线程总是用ArrayList,多线程时Vector稍微快一些,但使用要非常小心,可以用synchronizedList代替Vector;
当需要用线程时候可以用Collections类提供的同步列表:
synchronizedList(List<T> list) //also for Map/Set
Returns a synchronized (thread-safe) list backed by the specified list.
Hashtable:用HashMap代替Hashtable。-- Hashtable是同步的,HashMap不是同步的(但可以使用synchronizedMap实现)
只要不是多线程情况,就要始终使用HashMap来代替HashTable!
Satck:用LinkedList代替Stack。-- 是从Vector继承而来的,继承了一个elementAt()方法从而有了按索引访问的权限 -- 与栈属性不符! 所以应该自己用LinkedList实现栈!
Properties -- 它从HashTable继承而来,属性列表的键和值都是String类型的;
列出系统属性:
import java.util.*;
class PropTest
{
public static void main(String[] args)
{
Properties pps = System.getProperties();
pps.list(System.out);
}
}
这个blog对缩进显示的不好,代码看起来费劲一些;另外笔记中一些截图也看不到了,大家凑合着看看吧,希望对大家有所帮助! sonwaves