什么是进程?
什么是线程?
进程与线程之间的区别:
线程使用的场合:
创建方式一:
public class Test {
public static void main(String[] args) {
Thread1 thread1 = new Thread1();
Thread2 thread2 = new Thread2();
/*
启动线程要调用 start 方法,而不是直接调用 run 方法;当 start 方法调用完毕后,run 方法很快会被线程自行调用。
*/
thread1.start();
thread2.start();
}
}
class Thread1 extends Thread{
@Override
public void run() {
for (int i=0;i<100;i++){
System.out.println("你是谁啊?");
}
}
}
class Thread2 extends Thread{
@Override
public void run() {
for (int i=0;i<100;i++){
System.out.println("我是你dad!");
}
}
}
//output:
//你是谁啊?
//你是谁啊?
//我是你dad!
//我是你dad!
//我是你dad!
//你是谁啊?
//你是谁啊?
// ……
创建方式二:
public class Test {
public static void main(String[] args) {
//实例化两个任务
Runnable r1 = new Runnable1();
Runnable r2 = new Runnable2();
//创建两个线程并指派任务
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}
}
class Runnable1 implements Runnable{
@Override
public void run() {
for (int i=0;i<100;i++){
System.out.println("你是谁啊?");
}
}
}
class Runnable2 implements Runnable{
@Override
public void run() {
for (int i=0;i<100;i++){
System.out.println("我是你dad!");
}
}
}
currentThread:
线程提供了一个静态方法:
static Thread currentThread()
样例一:
public class Test {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println("运行main方法的线程为:"+thread);
}
}
//output:
//运行main方法的线程为:Thread[main,5,main]
//第一个main指线程名,5指的是线程的优先级,第二个main指的是线程所在的组
样例二:
public class Test {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println("运行main方法的线程为:"+thread);
Something();
}
public static void Something(){
Thread thread = Thread.currentThread();
System.out.println("运行Something方法的线程为:"+thread);
}
}
//output:
//运行main方法的线程为:Thread[main,5,main]
//运行Something方法的线程为:Thread[main,5,main]
//第一个main指线程名,5指的是线程的优先级,第二个main指的是线程所在的组
样例三:
public class Test {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println("运行main方法的线程为:"+thread);
Something();
Thread t = new Thread(){
@Override
public void run() {
Thread t = Thread.currentThread();
System.out.println("自定义线程"+t);
Something();
}
};
t.start();
}
public static void Something(){
Thread thread = Thread.currentThread();
System.out.println("运行Something方法的线程为:"+thread);
}
}
//output:
//运行main方法的线程为:Thread[main,5,main]
//运行Something方法的线程为:Thread[main,5,main]
//自定义线程Thread[Thread-0,5,main]
//运行Something方法的线程为:Thread[Thread-0,5,main]
线程自身信息的获取:
public class Test {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
//获取线程的名字
String name = thread.getName();
System.out.println("name:"+name);
//获取线程的唯一标识(id)
long id = thread.getId();
System.out.println("ID:"+id);
//获取线程的优先级(1~10),默认值是5
int priority = thread.getPriority();
System.out.println("优先级:"+priority);
//线程是否还处于活动状态
boolean isAlive = thread.isAlive();
System.out.println("isAlive?"+isAlive);
//线程是否被中断
boolean isInterrupted = thread.isInterrupted();
System.out.println("是否被中断?"+isInterrupted);
//线程是否为守护线程
boolean isDaemon = thread.isDaemon();
System.out.println("是否为守护线程?"+isDaemon);
}
}
//output:
//name:main
//ID:1
//优先级:5
//isAlive?true
//是否被中断?false
//是否为守护线程?false
线程优先级:
public class Test {
public static void main(String[] args) {
Thread max = new Thread(){
@Override
public void run() {
for (int i=0;i<100;i++)
System.out.println("max");
}
};
Thread min = new Thread(){
@Override
public void run() {
for (int i=0;i<100;i++)
System.out.println("min");
}
};
Thread normal = new Thread(){
@Override
public void run() {
for (int i=0;i<100;i++){
System.out.println("normal");
}
}
};
max.setPriority(Thread.MAX_PRIORITY);
min.setPriority(Thread.MIN_PRIORITY);
max.start();
min.start();
normal.start();
}
}
sleep阻塞:
线程提供了一个静态方法:
static void sleep(long ms)
public class Test {
public static void main(String[] args) {
System.out.println("程序开始了!");
try {
Thread.sleep(5000);//卡5秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("程序结束了!");
}
}
//output:
//程序开始了!
//(期间等了5秒)
//程序结束了!
sleep 方法要求必须处理中断异常,原因在于当一个线程调用了 sleep 方法处于阻塞状态的过程中若被调用了它的 interrupt 方法中断时,它就会在 sleep 方法中抛出中断异常。此时并非是将这个线程直接中断,而是中断了它的阻塞状态。
public class Test {
public static void main(String[] args) {
Thread thread_rest = new Thread(){
@Override
public void run() {
System.out.println("开始休息!");
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
System.out.println("休息打断!");
}
System.out.println("休息结束!");
}
};
Thread thread_interrupt = new Thread(){
@Override
public void run() {
System.out.println("闹钟开启!");
for (int i=0;i<3;i++){
System.out.println("闹钟响了!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("闹钟结束!");
//中断thread_rest线程
thread_rest.interrupt();
}
};
thread_rest.start();
thread_interrupt.start();
}
}
//output:
//开始休息!
//闹钟开启!
//闹钟响了!
//闹钟响了!
//闹钟响了!
//闹钟结束!
//休息打断!
//休息结束!
final Thread thread_rest = new Thread(){...}
守护线程:
又称为后台线程,默认创建的线程都是普通线程或称之为前台线程,线程提供了一个方法:
void setDaemon(boolean on)
守护线程在使用上与普通线程没有差别,但是在结束时机上有一个区别:进程结束时,所有正在运行的守护线程都会被强制停止。(进程结束:当一个进程中所有的普通线程都结束时进程既结束。)
public class Test {
public static void main(String[] args) {
Thread thread_sos = new Thread(){
@Override
public void run() {
for (int i=0;i<3;i++){
System.out.println("Help!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Death!");
}
};
Thread thread_help = new Thread(){
@Override
public void run() {
while (true){
System.out.println("I'm coming!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
thread_sos.start();
//将thread_help设置为守护线程,必须在启动前进行设置。
thread_help.setDaemon(true);
thread_help.start();
System.out.println("main线程结束了!");
}
}
//output:
//main线程结束了!
//I'm coming!
//Help!
//I'm coming!
//Help!
//I'm coming!
//Help!
//I'm coming!
//Death!
join阻塞:
线程提供了一个方法:
void join()
同步与异步:
public class Test {
//标识图片是否下载完毕
private static boolean isFinish = false;
public static void main(String[] args) {
Thread download = new Thread(){
@Override
public void run() {
System.out.println("开始下载图片……");
for (int i = 1;i<=100;i++){
System.out.println("down:"+i+"%");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("down:下载图片完毕!");
isFinish = true;
}
};
Thread show = new Thread(){
@Override
public void run() {
System.out.println("show:开始显示图片!");
//加载图片之前应当先等待下载线程将图片下载完毕!
try {
/*
show 线程在调用 download.join() 方法之后就进入了阻塞状态,直到 download 线程的 run 方法执行完毕才会解除阻塞。
*/
download.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (!isFinish){
throw new RuntimeException("加载图片失败!");
}
System.out.println("显示图片完毕!");
}
};
download.start();
show.start();
}
}
//output:
//开始下载图片……
//show:开始显示图片!
//down:1%
//down:2%
//……
//down:99%
//down:100%
//down:下载图片完毕!
//显示图片完毕!
yield方法:
问题的产生:
问题的解决:
问题代码举例:
public class Test {
public static void main(String[] args) {
Table table = new Table();
Thread thread = new Thread(){
@Override
public void run() {
while (true){
int bean = table.getBean();
System.out.println("线程"+getName()+"的豆:"+bean+"个!");
}
}
};
Thread thread1 = new Thread(){
@Override
public void run() {
while (true){
int bean = table.getBean();
Thread.yield();
System.out.println("线程"+getName()+"的豆:"+bean+"个!");
}
}
};
thread.start();
thread1.start();
}
}
class Table{
private int beans = 10;
public int getBean(){
if (beans==0){
throw new RuntimeException("没有豆子!");
}
//模拟线程执行到这里就没有时间了。
Thread.yield();
return beans--;
}
}
//output:
//程序有陷入死循环的风险,因为两个线程对于beans属性的操作的任意操作,会导致线程在运行getBean函数时可能会跳过下面的if语句块,譬如beans=1时,两个线程都取了一次beans,此时beans变成了一个负值就停不下来了!
//if (beans==0){
// ……
//}
解决方案:
//其他部分的代码一样
class Table{
private int beans = 10;
public synchronized int getBean(){
if (beans==0){
throw new RuntimeException("没有豆子!");
}
//模拟线程执行到这里就没有时间了。
Thread.yield();
return beans--;
}
}
//其他部分的代码一样
同步块:
synchronized(同步监视对象){
需要同步运行的代码片段
}
public class Test {
public static void main(String[] args) {
Shop shop = new Shop();
Thread thread1 = new Thread(){
@Override
public void run() {
shop.buy();
}
};
Thread thread2 = new Thread(){
@Override
public void run() {
shop.buy();
}
};
thread1.start();
thread2.start();
}
}
class Shop{
public void buy(){
try {
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+":正在挑衣服……");
Thread.sleep(1000);
synchronized (this){//this表示Shop对象,也就是main方法中的shop对象,多个线程指代的是同一个对象,因此可以实现“同步运行”的效果。
System.out.println(thread.getName()+":正在试衣服……");
Thread.sleep(1000);
}//顾客不能同时进入试衣间试衣服
System.out.println(thread.getName()+":结账离开……");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//output:
//Thread-1:正在挑衣服……
//Thread-0:正在挑衣服……
//Thread-0:正在试衣服……
//Thread-1:正在试衣服……
//Thread-0:结账离开……
//Thread-1:结账离开……
在方法上使用 synchronized,那么同步监视器对象就是当前方法所属对象,即:方法内部看到的 this 。
//以“并发安全问题”中的代码为例:
public synchronized int getBean(){
if (beans==0){
throw new RuntimeException("没有豆子!");
}
//模拟线程执行到这里就没有时间了。
Thread.yield();
return beans--;
}
静态方法使用 synchronized 修饰,那么该方法一定具有同步效果;静态方法对应的同步监视器对象为当前类的类对象(CLass的实例),类对象是后面反射的知识点。
public class Test {
public static void main(String[] args) {
Demo d1 = new Demo();
Demo d2 = new Demo();
Thread thread1 = new Thread(){
@Override
public void run() {
d1.Something();
// Demo.Something();
}
};
Thread thread2 = new Thread(){
@Override
public void run() {
d2.Something();
// Demo.Something();
}
};
thread1.start();
thread2.start();
}
}
class Demo{
public synchronized static void Something(){
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"正在运行Something方法!");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(thread.getName()+"运行Something方法完毕!");
}
}
//output:
//Thread-0正在运行Something方法!
//Thread-0运行Something方法完毕!
//Thread-1正在运行Something方法!
//Thread-1运行Something方法完毕!
当多个代码片段被 synchronized 块修饰之后,这些同步块的同步监听器对象又是同一个时,这些代码片段就是互斥的。多个线程不能同时在这些方法中运行。
public class Test {
public static void main(String[] args) {
Demo demo = new Demo();
Thread thread1 = new Thread(){
@Override
public void run() {
demo.methodA();
}
};
Thread thread2 = new Thread(){
@Override
public void run() {
demo.methodB();
}
};
thread1.start();
thread2.start();
}
}
class Demo{
public synchronized void methodA(){
Thread thread = Thread.currentThread();
try {
System.out.println(thread.getName()+"正在运行A方法!");
Thread.sleep(1000);
System.out.println(thread.getName()+"运行A方法完毕!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void methodB(){
Thread thread = Thread.currentThread();
try {
System.out.println(thread.getName()+"正在运行B方法!");
Thread.sleep(1000);
System.out.println(thread.getName()+"运行B方法完毕!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//output:
//Thread-0正在运行A方法!
//Thread-0运行A方法完毕!
//Thread-1正在运行B方法!
//Thread-1运行B方法完毕!
添加元素:
boolean add(E e)
import java.util.ArrayList;
import java.util.Collection;
public class Test {
public static void main(String[] args) {
Collection collection = new ArrayList();
collection.add("Hello");
collection.add("World");
collection.add("!");
System.out.println(collection);
}
}
//output:
// [Hello, World, !]
删除元素:
boolean remove(Object o)
import java.util.ArrayList;
import java.util.Collection;
public class CollectionTest {
public static void main(String[] args) {
Collection collection = new ArrayList();
collection.add(new Point(1,1));
collection.add(new Point(2,2));
collection.add(new Point(3,3));
collection.add(new Point(4,4));
System.out.println("之前的:"+collection);
Point point = new Point(1,1);
collection.remove(point);
System.out.println("之后的:"+collection);
}
}
//outputs:
//之前的:[Point{x=1, y=1}, Point{x=2, y=2}, Point{x=3, y=3}, Point{x=4, y=4}]
//之后的:[Point{x=2, y=2}, Point{x=3, y=3}, Point{x=4, y=4}]
获取集合的元素个数:
int size()
import java.util.ArrayList;
import java.util.Collection;
public class Test {
public static void main(String[] args) {
Collection collection = new ArrayList();
collection.add("Hello");
collection.add("World");
collection.add("!");
System.out.println(collection);
int size = collection.size();
System.out.println("集合的大小为:"+size);
}
}
//output:
//[Hello, World, !]
//集合的大小为:3
判断集合是否为空:
boolean isEmpty()
import java.util.ArrayList;
import java.util.Collection;
public class Test {
public static void main(String[] args) {
Collection collection = new ArrayList();
collection.add("Hello");
collection.add("World");
collection.add("!");
System.out.println(collection);
System.out.println("isEmpty?"+collection.isEmpty());
}
}
//output:
//[Hello, World, !]
//isEmpty?false
清空集合:
void clear()
import java.util.ArrayList;
import java.util.Collection;
public class Test {
public static void main(String[] args) {
Collection collection = new ArrayList();
collection.add("Hello");
collection.add("World");
collection.add("!");
System.out.println(collection);
collection.clear();
System.out.println(collection);
}
}
//output:
//[Hello, World, !]
//[]
判断集合是否包含指定元素:
boolean contains(Object o)
//测试元素类
public class Point {
private int x;
private int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "Point{" + "x=" + x + ", y=" + y + '}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Point point = (Point) o;
return x == point.x && y == point.y;
}
}
//主体程序:
import java.util.ArrayList;
import java.util.Collection;
public class ContainsTest {
public static void main(String[] args) {
Collection collection = new ArrayList();
collection.add(new Point(1,1));
collection.add(new Point(2,2));
collection.add(new Point(3,3));
collection.add(new Point(4,4));
//集合的toString会调用每个元素自身的toString方法体现出来。
System.out.println(collection);
Point point = new Point(3,3);
System.out.println("point为:"+point);
//contains方法是依靠元素自身equals方法比较的结果判断集合是否包含该元素。
System.out.println("是否包含point?"+collection.contains(point));
}
}
//output:
//[Point{x=1, y=1}, Point{x=2, y=2}, Point{x=3, y=3}, Point{x=4, y=4}]
//point为:Point{x=3, y=3}
//是否包含point?true
代码举例:
//测试元素类:
public class Point {
private int x;
private int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "Point{" + "x=" + x + ", y=" + y + '}';
}
public void setX(int x) {
this.x = x;
}
}
//主体程序:
import java.util.ArrayList;
import java.util.Collection;
public class Test {
public static void main(String[] args) {
Collection collection = new ArrayList();
Point point = new Point(1,2);
collection.add(point);
System.out.println("collection:"+collection);
System.out.println("point:"+point);
point.setX(2);
System.out.println("collection:"+collection);
System.out.println("point:"+point);
}
}
//output:
//collection:[Point{x=1, y=2}]
//point:Point{x=1, y=2}
//collection:[Point{x=2, y=2}]
//point:Point{x=2, y=2}
一个经典的代码题目:
import java.util.ArrayList;
import java.util.Collection;
public class CollectionTest {
public static void main(String[] args) {
int data = 1;
String string = "Hello";
Point point = new Point(1,2);
Collection collection = new ArrayList();
collection.add(point);
test(data,string,point,collection);
System.out.println(data);
System.out.println(string);
System.out.println(point);
System.out.println(collection);
}
public static void test(int data,String string,Point point,Collection collection){
data = 2;
string+=" World!";
point.setX(3);
point = new Point(5,6);
collection.clear();
collection.add(point);
point.setY(7);
collection = new ArrayList();
collection.add(point);
}
}
//output:
//1
//Hello
//Point{x=3, y=2}
//[Point{x=5, y=7}]
并集:
boolean addAll(Collection<? extends E> c)
import java.util.ArrayList;
import java.util.Collection;
public class CollectionTest {
public static void main(String[] args) {
Collection collection = new ArrayList();
collection.add("C");
collection.add("C#");
collection.add("C++");
System.out.println("collection:"+collection);
Collection collection1 = new HashSet();
collection1.add("Php");
collection1.add("Java");
System.out.println("collection1:"+collection1);
collection.addAll(collection1);
System.out.println("并集:"+collection);
}
}
//outputs:
//collection:[C, C#, C++]
//collection1:[Java, Php]
//并集:[C, C#, C++, Java, Php]
全包含:
boolean containsAll(Collection<?> c)
import java.util.ArrayList;
import java.util.Collection;
public class CollectionTest {
public static void main(String[] args) {
Collection collection = new ArrayList();
collection.add("C");
collection.add("C#");
collection.add("C++");
System.out.println("collection:"+collection);
Collection collection2 = new ArrayList();
collection2.add("C");
collection2.add("C#");
System.out.println("collection2:"+collection2);
System.out.println("是否全包含:"+collection.containsAll(collection2));
}
}
//outputs:
//collection:[C, C#, C++]
//collection2:[C, C#]
//是否全包含:true
删交集:
boolean removeAll(Collection<?> c)
import java.util.ArrayList;
import java.util.Collection;
public class CollectionTest {
public static void main(String[] args) {
Collection collection = new ArrayList();
collection.add("C");
collection.add("C#");
collection.add("C++");
System.out.println("collection:"+collection);
Collection collection2 = new ArrayList();
collection2.add("C");
collection2.add("C#");
collection2.add("Java");
System.out.println("collection2:"+collection2);
collection.removeAll(collection2);//删除交集
System.out.println("collection:"+collection);
}
}
//outputs:
//collection:[C, C#, C++]
//collection2:[C, C#, Java]
//collection:[C++]
集合提供了统一的遍历元素方式——迭代器模式。
Iterator iterator()
java.util.Iterator//接口
迭代器遍历集合元素遵循的过程——问、取、删。其中删除元素不是必要操作。
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionTest {
public static void main(String[] args) {
Collection collection = new ArrayList();
collection.add("one");
collection.add("#");
collection.add("two");
collection.add("#");
collection.add("three");
collection.add("#");
collection.add("four");
System.out.println("之前:"+collection);
//获取迭代器
Iterator iterator = collection.iterator();
/*
boolean hasNext()
判断集合是否还有元素可以迭代。
*/
while (iterator.hasNext()){//问
/*
E next()
迭代取出集合的下一个元素,默认返回值为Object类型。
*/
String string = (String)iterator.next();//取
if ("#".equals(string)){
iterator.remove();//删
//迭代器的remove方法无需传递参数,删除的就是next取出的元素
}
System.out.println(string);
}
System.out.println("之后:"+collection);
}
}
//outputs:
//之前:[one, #, two, #, three, #, four]
//one
//#
//two
//#
//three
//#
//four
//之后:[one, two, three, four]
JDK 5推出时,推出了一个新的特性:增强型 for 循环,也称为新循环;新循环不取代传统 for 循环的工作,它专门设计是用来遍历集合或数组的。
import java.util.ArrayList;
import java.util.Collection;
public class CollectionTest {
public static void main(String[] args) {
String[] array = {"Hello"," ","World","!"};
for (String string: array) {
System.out.print(string);
}
System.out.println();
Collection collection = new ArrayList();
collection.add("Hello");
collection.add(" ");
collection.add("World");
collection.add("!");
for (Object object: collection) {
String string = (String)object;
System.out.print(string);
}
}
}
//outputs:
//Hello World!
//Hello World!
泛型是 JDK 5 推出的特性,也称为参数化类型;它允许将一个类中属性的类型,方法参数的类型以及方法返回值类型等的定义权移交给使用者。这使得实际应用中使用这个类更加灵活便捷。
//主体类:
import java.util.ArrayList;
import java.util.Collection;
public class CollectionTest {
public static void main(String[] args) {
Location<Integer>location = new Location<Integer>(1,2);
int locationInt = location.getX();
System.out.println("X:"+locationInt);
System.out.println(location);
Location<Double>location1 = new Location<Double>(1.2,2.4);
double locationDouble = location1.getX();
System.out.println("X:"+locationDouble);
System.out.println(location1);
Location<String>location2 = new Location<String>("Hello ","World!");
String locationString = location2.getX();
System.out.println("X:"+locationString);
System.out.println(location2);
}
}
//outputs:
//X:1
//Location{x=1, y=2}
//X:1.2
//Location{x=1.2, y=2.4}
//X:Hello
//Location{x=Hello , y=World!}
//测试类
public class Location <E>{
private E x;
private E y;
//构造方法:
public Location(E x,E y){
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "Location{" + "x=" + x + ", y=" + y + '}';
}
public void setX(E x) {
this.x = x;
}
public E getX() {
return x;
}
public E getY() {
return y;
}
public void setY(E y) {
this.y = y;
}
}
public class Location <E,T,……>{
//……
}
泛型是编译器认可的,并非虚拟机;编译器会将泛型改为 Object,所以泛型的实际类型就是 Object。在使用泛型时,编译器会辅助做两个操作:
//Location类借用上面声明的类
public class CollectionTest {
public static void main(String[] args) {
Location<Integer>location = new Location<Integer>(2,2);
//编译器会检查实际赋值是否符合泛型类型要求,不符合则编译不通过。
location.setX(1);
/* 编译器会在编译时补全向下造型的代码为:
* int data = (Integer)location.getX();
* 然后还会触发自动拆箱,改为:
* int data = ((Integer)location.getX()).intValue();
*/
int data = location.getX();
System.out.println("location:"+location);
System.out.println("location_x:"+data);
//泛型可以不指定,不指定这按照默认的Object看待(下面的location1)。
Location location1 = location;
System.out.println("location1:"+location1);
//因为类型是Object,所以setX函数接收的是Object类型。
location1.setX("Hello");
System.out.println("location1:"+location1);
//再次以location1的角度获取x
data = (int) location1.getX();
System.out.println("location1_x:"+data);//报错!
}
}
//outputs:
//location:Location{x=1, y=2}
//location_x:1
//location1:Location{x=1, y=2}
//location1:Location{x=Hello, y=2}
//报错……
泛型在集合中的应用——约束集合中的元素类型。
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionTest {
public static void main(String[] args) {
Collection<String> stringCollection = new ArrayList<String>();
//指定之后add方法只能传入泛型要求的元素
stringCollection.add("A");
stringCollection.add("B");
stringCollection.add("C");
System.out.println(stringCollection);
//新循环可以直接用实际类型接收元素。
for (String string:stringCollection){
System.out.print(string);
}
System.out.println();
//迭代器也支持泛型,指定的类型与集合的泛型一致即可。
Iterator<String> stringIterator = stringCollection.iterator();
while(stringIterator.hasNext()){
String string = stringIterator.next();
System.out.print(string);
}
}
}
//outputs:
//[A, B, C]
//ABC
//ABC
List 接口是 Collection 的子接口,用于定义线性表数据结构;可重复,并且有序,提供了一组可以通过下标操作元素的方法;可以将 List 理解为存放对象的数组,只不过其元素个数可以动态的增加或者减少。
ArrayList 和 LinkedList:
获取元素_get 函数:
E get(int index);
import java.util.ArrayList;
import java.util.List;
public class ListTest {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
System.out.println(list);
String string = list.get(1);
System.out.println(string);
//List 可以使用普通的循环进行遍历
for (int i=0;i<list.size();i++)
System.out.print(list.get(i)+" ");
System.out.println();
//增强型 for 循环
for (String s : list) System.out.print(s + " ");
}
}
//output:
// [one, two, three, four]
// two
// one two three four
// one two three four
替换元素_set 函数:
E set(int index, E element);
import java.util.ArrayList;
import java.util.List;
public class ListTest {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
System.out.println(list);
System.out.println("替换之前的元素值:"+list.set(0,"1"));
System.out.println("替换之后的元素值:"+list.get(0));
System.out.println(list);
}
}
//output:
//[one, two, three, four]
//替换之前的元素值:one
//替换之后的元素值:1
//[1, two, three, four]
List 还提供了一对重载的 add,remove 方法:
void add(int index, E element);
import java.util.ArrayList;
import java.util.List;
public class ListTest {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
System.out.println(list);
//[one, two, three, four]
list.add(1,"2");
System.out.println(list);
//[one, 2, two, three, four]
}
}
//output:
//[one, two, three, four]
//[one, 2, two, three, four]
E remove(int index);
package Collection;
import java.util.ArrayList;
import java.util.List;
public class ListTest {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
System.out.println(list);
//[one, two, three, four]
System.out.println(list.remove(2));
System.out.println(list);
//[one, two, four]
}
}
//output:
//[one, two, three, four]
//three
//[one, two, four]
获取子集操作_subList 函数:
List<E> subList(int fromIndex, int toIndex);
import java.util.ArrayList;
import java.util.List;
public class ListTest {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
System.out.println(list);
List<String> list1 = list.subList(0,3);
System.out.println(list1);
}
}
//output:
//[one, two, three, four, five]
//[one, two, three]
import java.util.ArrayList;
import java.util.List;
public class ListTest {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
System.out.println("全集为:" + list);
List<Integer> subList = list.subList(0,4);
System.out.println("子集为:" + subList);
//将子集中的元素扩大10倍
for (int i=0;i<subList.size();i++){
subList.set(i, subList.get(i)*10);
}
System.out.println("子集为:" + subList);
System.out.println("全集为:" + list);
}
}
//output:
//全集为:[1, 2, 3, 4, 5]
//子集为:[1, 2, 3, 4]
//子集为:[10, 20, 30, 40]
//全集为:[10, 20, 30, 40, 5]
import java.util.ArrayList;
import java.util.List;
public class ListTest {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
System.out.println("全集为:" + list);
List<Integer> subList = list.subList(0,3);
System.out.println("子集为:" + subList);
//删除子集中的元素,进而查看原集合中的效果
subList.clear();
System.out.println("删除子集——————");
System.out.println("子集为:" + subList);
System.out.println("全集为:" + list);
}
}
//output:
//全集为:[1, 2, 3, 4, 5]
//子集为:[1, 2, 3]
//删除子集——————
//子集为:[]
//全集为:[4, 5]
集合转为数组:
集合提供了一个方法—— toArray,可以将当前集合转换为一个数组。
Object[] toArray();//不常用
在泛型推出之后,在 toArray 方法中可以传入一个数组以表示其数组类型:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class CollectionTest {
public static void main(String[] args) {
Collection<String> collection = new ArrayList<String>();
collection.add("one");
collection.add("two");
collection.add("three");
collection.add("four");
System.out.println(collection);
String[] array = collection.toArray(new String[collection.size()]);
System.out.println(array.length);
System.out.println(Arrays.toString(array));
}
}
//outputs:
//[one, two, three, four]
//4
//[one, two, three, four]
数组转为集合:
数组只能转换为 List,不能转换为 Set;这是因为 List 是可重复集合,Set 是不可重复集合;数组里面可能会出现重复的元素,所以数组不能转换为 Set 集合。
public static <T> List<T> asList(T... a) {}
代码演示:
import java.util.Arrays;
import java.util.List;
public class CollectionTest {
public static void main(String[] args) {
String[] strings = {"one","two","three","four"};
System.out.println(Arrays.toString(strings));
List<String> list = Arrays.asList(strings);
System.out.println(list);
}
}
//outputs:
//[one, two, three, four]
//[one, two, three, four]
一个有趣的现象:
import java.util.Arrays;
import java.util.List;
public class CollectionTest {
public static void main(String[] args) {
String[] strings = {"one","two","three","four"};
System.out.println("数组为:" + Arrays.toString(strings));
List<String> list = Arrays.asList(strings);
System.out.println("集合为:" + list);
list.set(1,"2");
System.out.println("改变之后………………");
System.out.println("集合为:" + list);
System.out.println("数组为:" + Arrays.toString(strings));
}
}
//outputs:
//数组为:[one, two, three, four]
//集合为:[one, two, three, four]
//改变之后………………
//集合为:[one, 2, three, four]
//数组为:[one, 2, three, four]
破解增删操作限制的方案——其实就是新建一个集合,再把数据复制过去。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class CollectionTest {
public static void main(String[] args) {
String[] strings = {"one","two","three","four"};
System.out.println("数组为:" + Arrays.toString(strings));
List<String> list = Arrays.asList(strings);
System.out.println("集合为:" + list);
//原方案:
// List list1 = new ArrayList();
// list1.addAll(list);
//代码优化:
//所有的集合都提供了一个参数为 Collection 的构造方法,
// 作用是在创建当前集合的同时包含给定集合中的所有元素。
List<String> list1 = new ArrayList<String>(list);
list1.add("five");
System.out.println("新集合为:" + list1);
}
}
//outputs:
//数组为:[one, two, three, four]
//集合为:[one, two, three, four]
//新集合为:[one, two, three, four, five]
关键就是借助集合工具类:
java.util.Collections;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ListTest {
public static void main(String[] args) {
List<Integer>list = new ArrayList<Integer>();
//生成随机数
for (int i=0;i<10;i++){
list.add((int)(Math.random()*100));
}
System.out.println(list);
Collections.sort(list);
System.out.println(list);
}
}
//output:
//[68, 28, 52, 42, 44, 15, 56, 44, 76, 37]
//[15, 28, 37, 42, 44, 44, 52, 56, 68, 76]
对于 Java 中内置的类,我们可以直接调用 sort 方法进行排序,对于自定义的类就不能直接调用 sort 进行排序了!
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ListTest {
public static void main(String[] args) {
List<Point> list = new ArrayList<Point>();
list.add(new Point(1,2));
list.add(new Point(2,3));
list.add(new Point(3,4));
System.out.println(list);
Collections.sort(list);//编译直接不通过
System.out.println(list);
}
}
//Point类的声明:
public class Point{
private int x;
private int y;
Point(int x,int y){
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "{" + x + "," + y + '}';
}
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ListTest {
public static void main(String[] args) {
List<Point> list = new ArrayList<Point>();
list.add(new Point(1,2));
list.add(new Point(6,3));
list.add(new Point(1,5));
list.add(new Point(3,4));
System.out.println(list);
Collections.sort(list);
System.out.println(list);
}
}
//output:
//[{1,2}, {6,3}, {1,5}, {3,4}]
//[{1,2}, {3,4}, {1,5}, {6,3}]
//Point类的声明:
public class Point implements Comparable<Point>{
private int x;
private int y;
Point(int x,int y){
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "{" + x + "," + y + '}';
}
//当一个类实现了 Comparable 接口之后必须重写方法——compareTo.
//该方法的作用是比较当前对象 this 与方法的参数对象 o 之间的大小。
@Override
public int compareTo(Point o) {
int thisLength = this.x*this.x+this.y*this.y;
int oLength = o.x*o.x+o.y*o.y;
return thisLength-oLength;
}
// 返回值不关心具体的取值,只关心取值范围:
// 当返回值>0:当前对象大于参数对象(this>o)
// 当返回值<0:当前对象小于参数对象(this
// 当返回值=0:当前对象等于参数对象(this=o)
}
问题的产生:
问题的解决:在实际开发中推荐使用该方式进行排序。
代码举例:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ListTest {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("你好");
list.add("明天见");
list.add("我");
list.add("下次一定");
System.out.println(list);
Collections.sort(list,new Comparator<String>(){
@Override
public int compare(String o1, String o2) {
return o1.length()-o2.length();//按照字符的多少比较大小!
}
});
//还可以进一步化简为lambda表达式
// Collections.sort(list, (o1, o2) -> {
// return o1.length()-o2.length();//按照字符的多少比较大小!
// });
System.out.println(list);
}
}
//output:
//[你好, 明天见, 我, 下次一定]
//[我, 你好, 明天见, 下次一定]
import java.util.Queue;
import java.util.LinkedList;
import java.util.Queue;
public class QueueTest {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<String>();
queue.offer("Hello");//入队操作,向队列末尾追加元素。
queue.offer("World");
queue.offer("!");
System.out.println(queue);
// queue.add("World");//和offer方法一样的效果,但是建议使用offer,更加规范
String string = queue.poll();//出队操作
System.out.println(string);
System.out.println(queue);
string = queue.peek();//引用队首元素,元素不出队
System.out.println(string);
System.out.println(queue);
//遍历操作
for (String str : queue) {
System.out.println(str);
}
System.out.println("_________");
//使用poll方法遍历队列
while (queue.size()>0){
System.out.println(queue.poll());
}
System.out.println(queue);
}
}
//output:
//[Hello, World, !]
//Hello
//[World, !]
//World
//[World, !]
//World
//!
//_________
//World
//!
//[]
import java.util.Deque;
继承于 Queue 接口。
双端队列是指队列的两端都可以进行进队出队操作。
常用实现类: L i n k e d L i s t LinkedList LinkedList。
常用函数:
代码举例:
import java.util.Deque;
import java.util.LinkedList;
public class DequeTest {
public static void main(String[] args) {
Deque<String> deque = new LinkedList<String>();
//放置元素:
deque.offer("one");
deque.offer("two");
deque.offerFirst("three");
deque.offerLast("four");
System.out.println(deque);
//取出元素:
System.out.println(deque.pollLast());
System.out.println(deque);
System.out.println(deque.pollFirst());
System.out.println(deque);
//引用元素:
System.out.println(deque.peek());
System.out.println(deque.peekLast());
System.out.println(deque.peekFirst());
System.out.println(deque);
}
}
//output:
//[three, one, two, four]
//four
//[three, one, two]
//three
//[one, two]
//one
//two
//one
//[one, two]
代码举例:
import java.util.Deque;
import java.util.LinkedList;
public class DequeTest {
public static void main(String[] args) {
Deque<String> stack = new LinkedList<String>();
//数据入栈
stack.push("one");
stack.push("two");
stack.push("three");
System.out.println(stack);
//数据出栈
System.out.println(stack.pop());
System.out.println(stack);
}
}
//output:
//[three, two, one]
//three
//[two, one]
import java.util.*;
public class DequeTest {
public static void main(String[] args) {
//List中的常用实现类ArrayList和LinkedList都不是线程安全的。
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
System.out.println(list);
//将给定集合转换为一个线程安全的。
list = Collections.synchronizedList(list);//看一下源代码发现是新建了一个线程安全的list
System.out.println(list);
//HashSet同样也不是线程安全的
Set<String> set = new HashSet<String>(list);
set = Collections.synchronizedSet(set);
System.out.println(set);
}
}
//output:
//[one, two, three]
//[one, two, three]
BlockingQueue、BlockingDeque(阻塞队列)是一个双缓冲队列,在多线程并发时,若需要使用队列,我们可以使用 Queue,但是要解决一个问题就是同步,但是同步操作会降低并发对 Queue 操作的效率。
BlockingQueue 内部使用两条队列,可以允许两个线程同时向队列一个做存储,一个做取出操作;在保证安全的同时提供了队列的存取效率。
继承阻塞队列实现的子类:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
public class DequeTest {
public static void main(String[] args) {
BlockingQueue<String> queue = new LinkedBlockingQueue<String>();
queue.offer("one");//塞不下时,直接抛出异常了。
try {//对于阻塞之后的处理手段,等待50ms,再看是否塞得下!
queue.offer("two",50, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(queue);
}
}
//output:
//[one, two]