Java集合类

如下是一个集合关系图:

相关概念

  1 Collection接口 Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection 的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。

  2 Map接口 Map集合没有继承Collection接口,其提供的是key到value的映射,Map中不能包含相同的key值,每个key只能影射一个相同的value.key值还决定了存储对象在映射中的存储位置。

Collection的子接口

   1 Set接口 Set中的元素是无序的,元素不可重复。其主要用到的方法有如下:

   1)添加:

add(object)//添加一个元素

addAll(Collection)//添加一个集合中的所有元素。

   2)删除:

clear()//将集合中的元素全删除,清空集合。

remove(obj)//删除集合中指定的对象。注意:删除成功,集合的长度会改变。

removeAll(collection)//删除部分元素。部分元素和传入Collection一致。

  3)判断:

boolean contains(obj)//集合中是否包含指定元素 。

boolean containsAll(Collection)//集合中是否包含指定的多个元素。

boolean isEmpty()//集合中是否有元素。 

  4)获取:

int size()//集合中有几个元素。

  5)取交集:

boolean  retainAll(Collection)//对当前集合中保留和指定集合中的相同的元素。如果两个集合元素相同,返回flase;如果retainAll修改了当前集合,返回true。

  6)获取集合中所有元素:

Iterator  iterator()//迭代器

  7)将集合变成数组:

toArray()

     实现了Set接口的主要有HashSet、TreeSet它们的共同特点是集合中的元素是不可重复的,但它们之间是有一定的区别的其各自对应的特点如下:

    HashSet:HashSet底层数据结构是哈希表。HashSet是通过以下的方式去保证元素的唯一性的:主要通过两个方法,hashcode方法和equals方法来完成。(hashcode和equals方法在Object类中都有实现),当试图往 HashSet中存放一个新元素的时候会调用该元素的hashcode方法获取该元素的hash值,如果hash值与已经存入的元素的hash值不相等,则将该元素存入HashSet。若尝试加入的元素与集合原有的元素hash值相等调用尝试加入的元素的equals方法若该方法相等则不能往HashSet中添加此元素若不相等则可以添加。(一般开发的时候,只要描述的这个事物往集合存的时候要该事物的equals和hashcode方法,对于判断集合中元素是否存在,以及删除等操作,依赖的是hashcode和equals方法)。

    TreeSet:TreeSet底层是二叉树结构,可以对其集合的元素进行排序。其保证元素唯一性的依据是通过元素的compareTo方法或集合的比较器中comparetor方法对元素比较并进根据方法返回的整数对元素进行排序,这两种排序方式可以分别称为自然排序和定制排序,以下为这两种排序的特点: 

    自然排序:要实现自然排序必须要先让元素自身具有比较性。元素需要实现comparable接口,覆盖compareTo方法。该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现该接口的类必须实现该方法,实现了该接口的类的对象就可以比较大小。当一个对象调用该方法与另一个对象进行比较,例如obj1.comparTo(obj2),如果该方法返回0,则表明这两个对象相等;如果返回一个正整数,则表明obj1大于obj2;如果该方法返回一个负整数,则表明obj1小于obj2. 如下程序:                            

class Compare implements Comparable 
{ 
    int count; 
    public Compare(int count) 
    { 
        this.count = count; 
    } 
    public int compareTo(Object obj) 
    { 
        if(obj instanceof Compare){
             Compare d=(Compare)obj;
              if(this.count==d.count){
                    return 0;
               }else if(this.count>d.count){
                    return 1;
                }
         } 
        return -1;
    } 
} 
    定制排序:当元素自身不具备比较性的时候即元素没有实现comparable接口,或者具备的比较性不是所需要的,这时就需要让集合自身具备比较性。 TreeSet的自然排序是根据集合元素的大小,TreeSet将他们以升序排列。如果需要实现定制排序,例如降序,则可以使用Comparator接口。该接口里包含一个int compare(T o1, T o2)方法,该方法用于比较o1和o2的大小。 如果需要实现定制排序,则需要在创建TreeSet集合对象时,并提供一个Comparator对象与该TreeSet集合关联,由该Comparator对象负责集合元素的排序逻辑。如下程序:

class T {
	int count;

	public T(int count) {
		this.count = count;
	}

	public String toString() {
		return "(count:" + count + ")";
	}
}

public class TestTreeSet {
	public static void main(String[] args) {
		TreeSet ts = new TreeSet(new Comparator() {
			public int compare(Object o1, Object o2) {
				T t1 = (T) o1;
				T t2 = (T) o2;
				if (t1.count < t2.count) {
					return -1;
				} else if (t1.count == t2.count) {
					return 0;
				} else {
					return 1;
				}
			}
		});
		ts.add(new T(1));
		ts.add(new T(2));
		ts.add(new T(3));
		System.out.println(ts);
	}
}
打印结果为:

[(count:1), (count:2), (count:3)]

  2 List接口 List集合中的元素是有序的,元素可以重复。因为该集合体系有索引。

1)添加:

add(index,element)//在指定的索引位插入元素。

addAll(index,collection)//在指定的索引位插入一堆元素。

2)删除:

remove(index)//删除指定索引位的元素。 返回被删的元素。

3)获取:

Object get(index)//通过索引获取指定元素。

int indexOf(obj)//获取指定元素第一次出现的索引位,如果该元素不存在返回-1;

  所以,通过-1,可以判断一个元素是否存在。

int lastIndexOf(Object o)//反向索引指定元素的位置。

List subList(start,end)//获取子列表。

4)修改:

Object set(index,element)//对指定索引位进行元素的修改。

5)获取所有元素:

ListIterator listIterator()//list集合特有的迭代器。

List集合支持对元素的增、删、改、查。

List集合因为角标有了自己的获取元素的方式: 遍历。

for(int x=0; x<list.size(); x++){
sop("get:"+list.get(x));
}
  在进行list列表元素迭代的时候,如果想要在迭代过程中,想要对元素进行操作的时候,比如满足条件添加新元素。 会发生.ConcurrentModificationException并发修改异常。

导致的原因是:

集合引用和迭代器引用在同时操作元素,通过集合获取到对应的迭代器后,在迭代中,进行集合引用的元素添加,迭代器并不知道,所以会出现异常情况。

如何解决呢?

既然是在迭代中对元素进行操作,找迭代器的方法最为合适.可是Iterator中只有hasNext,next,remove方法.通过查阅的它的子接口,ListIterator,发现该列表迭代器接口具备了对元素的增、删、改、查的动作。

ListIterator是List集合特有的迭代器。

ListIterator it = list.listIterator//取代Iterator it = list.iterator;

List常用的实现类有以下几个。

ArrayList:底层的数据结构使用的是链表数据结构。特点是1.查询很快增删稍慢2.线程不同步。底层实现是通过可变长度数组,每次长度增加为数组的  150%,并将旧数组的数据复制新数组中。开始默认长度为10。vector与之区别为每次长度增加为数组的100%区别。另外相对于set,List还有除迭代器外的迭代方式其事例代码如下:

public class ArrayListTest {
	public static void main(String args[]) {
		List<String> list = new ArrayList<String>();
		// 添加元素
		list.add("aaa");
		list.add("bbb");
		list.add("aaa");
		list.add("ddd");
		list.add("ccc");
		list.add(null);
		// 遍历
		for (String string : list) {
			System.out.println(string);
		}
		System.out.println("---------------");
		// 修改
		list.set(1, "bbb2");
		// 删除
		list.remove("ccc");
		// 迭代器遍历
		Iterator<String> iterator = list.iterator();
		while (iterator.hasNext()) {
			System.out.println(iterator.next());
		}
		System.out.println("--------------");
		list.clear(); // 清空列表
		System.out.println("清空后list的大小" + list.size());// 打印大小
		List<String> list2 = new ArrayList<String>();
		list2.add("a");
		list2.add("b");
		// 将list2添加到list中
		list.addAll(list2);
		// 遍历
		for (String string : list) {
			System.out.println(string);
		}

	}
}
输出结果为:

aaa
    bbb
    aaa
    ddd
    ccc
    null
---------------
    aaa
    bbb2
    aaa
    ddd
    null
--------------
    清空后list的大小0
    a
    b

 LinkedList:底层使用的是链表数据结构。特点增删很快,查询稍慢。以下的代码是用来演示LinkedList和ArrayList随即访问的速度:   

package com.list;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

public class LinkedListTest {
    public static void main(String[] args) {
        List<Integer> arraylist = new ArrayList<Integer>();
        for (int i = 0; i < 5000; i++) {
            arraylist.add(i);
        }
        List<Integer> linkedlist = new LinkedList<Integer>();
        for (int i = 0; i < 5000; i++) {
            linkedlist.add(i);
        }
        Random rand = new Random(5000);
        //随机访问arrayList
        long start = System.currentTimeMillis();
        for (long i = 0; i < 500000; i++) {
            arraylist.get(rand.nextInt(5000));
        }
        long end = System.currentTimeMillis();
        System.out.println("arrayList随机访问时间:"+(end - start));
        //随机访问arrayList
        start = System.currentTimeMillis();
        for (long i = 0; i < 500000; i++) {
            linkedlist.get(rand.nextInt(5000));
        }
        end = System.currentTimeMillis();
        System.out.println("linkedlist随机访问时间:"+(end - start));
    }
}
程序运行结果为:

arrayList随机访问时间:44
         linkedlist随机访问时间:1946

从结果不难得出ArrList的查询功能远比LinkedList出色的结论

Vector:底层数据结构线程同步。被ArrayList替代了。

Map类

Map集合

Map集合存储和Collection有着很大不同:

Collection一次存一个元素;Map一次存一对元素。

Collection是单列集合;Map是双列集合。

Map中的存储的一对元素:一个是键,一个是值,键与值之间有对应(映射)关系。

                 特点:要保证map集合中键的唯一性。

Map集合常用方法:

1) 添加

put(key,value)//当存储的键相同时,新的值会替换老的值,并将老值返回。如果键没有重复,返回null。

 void putAll(Map)

2) 删除

 void clear()//清空

 value remove(key)//删除指定键。

3) 判断

boolean isEmpty()

boolean containsKey(key)//是否包含key

boolean containsValue(value)//是否包含value

4) 取出。

int size()//返回长度

value get(key)//通过指定键获取对应的值。如果返回null,可以判断该键不存在。当然有特殊情况,就是在hashmap集合中,是可以存储null键null值的。

Collection values()//获取map集合中的所有的值。

5) 想要获取map中的所有元素:

原理:map中是没有迭代器的,collection具备迭代器,只要将map集合转成Set集合,可以使用迭代器了。之所以转成set,是因为map集合具备着键的唯一性,其实set集合就来自于map,set集合底层其实用的就是map的方法。

     HashMap:底层是哈希表数据结构,允许存入null值和null键,该集合石不同步的。

     TreeMap:底层是二叉树结构。结构不同步。可以给map集合中的键进行排序。

     Hashtable:底层是哈希表数据结构,不可以存入null键null值。该集合石线程同步的。

     Map集合的元素的两种取出方式:

     1.通过KeySet取出:将Map中的所有键存入到Set集合。因为Set具备迭代器。再用迭代方式取出所有的键,在根据get方法,获取每一个键对应的值。

     2.通过Set<Map.Entry<k,v>> entrySet:将map集合中的映射关系存入到了Set集合中,而这个关系的数据类型就是:Map.Entry.

以下为两种取出方式的事例代码:

public class MapTest       
{   
    public static void main(String[] args)   
     {   
         HashMap<String,String> kmap = new HashMap<String,String>();   
         HashMap<String, String> emap = new HashMap<String, String>();   
           
        //装数据   
        for (int i = 0; i < 100; i++)   
         {   
             kmap.put(""+i, "k"+i);   
         }   
        for (int i = 0; i < 100; i++)   
         {   
             emap.put(""+i, "e"+i);   
         }   
           
         Iterator<String> ktor = kmap.keySet().iterator();   
        while(ktor.hasNext())   
         {   
             System.out.println(kmap.get(ktor.next()));   
         }   
      Iterator<Entry<String, String>> itor = emap.entrySet().iterator();   
        while(itor.hasNext())   
         {   
             Entry<String, String> e = itor.next();   
            //System.out.println(e.getKey());   
             System.out.println(e.getValue());   
         }   
     }   
}  

打印结果为:

k3-k2-k1-k0-
e3-e2-e1-e0-

Util包中的一些其他的类:

Arrays类

用于操作数组对象的工具类,里面都是静态方法。

asList方法:将数组转换成list集合。

String[] arr = {"abc","kk","qq"};

List<String> list = Arrays.asList(arr);//将arr数组转成list集合。

将数组转换成集合,有什么好处呢?用aslist方法,将数组变成集合;

可以通过list集合中的方法来操作数组中的元素:isEmpty()、contains、indexOf、set; 

注意(局限性):数组是固定长度,不可以使用集合对象增加或者删除等,会改变数组长度的功能方法。比如add、remove、clear。(会报不支持操作异常UnsupportedOperationException);

如果数组中存储的引用数据类型,直接作为集合的元素可以直接用集合方法操作。

如果数组中存储的是基本数据类型,asList会将数组实体作为集合元素存在。

集合变数组:用的是Collection接口中的方法:toArray();

  如果给toArray传递的指定类型的数据长度小于了集合的size,那么toArray方法,会自定再创建一个该类型的数据,长度为集合的size。

  如果传递的指定的类型的数组的长度大于了集合的size,那么toArray方法,就不会创建新数组,直接使用该数组即可,并将集合中的元素存储到数组中,其他为存储元素的位置默认值null。

  所以,在传递指定类型数组时,最好的方式就是指定的长度和size相等的数组。将集合变成数组后有什么好处?限定了对集合中的元素进行增删操作,只要获取这些元素即可。

Arrays类和Array类的区别:

ARRAY类提供了动态创建和访问 Java 数组的方法。
ARRAYS此类包含用来操作数组(比如排序和搜索)的各种方法。此类还包含一个允许将数组作为列表来查看的静态工厂。

Date类

日期本地格式化:xx年xx月xx日:                  

import java.text.SimpleDateFormat;
import java.util.Date;

class DateDemo {
	public static void main(String args[]) {
		Date d = new Date();
		System.out.println(d);// 打印时间看不懂,希望格式化
		// 将模式封装到SimpleDateformat对象中。
		SimpleDateFormat sdf = new SimpleDateFormat("y年M月d日E");
		// 调用format方法让模式格式化指定Date对象
		String time = sdf.format(d);
		System.out.println(time);

	}

}

打印结果:

Mon Oct 20 22:37:26 CST 2014
2014年月20日星期一

获取指定日期:                   
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

class DateDemo {
	public static void main(String args[]) throws ParseException {
		//获取2014年10月20日的Date对象
		DateFormat dateFormat1=new SimpleDateFormat("y-M-d");
		Date myDate1=dateFormat1.parse("2014-10-20");
		System.out.println(myDate1);
        //获得2014年10月20日22点36分01秒的Date对象
		DateFormat dataFormat2=new SimpleDateFormat("y-M-d H:m:s");
		  Date myDate2=dataFormat2.parse("2014-10-2 22:48:53");
              System.out.println(myDate2);
	
	}

}

打印:

Mon Oct 20 00:00:00 CST 2014
Thu Oct 02 22:48:53 CST 2014

Properties类:

Properties是hashtable的子类该类主要用于读取以项目的配置文件(以.properties等后缀结束的文件和xml文件)

也就是说它具备map集合的特点。而且它里面存储的键值对都是字符串,是集合中和IO技术相结合的集合容器。该对象的特点:可以用子键值对形式的配置文件。那么加载数据时,需要有固定格式:键=值。

一些常用的操作:

程序执行之前C盘下的info.txt文件内容如下:


执行以下程序,代码如下:

public class loadDemo {
	public static void loadDemo() throws IOException {
		Properties prop = new Properties();
		FileInputStream fis = new FileInputStream("c:/info.txt");
		// 将流中的数据加载进集合,若没有这步新数据会覆盖掉就得数据
		prop.load(fis);
		prop.setProperty("h", "8");// 这里只是设置加载进内存的Properties
		FileOutputStream fos = new FileOutputStream("c:/info.txt");
		prop.store(fos, "gagagaga");//从Properties对象导出properties文件 
		prop.list(System.out);
	}
   public static void main(String args[]) throws IOException {
		loadDemo();
	}
}
执行后文件内容如下:


        Properties类用于记录程序运行的次数,如果使用次数已到,那么给出注册提示。很容易让人想到的是:计数器。可是计数器定义在程序中,随着程序的运行而在内存中存在,并在自增。可是随着该应用程序的退出,该计数器也在内存中消失了。下次启动时,又重新从0开始计数。这样不是需要的。程序即使结束,该计数器的值也存在,下次程序启动在先加载该计数器的值并加1后再重新存储起来。所以要建立一个配置文件。用于记录软件的使用次数。该配置文件使用键值对的形式。这样便于阅读数据并操作数据。键值对数据时map集合。数值是以文件形式存储,使用io技术。那么Map+io-->Properties,配置文件可以实现应用程序数据的共享。

public class RunCount {
   public static void main(String args[]) throws IOException{
	   Properties prop=new Properties();
	   File file=new File("count.ini");
	   if(!file.exists())
		   file.createNewFile();
	   FileInputStream fis=new FileInputStream(file);
	   prop.load(fis);
	   int count=0;
	   String value=prop.getProperty("time");
	   if(value!=null){
		   count=Integer.parseInt(value);
		   if(count>=5){
			   System.out.println("使用次数已到");
			   return;
		   }
		   count++;
		   prop.setProperty("time", count+"");
		   FileOutputStream fos=new FileOutputStream(file);
		   prop.store(fos, "");
		   fos.close();
		   fis.close();
	   }
   }
}

读取xml格式的配置文件test.xml文件如下:

<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
	<entry key="koo">bar</entry>
	<entry key="fu">baz</entry>
</properties>
读取xml的方法:

class Test {
	public static void main(String[] args) {
		File pFile = new File("e:\test.xml"); // properties文件放在e盘下(windows)
		FileInputStream pInStream = null;
		try {
			pInStream = new FileInputStream(pFile);
			Properties p = new Properties();
			p.loadFromXML(pInStream);
			p.list(System.out);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
保存为xml:

public class Test1 {
	public static void main(String[] args) {
		Properties p = new Properties();
		p.setProperty("id", "dean");
		p.setProperty("password", "123456");
		try {
			PrintStream fW = new PrintStream(new File("e:\test1.xml"));
			p.storeToXML(fW, "test");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
通过list 方法将Properties写入Properties文件:
public class Test3 {
	public static void main(String[] args) {
		Properties p = new Properties();
		p.setProperty("id", "dean");
		p.setProperty("password", "123456");
		try {
			PrintStream fW = new PrintStream(new File("e:\test1.properties"));
			p.list(fW);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
扩展:java读取properties文件的六种方法 
1 使用java.util.properties类的load()方法
示例: Inputstream in = new Bufferedinputstream(new FileInputstream(name)) 
      Properties p = new Properties() 
          p.load(in) 

    2 使用java.util.PropertyResourceBundle类的getbundle()方法
     示例: Resourcebundle rb = Resourcebundle.getbundle(name,Locale.getdefault()) 
    3 使用java.util.PropertyResourceBundle类的构造函数
     示例:
Inputstream in = new Bufferedinputstream(new FileInputstream(name)) 
      Resourcebundle rb = new Propertyresourcebundle(in) 
    4 使用class变量的getResourceAsStream()方法
      示例:
Inputstream in = Properties.class.getResourceAsStream(name) 
     Propertiesp = newProperties() 
          p.load(in) 
    5 使用class.getclassloader()所得到的java.lang.classloader的getResourceAsStream()方法
      示例:
Inputstream in = Properties.class.getClassLoader().getResourceAsStream(name) 
      Properties p = new Properties() 
          p.load(in) 

    6 使用java.lang.classloader类的getSystemResourceAsStream()静态方法
      示例:
 Inputstream in = ClassLoader.getSystemreSourceAsstream(name) 
       Properties p = new Properties() 
           p.load(in) 
补充
servlet中可以使用javax.servlet.servletcontext的PropertyResourceBundle()方法
      示例:
Inputstream in = context.getResource=AsStream(path) 
      Properties p = new Properties() 
          p.load(in)

Collections类

   它的出现给集合操作提供了更多的功能。这个类不需要创建对象,内部提供的都是静态方法。

静态方法:

Collections.sort(list);//list集合进行元素的自然顺序排序。

Collections.sort(list,new ComparatorByLen());//按指定的比较器方法排序。

class ComparatorByLen implements Comparator<String> {
	public int compare(String s1, String s2) {
		int temp = s1.length() - s2.length();
		return temp == 0 ? s1.compareTo(s2) : temp;
	}
}

Collections.max(list); //返回list中字典顺序最大的元素。

int index = Collections.binarySearch(list,"zz");//二分查找,返回角标。

Collections.reverseOrder();//逆向反转排序。

Collections.shuffle(list);//随机对list中的元素进行位置的置换。

将非同步集合转成同步集合的方法:Collections中的  XXX synchronizedXXX(XXX);

List synchronizedList(list);

Map synchronizedMap(map);

原理:定义一个类,将集合所有的方法加同一把锁后返回。

Collection 和 Collections的区别:

Collections是个java.util下的类,是针对集合类的一个工具类,提供一系列静态方法,实现对集合的查找、排序、替换、线程安全化(将非同步的集合转换成同步的)等操作。

Collection是个java.util下的接口,它是各种集合结构的父接口,继承于它的接口主要有Set和List,提供了关于集合的一些操作,如插入、删除、判断一个元素是否其成员、遍历等。

使用集合的技巧:

看到Array就是数组结构,有角标,查询速度很快。

看到link就是链表结构:增删速度快,而且有特有方法。addFirst; addLast; removeFirst(); removeLast(); getFirst();getLast();

看到hash就是哈希表,就要想要哈希值,就要想到唯一性,就要想到存入到该结构的中的元素必须覆盖hashCode,equals方法。

看到tree就是二叉树,就要想到排序,就想要用到比较。

比较的两种方式:

一个是Comparable:覆盖compareTo方法;

一个是Comparator:覆盖compare方法。

LinkedHashSet,LinkedHashMap:这两个集合可以保证哈希表有存入顺序和取出顺序一致,保证哈希表有序。

集合什么时候用?

当存储的是一个元素时,就用Collection。当存储对象之间存在着映射关系时,就使用Map集合。

保证唯一,就用Set。不保证唯一,就用List。




















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值