Java常用辅助类以及工具类系列

一、Date类

1、Date类概述

Date类是从JDK1.1就开始存在的老类,其提供了针对日期进行操作的诸多方法,但其却一直饱受诟病,不同的起始编号,国际化的低支持,JDK官方也认识到这个问题,后台提出使用Calendar类进行日期操作,日期的格式化交给DateFormat,虽然我们已经不再使用Date类中的大多数方法,但是还有一部分保留的内容指的我们一谈。

2、Date构造器

Date类之前有6大构造器,其中四个已经标注弃用,我们我不再看他,我们重点看另外两个:

	/**
     * Allocates a Date object and initializes it so that
     */
    public Date() {
        this(System.currentTimeMillis());
    }

    /**
     * @param   date   the milliseconds since January 1, 1970, 00:00:00 GMT.
     */
    public Date(long date) {
        fastTime = date;
    }

第一个构造器是无参构造器,通过调用System的currentTimeMillis()方法来获取当前时间戳,这个时间戳是从1970年到当前时间的毫秒级数据,第二个构造器,可以将一个毫秒级的数据定义为Date格式的日期。

3、Date常用方法

Date中定义了诸多的日期操作方法,但是大多数都已弃用,只剩余为数不多的几个方法:

/**
     * Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT
     * represented by this Date object.
     */
    public long getTime() {
        return getTimeImpl();
    }

    /**
     * Sets this Date object to represent a point in time that is
     * time milliseconds after January 1, 1970 00:00:00 GMT.
     */    
    public void setTime(long time) {
        fastTime = time;
        cdate = null;
    }
    /**
     * Tests if this date is before the specified date.
     */
    public boolean before(Date when) {
        return getMillisOf(this) < getMillisOf(when);
    }
    /**
     * Tests if this date is after the specified date.
     */
    public boolean after(Date when) {
        return getMillisOf(this) > getMillisOf(when);
    }

上面显示的四个方法是Date类中现在还在使用的几个常用方法:

  • long getTime()方法:返回从1970年00:00:00到Date对象所代表时间的毫秒级数据
  • void setTime(long
    time)方法:设置一个Date对象用来代表从1970年00:00:00开始的一段毫秒级数据后所代表的时间点
  • boolean before(Date when)方法:判断Date对象所代表的时间点是否在when所代表的时间点之前
  • boolean after(Date when)方法:判断Date对象所代表的时间点是否在when所代表的时间点之后

4、实例解析

public static void main(String[] args) {
        Date now = new Date();//获取当前时间
        Date when = new Date(10201020097865L);//根据时间戳定义指定时间点
        boolean b1 = now.after(when);
        boolean b2 = now.before(when);
        Long d1 = now.getTime();
        Long d2 = when.getTime();
        
        System.out.println("now值为:"+now);
        System.out.println("when值为:"+when);
        System.out.println("b1值为:"+b1);
        System.out.println("b2值为:"+b2);
        System.out.println("d1值为:"+d1);
        System.out.println("d2值为:"+d2);
        
    }

结果为:

now值为:Thu Jul 06 13:39:12 CST 2017
when值为:Tue Apr 04 16:41:37 CST 2293
b1值为:false
b2值为:true
d1值为:1499319552116
d2值为:10201020097865

5、总结

Date类现在并不推荐使用,Java推荐了Calendar和DateFormat,甚至SimpleDateFormat来替代它,Date中仅剩的几个方法仍然还很实用,尤其是before与after方法,可以很方便的判断两个时间点的先后,当然判断的条件是将你的时间转换成Date格式,使用Date剩余的两个构造器实现即可,当然也可以使用推荐的SimpleDateFormat方法进行简单的格式化日期格式字符串的方式得到Date格式的时间点,这些会在稍后了解到!

二、Calendar

1、Calendar概述

Java官方推荐使用Calendar来替换Date的使用,Calendar与Date之间可以自由的进行转换,转换的纽带是time,使用Calendar的getTime()方法可以得到一个Date类型的对象,这个对象底层是使用Date的第二个带Long型参数的构造器创建的,这个Long型参数是Calendar中的time字段中保存的值,这个time字段的值是在具体的实现类中定义赋值的比如GregorianCalendar中的实现computeTime(),这个方法的目的就是将field值转换为time值,这个涉及到Calendar中的两种模式,之后会有介绍;而通过Calendar的setTime(Date date)方法可以将一个Date对象转换为一个Calendar对象,这个方法以一个Date对象为参数,底层调用的setTimeInMillis(long millis)方法,将date.getTime()的值作为参数,再底层会将这个Long型参数值赋值给time字段,这时会重计算field值。

Calendar与Date的转换

public static void main(String[] args) {
        //Calendar--->Date
        Calendar c = Calendar.getInstance();
        Date d = c.getTime();
        //Date--->Calendar
        Date d1 = new Date();
        Calendar c1 = Calendar.getInstance();
        c1.setTime(d1);
        
        System.out.println(d);
        System.out.println(c1.get(Calendar.YEAR)+"年"+(c1.get(Calendar.MONTH)+1)+"月"+c1.get(Calendar.DATE)+"日");
    }

结果:

Sat Jul 08 10:39:14 CST 2017
201778

2、Calendar中的time与field

Calendar中有两种描述时间内容的域,一种就是time,它用来保存Calendar对象所代表的时间点据1970年1月1日 00:00:00的毫秒数,另一种就是field,它是一个数组,它表示的并不是一个内容,而是Calendar内部定义的最多静态常量字段。

而这一般情况下是同步的,即表述的是同一时间点,但也有可能会出现不同步的情况:

  • a、起初,field没有设置,time也是无效的
  • b、如果time被设置,所有的field都会自动被设置为同步的时间点
  • c、如果某一field被单独设置,time会自动失效

更确切的说,当我们通过Calendar.getInstance()方法获取一个全新的Calendar对象时,它所代表的时间点是通过time来设置的,而这个time的值是通过System.currentTimeMillis()得到的,通过time定义Calendar,isTimeSet为true,表示time值是最新的(真的),areFieldsSet为false,表示field字段的值都是旧的(假的),因为当我们重新设置了time值之后,Calendar所代表的时间点就发生了变化(这里是首次,相当于从无到有,也算是变化,之后当我们为Calendar的time重新设置一个新值时,Calendar的时间点就会再次发生变化,它会指向最新的time值所代表的时间点),而这时field中还表示的是原来的时间点内容,然后会调用computeFields()方法进行所有字段值的重计算,确保field中的值与time同步,并同时将areFieldsSet和areAllFieldsSet设置为true,表示所有的field代表的时间值也是最新的了(真)。其实我们每次更改time值都会自动触发重计算,来确保两个域所描述的时间点一致(即同步),这也就是上面b所述的内容。

但是如果我们通过set(int field, int value)单独对field中的某行一字段进行更改时,首先会触发一个验证,areFieldsSet为真而areAllFieldsSet为false时,表示只有一部分field是最新的情况,即存在部分field属于旧的情况,针对这种情况会触发field的重新计算;之后会将isTimeSet设置为false,areFieldsSet设置为false,将isSet[field]设置为true(将当前field设置为真),这种情况下,当我们使用getTime()获取time值所代表的时间点时,由于isTimeSet为false,会触发time的重计算,这个计算依据是根据field的值进行的,之后将isTimeSet设置为true,同样我们在通过get(int field)获取某个field值时也会先验证isTimeSet是否为true,如果为false,同样会触发time的重计算,然后验证areFieldsSet为false,则触发其余field的重计算。

time的重计算是依据field的,确切的说是依据部分field的,而有一部分field也是在field的基础上再计算的,所以可以说有一部分field是固定的,是和time息息相关的。

以上种种所述全部是Calendar内部的实现规则,对外而言,我们只需要简单的调用即可,所有这些都被隐藏在内部,从而保证我们通过对外方法获取到的直接就是正确的值。

public static void main(String[] args) throws ParseException {
        System.out.println("-------初始情况-------");
        Calendar c = Calendar.getInstance();
        System.out.println(c.getTime());
        System.out.println(c.get(Calendar.DATE));
        System.out.println(c.get(Calendar.HOUR));
        System.out.println("-------重设置time-------");
        c.setTime(new SimpleDateFormat("yyyyMMdd").parse("20170501"));
        System.out.println(c.getTime());
        System.out.println(c.get(Calendar.DATE));
        System.out.println(c.get(Calendar.HOUR));
        System.out.println("-------重设置field-------");
        c.set(Calendar.MONTH, 4);
        System.out.println(c.getTime());
        System.out.println(c.get(Calendar.DATE));
        System.out.println(c.get(Calendar.HOUR));
        System.out.println("总结:time与field所代表时间点同步,所有的不同步全部在内部处理完成");
    }

结果:

-------初始情况-------
Sat Jul 08 13:08:34 CST 2017
1
-------重设置time-------
Mon May 01 00:00:00 CST 2017
0
-------重设置field-------
Mon May 01 00:00:00 CST 2017
0
总结:time与field所代表时间点同步,所有的不同步全部在内部处理完成

3、Calendar中的两种解析模式

lenient:该模式下可以自动规则化用户赋值给Calendar的不规则值,比如1月32日会被解析为2月1日

non-lenient:该模式下不会自动解析不规则的输入,而是一旦发现不规则输入,就会报出异常

这也叫Calendar的容错性,lenient的开启与关闭使用setLenient(boolean lenient)方法来设置,true表示开启容错性(默认情况),false表示关闭该功能。

public static void main(String[] args) {
        Calendar c = Calendar.getInstance();
        c.set(Calendar.MONTH, 8);
        c.set(Calendar.DAY_OF_MONTH, 33);
        System.out.println(c.getTime()+"\n");
        c.setLenient(false);
        c.set(Calendar.MONTH, 8);
        c.set(Calendar.DAY_OF_MONTH, 33);
        System.out.println(c.getTime());
    }

结果:

Tue Oct 03 13:18:48 CST 2017

Exception in thread "main" java.lang.IllegalArgumentException: DAY_OF_MONTH
    at java.util.GregorianCalendar.computeTime(GregorianCalendar.java:2583)
    at java.util.Calendar.updateTime(Calendar.java:2606)
    at java.util.Calendar.getTimeInMillis(Calendar.java:1118)
    at java.util.Calendar.getTime(Calendar.java:1091)
    at JdkTest.main(JdkTest.java:87)

从上面的例子中可以看出,默认情况下,我们为Calendar的月份赋值为8即九月份,日期赋值为33即下一月3号,输出为10月3日,容错性将这种不符合规则的输入规则化处理了,而关闭容错性之后,同样的赋值只会报异常java.lang.IllegalArgumentException(非法参数异常)。

4、Calendar的使用

public static void main(String[] args) throws ParseException {
        //通过SimpleDateFormat解析日期字符串
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd hh:mm:ss.SSS");
        Date date = sdf.parse("20170502 13:33:23.433");
        //将Date格式日期转换成Calendar
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        //获取时间值
        System.out.println(c.getTime());
        System.out.println("年份为"+c.get(Calendar.YEAR));
        System.out.println("月份为"+c.get(Calendar.MONTH));
        System.out.println("日期为"+c.get(Calendar.DATE));
        System.out.println("日期为"+c.get(Calendar.DAY_OF_MONTH));
        System.out.println("日期为"+c.get(Calendar.DAY_OF_WEEK));
        System.out.println("日期为"+c.get(Calendar.DAY_OF_WEEK_IN_MONTH));
        System.out.println("日期为"+c.get(Calendar.DAY_OF_YEAR));
        System.out.println("时为"+c.get(Calendar.HOUR));
        System.out.println("时为"+c.get(Calendar.HOUR_OF_DAY));
        System.out.println("分为"+c.get(Calendar.MINUTE));
        System.out.println("秒为"+c.get(Calendar.SECOND));
        System.out.println("毫秒为"+c.get(Calendar.MILLISECOND));
        System.out.println("星期为"+c.get(Calendar.WEEK_OF_MONTH));
        System.out.println("星期为"+c.get(Calendar.WEEK_OF_YEAR));
        System.out.println("历型为"+c.get(Calendar.ERA));
        System.out.println("zone为"+c.get(Calendar.ZONE_OFFSET));
        //设置
        c.set(Calendar.MONTH, Calendar.APRIL);
        System.out.println("修改后月份为"+c.get(Calendar.MONTH));
        c.set(1999, 0, 23);
        System.out.println(c.getTime());
        c.set(2000, 1, 12, 13, 33, 14);
        System.out.println(c.getTime());
        c.set(2001, 2, 13, 14, 13);
        System.out.println(c.getTime());
        //运算
        System.out.println("-----运算-----");
        c.add(Calendar.YEAR, 12);
        System.out.println(c.getTime());
        c.add(Calendar.MONTH, -1);
        System.out.println(c.getTime());
        c.roll(Calendar.DATE, true);
        System.out.println(c.getTime());
        c.add(Calendar.DATE, 1);
        System.out.println(c.getTime());
        //roll与add运算对比
        c.set(2000, 1, 29);
        System.out.println(c.getTime());
        c.roll(Calendar.DATE, 1);
        System.out.println(c.getTime());
        c.set(2000, 1, 29);
        c.add(Calendar.DATE, 1);
        System.out.println(c.getTime());
    }

结果:

Tue May 02 13:33:23 CST 2017
年份为2017
月份为4
日期为2
日期为2
日期为3
日期为1
日期为122
时为1
时为13
分为33
秒为23
毫秒为433
星期为1
星期为18
历型为1
zone为28800000
修改后月份为3
Sat Jan 23 13:33:23 CST 1999
Sat Feb 12 13:33:14 CST 2000
Tue Mar 13 14:13:14 CST 2001
-----运算-----
Wed Mar 13 14:13:14 CST 2013
Wed Feb 13 14:13:14 CST 2013
Thu Feb 14 14:13:14 CST 2013
Fri Feb 15 14:13:14 CST 2013
Tue Feb 29 14:13:14 CST 2000
Tue Feb 01 14:13:14 CST 2000
Wed Mar 01 14:13:14 CST 2000

对比上面最后的两行输出,可以看出add与roll的运算规则其实是不同的,roll的运算不会影响大规则(这里的大规则指的是月份的改变)的改变,而add会影响。

综上所述,我们可以简单的认识一下Calendar类,并学会简单的使用它,对于其内部实现,还需要认真敲一敲源码。

三、BigDecimal

1、BigDecimal简介

Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。在实际应用中,需要对更大或者更小的数进行运算和处理。float和double只能用来做科学计算或者是工程计算,在商业计算中要用java.math.BigDecimal。BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。

2、构造器描述

BigDecimal(int)       创建一个具有参数所指定整数值的对象。 
BigDecimal(double) 创建一个具有参数所指定双精度值的对象。 
BigDecimal(long)    创建一个具有参数所指定长整数值的对象。 
BigDecimal(String) 创建一个具有参数所指定以字符串表示的数值的对象。

3、方法描述

add(BigDecimal)        BigDecimal对象中的值相加,然后返回这个对象。 
subtract(BigDecimal) BigDecimal对象中的值相减,然后返回这个对象。 
multiply(BigDecimal)  BigDecimal对象中的值相乘,然后返回这个对象。 
divide(BigDecimal)     BigDecimal对象中的值相除,然后返回这个对象。 
toString()                将BigDecimal对象的数值转换成字符串。 
doubleValue()          将BigDecimal对象中的值以双精度数返回。 
floatValue()             将BigDecimal对象中的值以单精度数返回。 
longValue()             将BigDecimal对象中的值以长整数返回。 
intValue()               将BigDecimal对象中的值以整数返回。

4、格式化及例子
由于NumberFormat类的format()方法可以使用BigDecimal对象作为其参数,可以利用BigDecimal对超出16位有效数字的货币值,百分值,以及一般数值进行格式化控制。

以利用BigDecimal对货币和百分比格式化为例。首先,创建BigDecimal对象,进行BigDecimal的算术运算后,分别建立对货币和百分比格式化的引用,最后利用BigDecimal对象作为format()方法的参数,输出其格式化的货币值和百分比。

public static void main(String[] args) {
    NumberFormat currency = NumberFormat.getCurrencyInstance(); //建立货币格式化引用 
    NumberFormat percent = NumberFormat.getPercentInstance();  //建立百分比格式化引用 
    percent.setMaximumFractionDigits(3); //百分比小数点最多3位 
    
    BigDecimal loanAmount = new BigDecimal("15000.48"); //贷款金额
    BigDecimal interestRate = new BigDecimal("0.008"); //利率   
    BigDecimal interest = loanAmount.multiply(interestRate); //相乘

    System.out.println("贷款金额:\t" + currency.format(loanAmount)); 
    System.out.println("利率:\t" + percent.format(interestRate)); 
    System.out.println("利息:\t" + currency.format(interest)); 
}

运行结果如下:

贷款金额:    ¥15,000.48
利率:    0.8%
利息:    ¥120.00

5、BigDecimal比较
BigDecimal是通过使用compareTo(BigDecimal)来比较的,具体比较情况如下:

public static void main(String[] args) {
    BigDecimal a = new BigDecimal("1");
    BigDecimal b = new BigDecimal("2");
    BigDecimal c = new BigDecimal("1");
    int result1 = a.compareTo(b);
    int result2 = a.compareTo(c);
    int result3 = b.compareTo(a);
    System.out.println(result1);
    System.out.println(result2);
    System.out.println(result3);
}

打印结果是:-1、0、1,即左边比右边数大,返回1,相等返回0,比右边小返回-1。
注意不能使用equals方法来比较大小。

使用BigDecimal的坏处是性能比double和float差,在处理庞大,复杂的运算时尤为明显,因根据实际需求决定使用哪种类型。

四、Collections

Java提供了一个操作Set、List和Map等集合的工具类:Collections,该工具类提供了大量方法对集合进行排序、查询和修改等操作,还提供了将集合对象置为不可变、对集合对象实现同步控制等方法。

排序操作

collections提供了如下几个方法用于对List集合元素进行排序:

static void reverse(List list): 反转列表中元素的顺序。

static void shuffle(List list) : 对List集合元素进行随机排序。

static void sort(List list) :根据元素的自然顺序 对指定列表按升序进行排序

static void sort(List list, Comparator c) : 根据指定比较器产生的顺序对指定列表进行排序。

static void swap(List list, int i, int j) :在指定List的指定位置i,j处交换元素。

static void rotate(List list, int distance) :当distance为正数时,将List集合的后distance个元素“整体”移到前面;当distance为负数时,将list集合的前distance个元素“整体”移到后边。该方法不会改变集合的长度。

下面程序简单示范了利用collections工具类来操作List集合:

public class TestSort
{
    public static void main(String[] args)
    {
        ArrayList nums = new ArrayList();
        nums.add(2);
        nums.add(-5);
        nums.add(3);
        nums.add(0);
        //输出:[2, -5, 3, 0]
        System.out.println(nums);
        //将List集合元素的次序反转
        Collections.reverse(nums);
        //输出:[0, 3, -5, 2]
        System.out.println(nums);
        //将List集合元素的按自然顺序排序
        Collections.sort(nums);
        //输出:[-5, 0, 2, 3]
        System.out.println(nums);
        //将List集合元素的按随机顺序排序
        Collections.shuffle(nums);
        //每次输出的次序不固定
        System.out.println(nums);
        //后两个整体移动到前边
        Collections.rotate(nums,2);
        System.out.println(nums);
    }
}

输出结果:
[2, -5, 3, 0]
[0, 3, -5, 2]
[-5, 0, 2, 3]
[2, 3, -5, 0]
[-5, 0, 2, 3]

重写sort中的Comparator接口实现自定义排序:

package collections.sort;
 
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
 
public class TestSort {
	public static void main(String[] args) {
		List<Book> books = new ArrayList<>();
		books.add(new Book(5l, subDate(0), "zjq5"));
		books.add(new Book(2l, subDate(3), "zjq2"));
		books.add(new Book(3l, subDate(2), "zjq3"));
		books.add(new Book(1l, subDate(4), "zjq1"));
		books.add(new Book(4l, subDate(1), "zjq4"));
		Collections.sort(books, new Comparator<Book>() {
 
			@Override
			public int compare(Book o1, Book o2) {
				//o1和o2反过来决定升序和降序
//				return o1.getId().compareTo(o2.getId());升序
//				return o2.getId().compareTo(o1.getId());降序
//				return o1.getName().compareTo(o2.getName());
//				return o2.getName().compareTo(o1.getName());
//				return o1.getTime().compareTo(o2.getTime());
				return o2.getTime().compareTo(o1.getTime());
			}
			
		});
		for (Book book : books) {
			System.out.println(book.getId());
		}
	}
	
	//日期减一天 加一天
	public static Date subDate(int sum){
		return new Date(new Date().getTime()-sum*24*60*60*1000);
	}
}

查找、替换操作

collections还提供了如下用于查找、替换集合元素的常用方法:

static int binarySearch(List list,Object key):使用二分搜索法搜索指定列表,以获得指定对象在List集合中的索引。 
此前必须保证List集合中的元素已经处于有序状态。

static Object max(Collection coll): 根据元素的自然顺序,返回给定collection 的最大元素。

static Object max(Collection coll,Comparator comp): 根据指定比较器产生的顺序,返回给定 collection 的最大元素。

static Object min(Collection coll): 根据元素的自然顺序,返回给定collection 的最小元素。

static Object min(Collection coll,Comparator comp): 根据指定比较器产生的顺序,返回给定 collection 的最小元素。

static void fill(List list, Object obj) : 使用指定元素替换指定列表中的所有元素。

static int frequency(Collection c, Object o) :返回指定 collection 中等于指定对象的出现次数。

static int indexOfSubList(List source, List target) : 返回指定源列表中第一次出现指定目标列表的起始位置;如果没有出现这样的列表,则返回 -1static int lastIndexOfSubList(List source, List target) :返回指定源列表中最后一次出现指定目标列表的起始位置;如果没有出现这样的列表,则返回 -1static boolean replaceAll(List list, T oldVal, T newVal) :使用一个新值替换List对象的所有旧值oldVal。

下面程序简单示范了Collections工具类的用法:

public class TestSearch
{
    public static void main(String[] args)
    {
        ArrayList nums = new ArrayList();
        nums.add(2);
        nums.add(-5);
        nums.add(3);
        nums.add(0);
        //输出:[2, -5, 3, 0]
        System.out.println(nums);
        //输出最大元素,将输出3
        System.out.println(Collections.max(nums));
        //输出最小元素,将输出-5
        System.out.println(Collections.min(nums));
        //将nums中的0使用1来代替
        Collections.replaceAll(nums , 0 , 1);
        //输出:[2, -5, 3, 1]
        System.out.println(nums);
        //判断-5 在List集合中出现的次数,返回1
        System.out.println(Collections.frequency(nums , -5));
        //对nums集合排序
        Collections.sort(nums);
        //输出:[-5, 1, 2, 3]
        System.out.println(nums);
        //只有排序后的List集合才可用二分法查询,输出3
        System.out.println(Collections.binarySearch(nums , 3));    
    }
}

同步控制

Collectons提供了多个synchronizedXxx()方法,该方法返回指定集合对象对应的同步对象,从而解决多线程并发访问集合时的线程安全问题。

正如前面介绍的HashSet,TreeSet,arrayList,LinkedList,HashMap,TreeMap都是线程不安全的。Collections提供了多个静态方法可以把他们包装成线程同步的集合。

方法如下:

static Collection synchronizedCollection(Collection c) :返回指定 collection 支持的同步(线程安全的)collection。

static List synchronizedList(List list) :返回指定列表支持的同步(线程安全的)列表。

static Map synchronizedMap(Map m) :返回由指定映射支持的同步(线程安全的)映射。

static Set synchronizedSet(Set s) : 返回指定 set 支持的同步(线程安全的)set。

下面程序示范了创建四个线程同步集合对象:

public class TestSynchronized
{
    public static void main(String[] args)
    {
        //下面程序创建了四个同步的集合对象
        Collection c = Collections.synchronizedCollection(new ArrayList());
        List list = Collections.synchronizedList(new ArrayList());
        Set s = Collections.synchronizedSet(new HashSet());
        Map m = Collections.synchronizedMap(new HashMap());
    }
}

上面程序中,直接将新创建的集合对象传给了Collections的synchronizedXxx方法,这样就直接获取了List、Set、Map的线程实现版本。

设置不可变集合

Collections提供了如下三个方法来返回一个不可变集合:

emptyXxx(): 返回一个空的、不可变的集合对象,此处的集合既可以是List,也可以是Set,还可以是Map。

singletonXxx(): 返回一个只包含指定对象(只有一个或一个元素)的不可变的集合对象,此处的集合可以是:List,Set,Map。

unmodifiableXxx(): 返回指定集合对象的不可变视图,此处的集合可以是:List,Set,Map。

通过上面Collections提供的三类方法,可以生成“只读”的Collection或Map,看下面程序:

public class TestUnmodifiable
{
    public static void main(String[] args)
    {
        //创建一个空的、不可改变的List对象
        List<String> unmodifiableList = Collections.emptyList();
        //unmodifiableList.add("java");  //添加出现异常:java.lang.UnsupportedOperationException
        System.out.println(unmodifiableList);// []
        //创建一个只有一个元素,且不可改变的Set对象
        Set unmodifiableSet = Collections.singleton("Struts2权威指南");//[Struts2权威指南]
        System.out.println(unmodifiableSet);
        //创建一个普通Map对象
        Map scores = new HashMap();
        scores.put("语文" , 80);
        scores.put("Java" , 82);
        //返回普通Map对象对应的不可变版本
        Map unmodifiableMap = Collections.unmodifiableMap(scores);
        //下面任意一行代码都将引发UnsupportedOperationException异常
        unmodifiableList.add("测试元素");
        unmodifiableSet.add("测试元素");
        unmodifiableMap.put("语文",90);
    }
}

注意不要把Collection接口和Collections工具类搞混哦!

五、Arrays类

Arrays类位于 java.util 包中,主要包含了操纵数组的各种方法。

Arrays类的常用方法

1、Arrays.asList(T… data)

注意:该方法返回的是Arrays内部静态类ArrayList,而不是我们平常使用的ArrayList,,该静态类ArrayList没有覆盖父类的add, remove等方法,所以如果直接调用,会报UnsupportedOperationException异常

  • 将数组转换为集合,接收一个可变参数。
List<Integer> list = Arrays.asList(1, 2, 3);
list.forEach(System.out::println); // 1 2 3
Integer[] data = {1, 2, 3};
List<Integer> list = Arrays.asList(data);
list.forEach(System.out::println); // 1 2 3
  • 如果将基本数据类型的数组作为参数传入, 该方法会把整个数组当作返回的List中的第一个元素。
int[] data = {1, 2, 3};
List<int[]> list = Arrays.asList(data);
System.out.println(list.size()); // 1
System.out.println(Arrays.toString(list.get(0))); // [1, 2, 3]

2、Arrays.fill()

Arrays.fill(Object[] array, Object obj)

  • 用指定元素填充整个数组(会替换掉数组中原来的元素)
Integer[] data = {1, 2, 3, 4};
Arrays.fill(data, 9);
System.out.println(Arrays.toString(data)); // [9, 9, 9, 9]

Arrays.fill(Object[] array, int fromIndex, int toIndex, Object obj)

  • 用指定元素填充数组,从起始位置到结束位置,取头不取尾(会替换掉数组中原来的元素)
Integer[] data = {1, 2, 3, 4};
Arrays.fill(data, 0, 2, 9);
System.out.println(Arrays.toString(data)); // [9, 9, 3, 4]

3、Arrays.sort()

Arrays.sort(Object[] array)

  • 对数组元素进行排序(串行排序)
String[] data = {"1", "4", "3", "2"};
System.out.println(Arrays.toString(data)); // [1, 4, 3, 2]
Arrays.sort(data);
System.out.println(Arrays.toString(data)); // [1, 2, 3, 4]

Arrays.sort(T[] array, Comparator comparator)

  • 使用自定义比较器,对数组元素进行排序(串行排序)
String[] data = {"1", "4", "3", "2"};
System.out.println(Arrays.toString(data)); // [1, 4, 3, 2]
// 实现降序排序,返回-1放左边,1放右边,0保持不变
Arrays.sort(data, (str1, str2) -> {
    if (str1.compareTo(str2) > 0) {
        return -1;
    } else {
        return 1;
    }
});
System.out.println(Arrays.toString(data)); // [4, 3, 2, 1]

Arrays.sort(Object[] array, int fromIndex, int toIndex)

  • 对数组元素的指定范围进行排序(串行排序)
String[] data = {"1", "4", "3", "2"};
System.out.println(Arrays.toString(data)); // [1, 4, 3, 2]
// 对下标[0, 3)的元素进行排序,即对1,4,3进行排序,2保持不变
Arrays.sort(data, 0, 3);
System.out.println(Arrays.toString(data)); // [1, 3, 4, 2]

Arrays.sort(T[] array, int fromIndex, int toIndex, Comparator c)

  • 使用自定义比较器,对数组元素的指定范围进行排序(串行排序)
String[] data = {"1", "4", "3", "2"};
System.out.println(Arrays.toString(data)); // [1, 4, 3, 2]
// 对下标[0, 3)的元素进行降序排序,即对1,4,3进行降序排序,2保持不变
Arrays.sort(data, 0, 3, (str1, str2) -> {
    if (str1.compareTo(str2) > 0) {
        return -1;
    } else {
        return 1;
    }
});
System.out.println(Arrays.toString(data)); // [4, 3, 1, 2]

4、Arrays.parallelSort()

注意:其余重载方法与 sort() 相同

Arrays.parallelSort(T[] array)

  • 对数组元素进行排序(并行排序),当数据规模较大时,会有更好的性能。
String[] data = {"1", "4", "3", "2"};
Arrays.parallelSort(data);
System.out.println(Arrays.toString(data)); // [1, 2, 3, 4]

5、Arrays.binarySearch()

注意:在调用该方法之前,必须先调用sort()方法进行排序,如果数组没有排序,
那么结果是不确定的,此外如果数组中包含多个指定元素,则无法保证将找到哪个元素。

Arrays.binarySearch(Object[] array, Object key)

  • 使用 二分法 查找数组内指定元素的索引值。

这里需要先看下binarySearch()方法的源码,对了解该方法有很大的帮助。

public static int binarySearch(long[] a, long key) {
        return binarySearch0(a, 0, a.length, key);
    }
private static int binarySearch0(long[] a, int fromIndex, int toIndex, long key) {
        int low = fromIndex;
        int high = toIndex - 1;

        while (low <= high) {
            int mid = (low + high) >>> 1;
            long midVal = a[mid];

            if (midVal < key)
                low = mid + 1;
            else if (midVal > key)
                high = mid - 1;
            else
                return mid; // key found
        }
        return -(low + 1);  // key not found.
    }

从源码中可以看到:

当搜索元素是数组元素,则返回该元素的索引值。
如果不是数组元素,则返回 - (索引值 + 1),具体的用法可以看下面的例子。

  • 搜索元素是数组元素,返回该元素索引值。
Integer[] data = {1, 3, 5, 7};
Arrays.sort(data);
System.out.println(Arrays.binarySearch(data, 1)); // 0
  • 搜索元素不是数组元素,且小于数组中的最小值。
Integer[] data = {1, 3, 5, 7};
Arrays.sort(data);
// 此时程序会把数组看作 {0, 1, 3, 5, 7},此时0的索引值为0,则搜索0时返回 -(0 + 1) = -1
System.out.println(Arrays.binarySearch(data, 0)); // -1
  • 搜索元素不是数组元素,且大于数组中的最大值。
Integer[] data = {1, 3, 5, 7};
Arrays.sort(data);
// 此时程序会把数组看作 {1, 3, 5, 7, 9},此时9的索引值为4,则搜索8时返回 -(4 + 1) = -5
System.out.println(Arrays.binarySearch(data, 8)); // -5
  • 搜索元素不是数组元素,但在数组范围内。
Integer[] data = {1, 3, 5, 7};
Arrays.sort(data);
// 此时程序会把数组看作 {1, 2, 3, 5, 7},此时2的索引值为1,则搜索2时返回 -(1 + 1) = -2
System.out.println(Arrays.binarySearch(data, 2)); // -2

Arrays.binarySearch(Object[] array, int fromIndex, int toIndex, Object obj)

  • 使用 二分法 查找数组内指定范围内的指定元素的索引值。
Integer[] data = {1, 3, 5, 7};
Arrays.sort(data);
// {1, 3},3的索引值为1
System.out.println(Arrays.binarySearch(data, 0, 2, 3)); // 1

6、Arrays.copyOf()

Arrays.copyOf(T[] original, int newLength)

  • 拷贝数组,其内部调用了 System.arraycopy() 方法,从下标0开始,如果超过原数组长度,会用null进行填充。
Integer[] data1 = {1, 2, 3, 4};
Integer[] data2 = Arrays.copyOf(data1, 2);
System.out.println(Arrays.toString(data2)); // [1, 2]
Integer[] data2 = Arrays.copyOf(data1, 5);
System.out.println(Arrays.toString(data2)); // [1, 2, 3, 4, null]

7、Arrays.copyOfRange(T[] original, int from, int to)

  • 拷贝数组,指定起始位置和结束位置,如果超过原数组长度,会用null进行填充。
Integer[] data1 = {1, 2, 3, 4};
Integer[] data2 = Arrays.copyOfRange(data1, 0, 2);
System.out.println(Arrays.toString(data2)); // [1, 2]
Integer[] data2 = Arrays.copyOfRange(data1, 0, 5);
System.out.println(Arrays.toString(data2)); // [1, 2, 3, 4, null]

8、Arrays.equals(Object[] array1, Object[] array2)

  • 判断两个数组是否相等,实际上比较的是两个数组的哈希值,即 Arrays.hashCode(data1) ==
    Arrays.hashCode(data2)。
Integer[] data1 = {1, 2, 3};
Integer[] data2 = {1, 2, 3};
System.out.println(Arrays.equals(data1, data2)); // true

9、Arrays.deepEquals(Object[] array1, Object[] array2)

  • 判断两个多维数组是否相等,实际上比较的是两个数组的哈希值,即 Arrays.hashCode(data1) ==
    Arrays.hashCode(data2)。
Integer[][] data1 = {{1,2,3}, {1,2,3}};
Integer[][] data2 = {{1,2,3}, {1,2,3}};
System.out.println(Arrays.deepEquals(data1, data2)); // true

10、Arrays.hashCode(Object[] array)

  • 返回数组的哈希值。
Integer[] data = {1, 2, 3};
System.out.println(Arrays.hashCode(data)); // 30817

11、Arrays.deepHashCode(Object[] array)

  • 返回多维数组的哈希值。
Integer[][] data = {{1, 2, 3}, {1, 2, 3}};
System.out.println(Arrays.deepHashCode(data)); // 987105

12、Arrays.toString(Object[] array)

  • 返回数组元素的字符串形式。
Integer[] data = {1, 2, 3};
System.out.println(Arrays.toString(data)); // [1, 2, 3]

13、Arrays.deepToString(Object[] array)

返回多维数组元素的字符串形式。

Integer[][] data = {{1, 2, 3}, {1, 2, 3}};
System.out.println(Arrays.deepToString(data)); // [[1, 2, 3], [1, 2, 3]]

14、Arrays.setAll(T[] array, IntFunction)

Integer[] data = {2, 3, 4, 5};
// 第一个元素2不变,将其与第二个元素3一起作为参数x, y传入,得到乘积6,作为数组新的第二个元素
// 再将6和第三个元素4一起作为参数x, y传入,得到乘积24,作为数组新的第三个元素,以此类推
Arrays.parallelPrefix(data, (x, y) -> x * y);
System.out.println(Arrays.toString(data)); // [2, 6, 24, 120]

15、Arrays.parallelSetAll(T[] array, IntFunction)

Integer[] data = {2, 3, 4, 5};
// 第一个元素2不变,将其与第二个元素3一起作为参数x, y传入,得到乘积6,作为数组新的第二个元素
// 再将6和第三个元素4一起作为参数x, y传入,得到乘积24,作为数组新的第三个元素,以此类推
Arrays.parallelPrefix(data, (x, y) -> x * y);
System.out.println(Arrays.toString(data)); // [2, 6, 24, 120]

16、Arrays.spliterator(T[] array)

  • 返回数组的分片迭代器,用于并行遍历数组。
public class Students {

    private String name;

    private Integer age;

    public Students(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    // 省略get、set方法
}

public static void main(String[] args) {
    Students[] data = new Students[5];
    IntStream.range(0,5).forEach(i -> data[i] = new Students("小明"+i+"号", i));
    // 返回分片迭代器
    Spliterator<Students> spliterator = Arrays.spliterator(data);
    spliterator.forEachRemaining(stu -> {
        System.out.println("学生姓名: " + stu.getName() + "  " + "学生年龄: " + stu.getAge());
        // 学生姓名: 小明0号  学生年龄: 0
        // 学生姓名: 小明1号  学生年龄: 1
        // 学生姓名: 小明2号  学生年龄: 2
        // 学生姓名: 小明3号  学生年龄: 3
        // 学生姓名: 小明4号  学生年龄: 4
    });
}

17、Arrays.stream(T[] array)

  • 返回数组的流Stream,然后我们就可以使用Stream相关的许多方法了。
Integer[] data = {1, 2, 3, 4};
List<Integer> list = Arrays.stream(data).collect(toList());
System.out.println(list); // [1, 2, 3, 4]

六、java工具类

1、 org.apache.commons.io.IOUtils

closeQuietly:关闭一个IO流、socket、或者selector且不抛出异常,通常放在finally块。

toString:转换IO流、 Uri、 byte[]为String。

copy:IO流数据复制,从输入流写到输出流中,最大支持2GB。

toByteArray:从输入流、URI获取byte[]。

write:把字节. 字符等写入输出流。

toInputStream:把字符转换为输入流。

readLines:从输入流中读取多行数据,返回List<String>。

copyLarge:同copy,支持2GB以上数据的复制。

lineIterator:从输入流返回一个迭代器,根据参数要求读取的数据量,全部读取,如果数据不够,则失败。

2、 org.apache.commons.io.FileUtils

deleteDirectory:删除文件夹。

readFileToString:以字符形式读取文件内容。

deleteQueitly:删除文件或文件夹且不会抛出异常。

copyFile:复制文件。

writeStringToFile:把字符写到目标文件,如果文件不存在,则创建。

forceMkdir:强制创建文件夹,如果该文件夹父级目录不存在,则创建父级。

write:把字符写到指定文件中。

listFiles:列举某个目录下的文件(根据过滤器)。

copyDirectory:复制文件夹。

forceDelete:强制删除文件。

3、 org.apache.commons.lang.StringUtils

isBlank:字符串是否为空 (trim后判断)

isEmpty:字符串是否为空 (不trim并判断)

equals:字符串是否相等

join:合并数组为单一字符串,可传分隔符

split:分割字符串

EMPTY:返回空字符串

trimToNull:trim后为空字符串则转换为null

replace:替换字符串

4、org.apache.http.util.EntityUtils

toString:把Entity转换为字符串

consume:确保Entity中的内容全部被消费。可以看到源码里又一次消费了Entity的内容,假如用户没有消费,那调用Entity时候将会把它消费掉

toByteArray:把Entity转换为字节流

consumeQuietly:和consume一样,但不抛异常

getContentCharset:获取内容的编码

5、 org.apache.commons.lang3.StringUtils

isBlank:字符串是否为空 (trim后判断)

isEmpty:字符串是否为空 (不trim并判断)

equals:字符串是否相等

join:合并数组为单一字符串,可传分隔符

split:分割字符串

EMPTY:返回空字符串

replace:替换字符串

capitalize:首字符大写

6、 org.apache.commons.io.FilenameUtils

getExtension:返回文件后缀名

getBaseName:返回文件名,不包含后缀名

getName:返回文件全名

concat:按命令行风格组合文件路径(详见方法注释)

removeExtension:删除后缀名

normalize:使路径正常化

wildcardMatch:匹配通配符

seperatorToUnix:路径分隔符改成unix系统格式的,即/

getFullPath:获取文件路径,不包括文件名

isExtension:检查文件后缀名是不是传入参数(List<String>)中的一个

7、 org.springframework.util.StringUtils

hasText:检查字符串中是否包含文本

hasLength:检测字符串是否长度大于0

isEmpty:检测字符串是否为空(若传入为对象,则判断对象是否为null)

commaDelimitedStringToArray:逗号分隔的String转换为数组

collectionToDelimitedString:把集合转为CSV格式字符串

replace 替换字符串

delimitedListToStringArray:相当于split

uncapitalize:首字母小写

collectionToDelimitedCommaString:把集合转为CSV格式字符串

tokenizeToStringArray:和split基本一样,但能自动去掉空白的单词

8、 org.apache.commons.lang.ArrayUtils

contains:是否包含某字符串

addAll:添加整个数组

clone:克隆一个数组

isEmpty:是否空数组

add:向数组添加元素

subarray:截取数组

indexOf:查找某个元素的下标

isEquals:比较数组是否相等

toObject:基础类型数据数组转换为对应的Object数组

9、 org.apache.commons.lang.StringEscapeUtils

参考十五:org.apache.commons.lang3.StringEscapeUtils

10、 org.apache.http.client.utils.URLEncodedUtils

format:格式化参数,返回一个HTTP POST或者HTTP PUT可用application/x-www-form-urlencoded字符串

parse:把String或者URI等转换为List<NameValuePair>

11、 org.apache.commons.codec.digest.DigestUtils

md5Hex:MD5加密,返回32位字符串

sha1Hex:SHA-1加密

sha256Hex:SHA-256加密

sha512Hex:SHA-512加密

md5:MD5加密,返回16位字符串

12、org.apache.commons.collections.CollectionUtils

isEmpty:是否为空

select:根据条件筛选集合元素

transform:根据指定方法处理集合元素,类似List的map()

filter:过滤元素,雷瑟List的filter()

find:基本和select一样

collect:和transform 差不多一样,但是返回新数组

forAllDo:调用每个元素的指定方法

isEqualCollection:判断两个集合是否一致

13、 org.apache.commons.lang3.ArrayUtils

contains:是否包含某个字符串

addAll:添加整个数组

clone:克隆一个数组

isEmpty:是否空数组

add:向数组添加元素

subarray:截取数组

indexOf:查找某个元素的下标

isEquals:比较数组是否相等

toObject:基础类型数据数组转换为对应的Object数组

14、org.apache.commons.beanutils.PropertyUtils

getProperty:获取对象属性值

setProperty:设置对象属性值

getPropertyDiscriptor:获取属性描述器

isReadable:检查属性是否可访问

copyProperties:复制属性值,从一个对象到另一个对象

getPropertyDiscriptors:获取所有属性描述器

isWriteable:检查属性是否可写

getPropertyType:获取对象属性类型

15、 org.apache.commons.lang3.StringEscapeUtils

unescapeHtml4:转义html

escapeHtml4:反转义html

escapeXml:转义xml

unescapeXml:反转义xml

escapeJava:转义unicode编码

escapeEcmaScript:转义EcmaScript字符

unescapeJava:反转义unicode编码

escapeJson:转义json字符

escapeXml10:转义Xml10

这个现在已经废弃了,建议使用commons-text包里面的方法。

16、org.apache.commons.beanutils.BeanUtils

copyPeoperties:复制属性值,从一个对象到另一个对象

getProperty:获取对象属性值

setProperty:设置对象属性值

populate:根据Map给属性复制

copyPeoperty:复制单个值,从一个对象到另一个对象

cloneBean:克隆bean实例

你可能感兴趣的:(工具辅助,JAVA基础)