前面一章,讲到了集合,这里大略回忆下,最上级Collection接口下的基本方法,如add(),remove(),equals(),contains(),hashCode(),isEmpty(),iterator()等基本方法,即其下面的子接口或实现类都能使用或复写的功能。接着是List集合,其特点是有序的,可以重复的,因为它带有索引。另一个就是Set集合,特点是无序的,不可重复的。 List集合是带有索引的,所以凡是可以操作角标的方法都是该体系中的特有方法。Collection的实现类都可以使用Iterator 迭代器 ,在使用迭代器迭代的过程中,我们可以对元素进行判断(hasNext()),获取(next()),以及删除(remove)。而在List集合下,却又比Set集合多了个功能:列表迭代器(ListIterator),其在迭代的过程中,我们可以对元素进行增删改查功能。 List下面三员大将。分别是Arraylist、LinkedList,还有老将Vector。ArrayList和Vector的底层都是数组数据结构。Vector这个老将获取集合的方式除了for循环,iterator以外,还有就是通过枚举。不过因为老迈(因为枚举的名称及方法的名称都过长,所以被迭代器取代了),渐渐退出了战争舞台。ArrayList底层是数组结构,所以方法大都是可以操作角标的,查询起来很快,但是增删操作略慢。而LinkedList底层是链表结构。特点是查询略慢,但是增删效率高。其特有方法如addFirst(),getFirst(),getLast(),removeFirst()等。 其中,ArrayList和LinkedList对元素的删除remove和判断contains依赖的都是元素对象的equals方法。 Set集合的左膀右臂分别是HashSet 和TreeSet。HashSet 的底层数据结构是哈希表。因为Set集合中元素是不重复的,那么HashSet 和TreeSet是如何保证元素的唯一性呢?hashSet保证元素唯一的依赖就是hashCode()和equals()。先判断两个元素的哈希值是否相同,若不同,则不是同一对象,若相同,再通过equals比较,为true,则为同一对象,否则反之。而TreeSet保证唯一的则是compareTo() 方法 return 0 则表示相同。因为TreeSet底层是二叉树数组,所以会对元素进行排序。排序方式有两种。 一、实现comparable接口,然后复写compareTo() 方法。返回比较结果。 二、创建一个比较器(类实现Comparator接口),复写compare() 方法,然后将这个比较器的对象作为参数传递给集合的构造方法。
还有一个集合就是Map,它是键值映射关系的集合。就是说里面的元素都是成对出现的。每一个元素都有唯一的键。它下面有三个实现类,分别是HashTable,HashMap,TreeMap,其中,HashTable底层是哈希表数据结构,不可以存入null 键和null值。线程是同步的,效率较低,所以失宠了。。。。所以后期用到的主要是hashMap,TreeMap。看到这两个名字,我一楞,这不是和Set集合里的HashSet 和TreeSet 很像么,怎么没有ArrayMap和LinkedMap呢?再一看Set集合和Map集合的特点,我隐约有点明白了。。。Set集合中元素是不重复的,而Map集合中key是唯一的,也就是说元素也是唯一性的。那么hashMap和HashSet ,TreeSet和TreeMap有什么相同点呢? HashMap和HashSet都是哈希表数据结构,那么,HashMap保证元素唯一的依赖自然也是hashCode和equals了,而TreeSet 和TreeMap都是二叉树结果,那么,二都都可以通过实现Comparable接口和创建比较器来对集合中的元素进行排序了。
另外有一点,Map集合中没有iterator迭代器,如果要取出集合中的元素,需要将Map集合转换成Set 集合,然后再通过Iterator来获取元素列表。而将Map集合转换成Set集合也有两种方式:keySet 和 entrySet keySet 方式是将Map集合中的键存入Set集合中,再通过迭代器,在迭代过程中,通过Map集合的get方法获取与键对应的Value值。 entrySet方式是将Map集合中的键值映射关系存入到Set集合中,而这种映射关系的类型是Map.Entry<K,V>,然后再通过迭代器,在迭代过程中,使用Map集合中的方法getKey()和getValue()分别获取每一个键值对的键和值。
好了,上面是回忆了上一章的大概内容,这一次要说的是集合框架中的工具类Collections和Arrays了。
Collections
Collections 专门用于对集合进行操作的工具类
常用方法: 1、 sort() 排序 对集合中的元素进行排序 max() 获取元素中的最大值(默认按自然顺序中的最大值) 根据元素的自然顺序,返回给定collection的最大元素。collection中的所有元素都必须实现Comparable接口。
并且collection中的所有元素都必须是可相互比较的
import java.util.*;
class CollectionsDemo
{
public static void main(String[] args)
{
sortDemo();
}
public static void sortDemo()
{
List<String> list=new ArrayList<String>();
list.add("kdl");
list.add("bcw");
list.add("adf");
list.add("qwdd");
list.add("ww");
//使用Collections类中的sort方法进行自然排序
Collections.sort(list);
sop(list);
//获取最大值
sop(Collections.max(list));
//使用比较器按字符串长度排序
Collections.sort(list,new StringLenComparator());
//打印集合
sop(list);
//按字符串长度排序后的最大值
sop(Collections.max(list,new StringLenComparator()));
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
//创建一个比较器,按字符串长度排序
class StringLenComparator implements Comparator<String>
{
public int compare(String s1,String s2)
{
int num=new Integer(s1.length()).compareTo(new Integer(s2.length()));
if(num==0)
return s1.compareTo(s2);
return num;
}
}
结果:
2、二分查找:
binarySearch(List<? extends Comparable<? super T>> list, T key)
binarySearch(List<? extends T> list, T key, Comparator<? super T> c)
使用比较器进行二分查找
使用二分搜索法搜索指定列表,以获得指定对象。import java.util.*;
class CollectionsTest
{
public static void main(String[] args)
{
List<String> list=new ArrayList<String>();
list.add("kdl");
list.add("bcw");
list.add("adf");
list.add("qwdd");
list.add("ww");
Collections.sort(list);
sop(list);
//二分查找(折半查找)
int index=Collections.binarySearch(list,"ww");
sop(index);
}
//折半查找原理
public static int halfSearch(List<String> list,String key)
{
int max,min,mid;
min=0;max=list.size()-1;
while(min<=max)
{
mid=(min+max)/2;
String str=list.get(mid);
int num=str.compartTo(key);
if(num>0)
{
max=min-1;
}
else if(num<0)
min=min+1;
else return mid;
}
return -1;
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
3、fillfill(List<? super T> list,T obj) :使用指定元素替换指定列表中的所有元素
public static void fillDemo()
{
List<String> list=new ArrayList<String>();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
list.add("zhaoliu");
Collections.fill(list,"qq");
Iterator<String> it=list.iterator();
while(it.hasNext())
{
String str=it.next();
//打印元素
sop(str);
}
}
4、replaceAll replaceAll:使用另一个值替换列表中出现的所有某一指定值 reverse:反转指定列表中元素的顺序
如下代码:
public static void replaceAllDemo()
{
List<String> list=new ArrayList<String>();
list.add("abc");
list.add("bdfs");
list.add("sab");
list.add("cdf");
//替换
Collections.replaceAll(list,"sab","w");
sop(list);
Collections.reverse(list);
sop(list);
}
结果: 从结果里可以看出,replaceAll() 方法将集合中的"sab"替换成了"w",而reverse将替换后的集合按原顺序反转了。
5、reverseOrder()
reverseOrder()返回一个比较器,它强行逆转实现了 Comparable 接口的对象 collection 的自然顺序。
reverseOrder(Comparator<T> cmp) 返回一个比较器,它强行逆转指定比较器的顺序。
如下例:
import java.util.*;
class CollectionsDemo2
{
public static void main(String[] args)
{
reverseOrderDemo();
}
public static void reverseOrderDemo()
{
//在当集合的构造函数中传递Collections.reverseOrder()后,排序顺序就之前的排序的倒序
//返回一个比较器,它强行逆转实现了 Comparable 接口的对象 collection 的自然顺序。
//TreeSet<String> ts=new TreeSet<String>(Collections.reverseOrder());
//当有比较器时,则将比较器强行逆转
TreeSet<String> ts=new TreeSet<String>(Collections.reverseOrder(new StringLenComparator()));
ts.add("adsf");
ts.add("bc");
ts.add("asdfklasdf");
ts.add("ddf");
ts.add("qq");
Iterator<String> it=ts.iterator();
while(it.hasNext())
{
String str=it.next();
sop(str);
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
//根据字符串长度排序的比较器
class StringLenComparator implements Comparator<String>
{
public int compare(String s1,String s2)
{
int num=(new Integer(s1.length())).compareTo(new Integer(s2.length()));
if(num==0)
return s1.compareTo(s2);
return num;
}
}
结果:
6、swap,shuffle
swap(List<?> list, int i, int j) : 在指定列表的指定位置处交换元素。
shuffle(List<?> list) 使用默认随机源对指定列表进行置换。
shuffle() 置换后每次的结果都不一样
看代码演示:
import java.util.*;
class CollectionsSwap
{
public static void main(String[] args)
{
List<String> list =new ArrayList<String>();
list.add("abc01");
list.add("abc02");
list.add("abc03");
list.add("abc04");
list.add("abc05");
sop(list);
//对指定位置的两个元素进行互换
Collections.swap(list,1,3);
sop(list);
//对指定列表进行元素随机置换
Collections.shuffle(list);
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
输出:
集合框架中的另一个工具类:Arrays
Arrays :用于操作数组的工具类(里面都是静态方法。)
常见方法: asList:将数组变成List集合
把数组变成List集合有什么好处?
可以使用集合的思想和方法来操作数组中的元素。
注意:将数组变成集合,不可以使用集合的增删方法。因为数组的长度是固定的
contains,get,indexOf,subList均可用,但是如果用了增删,会发生UnsupportedOperationException
如下:
import java.util.*;
class ArraysDemo
{
public static void main(String[] args)
{
asListDemo();
}
public static void asListDemo()
{
String[] arr={"dd","abc","df","www","er"};
List<String> list=Arrays.asList(arr);
//判断数组中是否包含某元素(需要通过循环来判断)
boolean arrcontains=myContains(arr,"www");
sop(arrcontains);
//判断集合中是否包含某元素(直接通过contains判断)
boolean listcontains=list.contains("www");
sop(listcontains);
sop(list);
/*****************************/
//将数组变成集合,不可以使用集合的增删方法。因为数组的长度是固定的
//contains,get,indexOf,subList均可用,但是如果用了增删,会发生UnsupportedOperationExce
//list.add("ads");
/********特殊之处(打印结果不一样)*******/
int[] nums={2,4,5,6};
List li=Arrays.asList(nums);
//将数组变成List集合
List<int[]> lii=Arrays.asList(nums);
sop(li);
sop(lii);
Integer[] in={2,4,5,6};
List<Integer> lis=Arrays.asList(in);
sop(lis);
/*
如果数组中的元素都是对象。那么变成集合时,数组中的元素就直接转成集合中的元素。
如果数组中的元素都是基本数据类型时,那么会将该数组作为集合中的元素存在
*/
}
//判断数组中是否包含某元素
public static boolean myContains(String[] arr,String key)
{
for(int i=0;i<arr.length;i++)
{
if(arr[i].equals(key))
return true;
}
return false;
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
集合变数组 Collection接口中的toArray() 1、指定类型的数组到底要定义多长呢?
当指定类型的数组长度小于了集合的Size,那么该方法内部会创建一个新的数组,长度为集合的size
当指定类型的数组长度大于了集合的size,就不会创建新的数组,而是使用传递进来的数组。
所以创建一个刚刚好的数组最优
2、为什么要将集合变数组?
是为了限定对元素的操作。
看使用示例:
import java.util.*;
class CollectionToArrayDemo
{
public static void main(String[] args)
{
CollectionToArray();
}
public static void CollectionToArray()
{
ArrayList<String> al=new ArrayList<String>();
al.add("askl");
al.add("aww");
al.add("adf");
al.add("asdf");
String[] arr=al.toArray(new String[0]); //打印出的数组长度为3
String[] arr1=al.toArray(new String[10]); //打印出的数组长度为10
//正确方法
String[] arr2=al.toArray(new String[al.size()]);
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.toString(arr1));
System.out.println(Arrays.toString(arr2));
}
}
输出:
高级for循环
格式:
for(数据类型 变量名:被遍历的集合(Collection)或者数组)
{
}
对集合进行遍历。只能获取集合元素。但是不能对集合进行操作。
迭代器除了遍历,还可以进行remove集合中的元素的动作(remove)
如果是用ListIterator,还可以在遍历过程中对集合进行增删改查的动作。
传统for和高级for有什么区别?
高级for有一个局限性,必须有被遍历的目标。
建议在遍历数组的时候,还是希望使用传统for。因为传统for可以定义角标。
看高级for在实际中的应用例子:
import java.util.*;
class ForEachDemo
{
public static void main(String[] args)
{
ForEach();
}
public static void ForEach()
{
HashMap<Integer,String> hm=new HashMap<Integer,String>();
hm.put(1,"a");
hm.put(2,"b");
hm.put(3,"c");
hm.put(4,"d");
/*************keySet***************/
Set<Integer> keyset=hm.keySet();
//高级for循环
for(Integer i:keyset)
{
System.out.println(i+"::"+hm.get(i));
}
sop("********************************");
/*************entrySet*******************/
Set<Map.Entry<Integer,String>> entryset=hm.entrySet();
//高级for循环
for(Map.Entry<Integer,String> me:entryset)
{
Integer key=me.getKey();
String value=me.getValue();
System.out.println(key+"::"+value);
}
}
public static void ForEach0()
{
ArrayList<String> al=new ArrayList<String>();
al.add("askl");
al.add("aww");
al.add("adf");
al.add("asdf");
//使用迭代器
Iterator<String> it=al.iterator();
while(it.hasNext())
{
System.out.println(it.next());
}
//使用for增强
for(String s:al)
{
System.out.println(s);
}
}
}
可变参数和静态导入
一、可变参数
其实就是数组参数的简写格式。不用每一次都手动的建立数组对象,只要将要操作的元素作为参数传递即可。隐式的将这些参数封装成了数组
使用注意事项:可变参数一定要放在参数列表的最后面
二、静态导入
当类名重名时,需要指定具体的包名。
当方法重名时,指定具体所属的对象或类
如下例:
import java.util.*;
import static java.util.Arrays.*;
import static java.lang.System.*;//导入了System类中所有静态成员
class ParamMethodDemo
{
public static void main(String[] args)
{
method("asdf",3,4,5,6);
method_StaticImport();
}
public static void method_StaticImport()
{
int[] arr={3,2,8};
Arrays.sort(arr);
//int index=Arrays.binarySearch(arr,1);
int index=binarySearch(arr,1);
//下面这个Arrays为什么没有省去呢?因为Object类下也有toString()
System.out.println(Arrays.toString(arr));
out.println("Index="+index);
}
//可变参数
public static void method(String a,int... arr)
{
System.out.println(arr.length);
}
}
其他对象
一、System System:类中的方法和属性都是静态的。
out:标准输出,默认是控制台
in:标准输入,默认是键盘。
Properties:描述系统的一些信息。
Properties getProperties()
因为Properties是Hashtable的子类,也就是MAP集合的一个子类对象。那么可以通过map的方法取出该集合中的元素
该集合中存储都是字符串,没有泛型定义。
看下例:
import java.util.*;
class SystemDemo
{
public static void main(String[] args)
{
Properties prop=System.getProperties();
//因为Properties是Hashtable的子类,也就是MAP集合的一个子类对象。
//那么可以通过map的方法取出该集合中的元素
//该集合中存储都是字符串,没有泛型定义。
for(Object obj :prop.keySet())
{
String s=(String)obj;
String value=(String)prop.get(s);
sop(s+":"+value);
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
结果:
自定义系统属性:
//如何在系统中自定义一些特有信息呢?
System.setProperty("meng","hanshi");
//获取指定属性信息
String value=System.getProperty("meng");
sop(value);
结果如图:
或者如下方式:
//能否在JVM启动时,动态加载一些属性信息?
String v=System.getProperty("haha");
sop(v);//结果为null,因为系统属性中没有haha这个属性
//在控制台上执行:java
//然后再执行:java -Dhaha=qqqqq SystemDemo
//可得到结果
如下:
二、Runtime Runtime 对象
该类并没有提供构造函数,说明不可以new对象。那么会直接想到该类中的方法都是静态的。
发现该类中还有有非静态函数。说明该类中肯定提供了方法获取本类对象。
而且该方法是静态的,并且返回值类型是本类类型。由这个特点可以看出该类使用了单例设计模式
常用方法:
Process exec(String command) 在单独的进程中执行指定的字符串命令 destroy() 杀掉子进程。是抽象类Process中的方法
看使用方式:
class RuntimeDemo
{
public static void main(String[] args) throws Exception
{
Runtime rt=Runtime.getRuntime();
Process p=rt.exec("winmine.exe");//单独的进程中执行指定的字符串命令
Process p1=rt.exec("notepad SystemDemo.java");
Thread.sleep(4000);
p.destroy();//杀死子进程
}
}
注意:exec() 执行的命令中常带有路径,当这个路径不存在的时候,会报IoException异常
三、Date Date 类表示特定的瞬间,精确到毫秒。
日期和时间模式: 字母 日期或时间元素 表示 示例
G Era 标志符 Text AD
y 年 Year 1996; 96
M 年中的月份 Month July; Jul; 07
w 年中的周数 Number 27
W 月份中的周数 Number 2
D 年中的天数 Number 189
d 月份中的天数 Number 10
F 月份中的星期 Number 2
E 星期中的天数 Text Tuesday; Tue
a Am/pm 标记 Text PM
H 一天中的小时数(0-23) Number 0
k 一天中的小时数(1-24) Number 24
K am/pm 中的小时数(0-11) Number 0
h am/pm 中的小时数(1-12) Number 12
m 小时中的分钟数 Number 30
s 分钟中的秒数 Number 55
S 毫秒数 Number 978
z 时区 General time zone Pacific Standard Time; PST; GMT-08:00
Z 时区 RFC 822 time zone -0800
直接看使用方式:
import java.util.*;
import java.text.*;
class DateDemo
{
public static void main(String[] args)
{
Date d=new Date();
sop(d); //打印的时间格式不符合人们的习惯
//将模式封装到SimpleDateformat对象中。
SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日 E hh:mm:ss");
//调用format方法让模式格式化指定对象
String time=sdf.format(d);
sop(time);
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
输出:
四、Calendar 日历类 Calendar 类是一个抽象类,它为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等 日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法
常用方法: add(int field, int amount) 根据日历的规则,为给定的日历字段添加或减去指定的时间量
get(int field) 返回给定日历字段的值
set(int year, int month, int date) 设置日历字段 YEAR、MONTH 和 DAY_OF_MONTH 的值
如下:
import java.util.*;输出:
import java.text.*;
class CalendarDemo
{
public static void main(String[] args)
{
Calendar c=Calendar.getInstance();
printDate(c);
sop("*************Set日历字段**************");
//设置日历字段
c.set(2025,5,15);
printDate(c);
/* //获取年(老方法)
Date date=new Date();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy");
String s=sdf.format(date);
sop(s);
*/
}
public static void printDate(Calendar c)
{
//获取年份
int year=c.get(Calendar.YEAR);
sop(year);
/*查表法*/
String[] mons={"一月","二月","三月","四月",
"五月","六月","七月","八月",
"九月","十月","十一月","十二月",};
String[] weeks={"","星期日","星期一","星期二","星期三","星期四","星期五","星期六"};
//获取月份(从0开始,0表示一月,依次类推)
int month=c.get(Calendar.MONTH);
//通过查表法获取实际月份
sop(mons[month]);
//获取当天日期
int day=c.get(Calendar.DAY_OF_MONTH);
sop(day);
//通过查表法获取当天所属星期
int week=c.get(Calendar.DAY_OF_WEEK);
sop(weeks[week]);
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
看一个练习题:
/*
1、获取任意年的二月有多少天?
2、获取昨天的现在这个时刻
*/
import java.util.*;
import java.text.*;
class CalendarTest
{
public static void main(String[] args)
{
getDateNum(2013,2);
getYestodayNow();
}
//获取昨天的现在这个时刻
public static void getYestodayNow()
{
Calendar c=Calendar.getInstance();
c.add(Calendar.DAY_OF_MONTH,-1);
printDate(c);
}
//获取任意年的任意月有多少天
public static void getDateNum(int Year,int Month)
{
Calendar c=Calendar.getInstance();
c.set(Year,Month,1);
c.add(Calendar.DAY_OF_MONTH,-1);
sop(c.get(Calendar.DAY_OF_MONTH));
}
//打印日期
public static void printDate(Calendar c)
{
//获取年份
sop(c.get(Calendar.YEAR));
/*查表法*/
String[] mons={"一月","二月","三月","四月",
"五月","六月","七月","八月",
"九月","十月","十一月","十二月",};
String[] weeks={"","星期日","星期一","星期二","星期三","星期四","星期五","星期六"};
//获取月份
sop(mons[c.get(Calendar.MONTH)]);
//获取日期
sop(c.get(Calendar.DAY_OF_MONTH));
//获取星期
sop(weeks[c.get(Calendar.DAY_OF_WEEK)]);
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
Math类 Math 类包含用于执行基本数学运算的方法
常用方法 double ceil() 返回大于指定数据的最小整数
double floor() 返回小于指定数据的最大整数
double round() 四舍五入
double pow(double a, double b) 返回第一个参数的第二个参数次幂的值
random() 返回带正号的 double 值,该值大于等于 0.0 且小于 1.0。
如下例:
public static void methods()输出:
{
double d=Math.ceil(16.23); //ceil 返回大于指定数据的最小整数
sop(d);
double d1=Math.floor(12.45);// floor 返回小于指定数据的最大整数
sop(d1);
long l=Math.round(12.54);//四舍五入
sop(l);
double p=Math.pow(2,3);//2的3次方
sop(p);
}
public static void sop(Object obj)
{
System.out.println(obj);
}
random() 返回带正号的 double 值,该值大于等于 0.0 且小于 1.0。如下:
public static void method_Random()结果:
{
/******使用Math.random*/
for(int i=0;i<10;i++)
{
int d=(int)(Math.random()*10+1);
sop(d);
}
sop("***********使用Random类*****************");
/**********使用Random类**********/
Random r=new Random();
for(int i=0;i<10;i++)
{
int d=r.nextInt(10)+1;
sop(d);
}
}