集合

一、 集合框架

Iterable:(接口)
	|-Collection:(接口)
		|-List:(接口)
			|-ArrayList
			|-LinkedList
			|-Vector
		|-Set:(接口)
			|-HashSet
				|-LinkedHashSet
			|-TreeSet
Iterator:迭代器
	|-ListIterator:列表迭代器(List集合特有的迭代器)	
Map:(接口)
	|-HashMap
	|-TreeMap
	|-HashTable
		|-Properties
Utilities:工具类
	Collections:集合工具类
	Arrays:数组工具类
</span>

二、 List集合 

List集合的特点:

         List集合代表一个元素有序的,可重复的集合,集合中每一个元素都有其对应的顺序索引。List集合允许使用重复元素,可以通过索引访问指定位子的集合 元素。List集合默认按照元素的添加顺序设置元素索引,例如第一个元素索引为0,第二个添加的元素索引为1...

List是Collection接口的子接口,可以使用Collection接口中的全部方法。而且List集合时有序的,因此List集合中增加了一些根据索引来操作集合汇总元素的方法:

	void add(int index,Object element):将元素插入到List集合的index处。
	boolean addAll(int index,Collection c):将集合c包含的所有元素插入到List集合的index处。
	Object get(int index):返回集合index索引处的元素。
	int indexOf(Object o):返回对象o在List集合中第一次出现的位置索引。
	int lastIndexOf(Object o):返回对象o在List集合中最后一次出现的位置索引。
	Object remove(int index):删除并返回index索引处的元素。
	Object set(int index, Object element):将index处的元素替换为element对象,返回新元素。
	List subList(int fromIndex,int toIndex):返回从索引fromIndex(包含)到索引toIndex(不包含)</span>

         List集合中元素因为有角标所以有了类似于数组的获取元素的方式。在随List集合中的元素进行迭代时,如果想在迭代过程中对元素进行操作(比如满足条件添加新元素),会发生ConcurrentModificationException并发修改异常。导致这种问题的原因是:集合引用和迭代器引用在同时操作元素,通过集合获取到对应迭代后,在迭代中进行集合引用的元素添加,迭代器并不知道,所以会出现异常情况。

         基于上边的问题,List与Set集合不同,List集合不但提供了iterator()方法外,还额外提供了一个listIterator()方法,该方法返回一个ListIterator对象,ListIterator接口继承了Iterator接口,提供了专门操作List的方法。ListIterator接口在Iterator接口基础上增加了如下方法,在迭代过程中对元素进行增删改查:

         booleanhasPrevious():返回该迭代器关联的集合是否还有上一个元素。
         Objectprevious():返回迭代器的上一个元素。
         voidadd():在指定位置插入一个元素。
         voidremove():从列表中移除由next或者previous返回的最后一个元素。
         voidset(E e):用指定元素替换由next或者previous返回的最后一个元素。
         intnextIndex():返回对 next 的后续调用所返回元素的索引。
1、ArrayList

         底层的数据结构使用的是数组结构,特点在于查询速度很快,但是增删稍慢,线程不同步。

ArrayList集合代码示例:
/*
需求:将自定义的对象作为元素存到ArrayList集合中,并去除重复元素。同姓名同年龄,视为重复元素。
思路:
1.对人描述,将数据封装进人对象
2.定义容器。将人存入。
3.取出
List集合判断元素是否相同,依据的是元素的equals方法。
*/
import java.util.*;
class Person
{
	private String name;
	private int age;
	Person(String name, int age)
	{
		this.name = name;
		this.age = age;
	}
	public String getName()
	{
		return name;
	}
	public int getAge()
	{
		return age;
	}
	public String toString()
	{
		return name+"..."+age;
	} 
	//重写equals()方法来判断相同元素。
	public boolean equals(Object obj)
	{
		if(!(obj instanceof Person))
			throw new RuntimeException("不是Person对象");
		Person p = (Person)obj;
		return this.name.equals(p.name) && this.age==(p.age);
	}
}

class ArrayListDemo
{
	public static void main(String[] args)
	{
		ArrayList al = new ArrayList();
		al.add(new Person("lisi1",31));
		al.add(new Person("lisi2",32));
		al.add(new Person("lisi2",32));
		al.add(new Person("lisi3",33));
		al.add(new Person("lisi4",34));
		al.add(new Person("lisi4",34));
		
		sop(al.toString());
		al = singleElement(al);
		sop(al.toString());
		
		
		Iterator it = al.iterator();
		while(it.hasNext())
		{	
			Person p = (Person)it.next();
			sop(p.getName()+":"+p.getAge());
		}
		
	}
	public static ArrayList singleElement(ArrayList al)
	{
		//定义一个临时容器newAl,判断如果不包含该元素就添加进集合,否则不添加。
		ArrayList newAl = new ArrayList();
		
		Iterator it = al.iterator();
		while(it.hasNext())
		{
			Object obj = it.next();	
			if(!newAl.contains(obj))//contains的底层原理就是equals,所以要复写equals
				newAl.add(obj);//调用contains运行equals
		}
		return newAl;
	}	
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}
2、LinkedList

 底层数据结构使用的是链表结构,特点是增删的速度很快,查询速度慢。

         LinkedList的特有方法:

	void addFirst(E e):将指定元素插入此列表的开头。 
	void addLast(E e):将指定元素添加到此列表的结尾。 
	E getFirst():返回此列表的第一个元素。 
	E getLast():返回此列表的最后一个元素。 
	E removeFirst():移除并返回此列表的第一个元素。 
	E removeLast():移除并返回此列表的最后一个元素。 
	(在JDK1.6出现以上方法的替代方法)
LinkedList集合代码示例:
/*
需求:使用linkedList模拟一个堆栈或者队列的数据结构
堆栈:先进后出:如同一个杯子
队列:先进先出:First in First out: FIFO:如同一个水管。
*/
import java.util.*;
class DuiLie
{
	private LinkedList link;
	DuiLie()
	{
		link = new LinkedList();
	}
	public void myAdd(Object obj)
	{
		link.addFirst(obj);
	}
	public Object myGet()
	{
		//return link.removeLast();//先进先出
		return link.removeFirst();//先进后出
	}
	public boolean isNull()
	{
		return link.isEmpty();
	}
}
class LinkedListDemo
{
	public static void main(String[] args)
	{
		DuiLie dl = new DuiLie();
		dl.myAdd("java1");
		dl.myAdd("java2");
		dl.myAdd("java3");
		dl.myAdd("java4");
		
		while(!dl.isNull())
		{
			System.out.println(dl.myGet());
		}
	
		sop(dl.isNull());//true,元素被全部去除
	}
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}
3、Vector

 底层的数据结构是数组结构,线程是同步的,查询和增删速度都很慢,被ArrayList取代。

         ArrayList和Vector的显著区别是:ArrayList是线程不安全的,当多个线程访问同一个ArrayList集合时,如果有超过一个线程修改了ArrayList集合,则程序必须手动保证该线程集合的同步性;但是Vector集合则是线程安全的,无需程序保证该集合的同步性。因为Vector线程安全,所以它的性能要比ArrayList低的多。推荐使用ArrayList,并且Collections工具类可以将ArrayList变成线程安全的。

三、 Set集合

Set集合的特点:

         Set接口中的方法和Collection中方法一致的。Set接口取出方式只有一种:Iterator迭代器。Set元素是无序的(存入和取出的顺序不一定一致),该集合体系没有索引。Set集合不允许包含相同的元素,如果试图把两个相同的元素加入同一个Set集合中,则添加操作失败,add()方法返回false,且新元素不会被加入。Set集合判断两个元素是否相同使用的是equals方法,只要两个对象用equals方法返回true,Set就不会接受这两个对象;反之,只要两个对象用equals方法返回true,Set就会接受这两个对象。

1、HashSet

         底层数据结构式哈希表,线程不同步的,高效的。当想HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据该对象的hashCode值决定该对象在HashSet集合中的寻出位置。

         HashSet集合保证元素唯一性:通过元素的hashCode方法,和equals方法完成的。当元素的hashCode值相同时,才继续判断元素的equals是否为true。如果为true,那么视为相同元素,不存。如果为false,那么存储。 如果hashCode值不同,那么不判断equals,从而提高对象比较的速度。

HashSet是线程不同步的,如果有多线程访问一个HashSet集合,需要用代码保证其同步。

         HashSet集合代码示例:

/*
需求:往HashSet存入自定义对象,姓名和年龄相同为同一个对象
*/
import java.util.*;
class HashSetDemo
{	
	public static void main(String[] args)
	{
		HashSet hs = new HashSet();
		
		hs.add(new Person("al",11));
		hs.add(new Person("a2",12));
		hs.add(new Person("a2",12));
		hs.add(new Person("a3",13));
		hs.add(new Person("a4",14));
		
		sop(hs.toString());
		Iterator it = hs.iterator();
		 
		while(it.hasNext())
		{
			Person p  = (Person)it.next();
			sop(p.getName()+"..."+p.getAge());
		}
	}
	public static void sop(Object obj)
	{ 
		System.out.println(obj);
	}
}
class Person
{
	private String name;
	private int age;
	Person(String name, int age)
	{
		this.name = name;
		this.age = age;
	}
	public String getName()
	{
		return name;
	}
	public int getAge()
	{
		return age;
	}
	public String toString()
	{
		return name+"="+age;
	}
	//重写hashCode()方法
	public int hashCode()
	{
		return name.hashCode()+age;
		//return name.hashCode()+age*39;尽量保证hashCode的唯一性
	}
	//重写equals()方法
	public boolean equals(Object obj)
	{
		if(!(obj instanceof Person))
			throw new RuntimeException("不是Person对象");
		Person p = (Person)obj;
		return this.name.equals(p.name) && this.age==(p.age);
	} 
}

         LinkedHashSet:HashSet的子类,LinkedHashSet也是根据元素的hashCode值来决定元素的存储位置,但它同时使用链表维护元素的次序,这样可以使元素看起来是以插入的顺序保存的。也就是说,当遍历LinkedHashSet集合里的元素时,LinkedHashSet将会按元素添加顺序来访问集合里的元素。所以性能略低于HashSet。

哈希表的原理: 

1,对对象元素中的关键字(对象中的特有数据),进行哈希算法的运算,并得出一个具体的算法值,这个值 称为哈希值。

2,哈希值就是这个元素的位置。 

3,如果哈希值出现冲突,再次判断这个关键字对应的对象是否相同。如果对象相同,就不存储,因为元素重复。如果对象不同,就存储,在原来对象的哈希值基础 +1顺延。

4,存储哈希值的结构,我们称为哈希表。 

5,既然哈希表是根据哈希值存储的,为了提高效率,最好保证对象的关键字是唯一的。 这样可以尽量少的判断关键字对应的对象是否相同,提高了哈希表的操作效率。

 对于ArrayList集合,判断元素是否存在,或者删元素底层依据都是equals方法。 

对于HashSet集合,判断元素是否存在,或者删除元素,底层依据的是hashCode方法和equals方法。

2、TreeSet

         TreeSet集合底层数据结构是二叉树,保证元素唯一性的依据是compareTo方法的结果是否为0,如果return 0,视为两个对象重复。

TreeSet集合可以对集合中元素进行排序,排序需要依据元素自身具备的比较性。如果元素不具备比较性,在运行时会发生ClassCastException异常。 所以需要元素实现Comparable接口,强制让元素具备比较性,复写compareTo方法。 依据compareTo方法的返回值,确定元素在TreeSet数据结构中的位置。

注意:在进行比较时,如果判断元素不唯一,比如:同姓名,同年龄,才视为同一个人。 在判断时,需要分主要条件和次要条件,当主要条件相同时,再判断次要条件,按照次要条件排序。 

TreeSet集合排序有两种方式:

1、让元素自身具备比较性,需要让元素对象实现Comparable接口,并覆盖compareTo方法。 这种方式成为元素的自然排序,或者默认排序。

         原理:Comparable接口中定义了一个compareTo(Objectobj)方法,该方法返回一个整数值,实现该接口的类必须实现该方法,实现给接口的类的对象就可以比较大小了。当一个对象调用该方法与另一个对象进行比较时,例如:obj1.compareTo(obj2),如果该方法返回0,则表明这两个对象相等,如果返回一个正整数,则表明obj1>obj2;如果返回一个负整数,则表明obj1<obj2。

2、让集合自身具备比较性,需要定义一个实现Comparator接口的比较器,并覆盖compare方法,并将该对象作为实际参数传递给TreeSet集合的构造函数。

原理:Comparator接口中包含一个intcompare(T o1,T o2)方法,该方法用于比较o1和o2的大小:如果该方法返回正整数,则表明o1>o2;如果返回0,则表明o1=o2;如果方法返回负整数,则表明o1<o2。

TreeSet两种排序代码示例:
import java.util.*;
class TreeSetDemo
 {
	public static void main(String[] args)
	{
		method1();
		sop("----------------------");
		method2();
		sop("----------------------");
		method3();
	}
	//按照元素自然顺序排序(其实String类中已经实现了Comparable接口并覆写了comparaTo方法)
	public static void method1()
	{
		TreeSet ts = new TreeSet();
		
		ts.add("acbd");		
		ts.add("cd");		
		ts.add("cbd");		
		ts.add("bcbd");		
		
		Iterator it = ts.iterator();
		while(it.hasNext())
		{
			sop(it.next());
		}
		sop("按照自然顺序排序");
	}
	
	public static void method2()
	{
		TreeSet ts = new TreeSet();
		
		ts.add(new Student("zhangsan",21));		
		ts.add(new Student("lisi",22));		
		ts.add(new Student("nlisi",22));		
		ts.add(new Student("wangwu",23));		
		ts.add(new Student("zhaoliu",24));		
	
		
		Iterator it = ts.iterator();
		while(it.hasNext())
		{
			Student s = (Student)it.next();
			
			sop(s.getName()+"...."+s.getAge());
		}
		sop("按照年龄排序,如果年龄相等,按照姓名字母排序");
	}
	
	public static void method3()
	{
		TreeSet ts = new TreeSet(new MyComparator());
		
		ts.add(new Student("zhangsan",21));		
		ts.add(new Student("lisi",22));		
		ts.add(new Student("lisi",23));		
		ts.add(new Student("wangwu",23));		
		ts.add(new Student("zhaoliu",24));		
			
		Iterator it = ts.iterator();
		while(it.hasNext())
		{
			Student s = (Student)it.next();
			
			sop(s.getName()+"...."+s.getAge());
		}	

		sop("按照姓名排序,如果姓名相同,按照年龄升序排序");
	}

	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}

class Student implements Comparable
{
	private int age;
	private String name;
	Student(String name,int age)
	{
		this.name = name;
		this.age = age;
	}
	public int getAge()
	{
		return age;
	}
	public String getName()
	{
		return name;
	}
	public int compareTo(Object obj)//按照年龄排序,如果年龄相等,按照姓名排序
	{
		if(!(obj instanceof Student))
			throw new RuntimeException("不是学生对象");
			
		Student s = (Student)obj;
		if(this.age>s.age)
			return 1;
		if(this.age==s.age)
		{
			return this.name.compareTo(s.name);
		}
		return -1;
	}	
}
//按照姓名排序,如果姓名相同,按照年龄升序排序
class MyComparator implements Comparator
{
	public int compare(Object o1,Object o2)
	{
		Student s1 = (Student)o1;
		Student s2 = (Student)o2;
		
		int num = s1.getName().compareTo(s2.getName());	
		if(num==0)
			return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));	
		return num;
	}
}

使用集合的技巧: 
看到Array就是数组结构,有角标,查询速度很快。 
看到link就是链表结构:增删速度快,而且有特有方法。addFirst; addLast; removeFirst(); removeLast(); getFirst();getLast(); 
看到hash就是哈希表,就要想要哈希值,就要想到唯一性,就要想到存入到该结构的中的元素必须覆盖hashCode,equals方法。 
看到tree就是二叉树,就要想到排序,就想要用到比较。 

四、 迭代器

Iterable接口中有一个抽象方法:iterator(),其返回值类型是一个Iterator接口类型。

Iterator接口也是Java集合框架成员,但它与Collection、Map集合不同。前者称为迭代器,主要用于遍历Collection集合中的元素,后者主要用于盛装其他对象。Iterator接口隐藏了各种Collection实现类的底层细节,向应用程序提供了遍历Collection集合元素的统一编程接口。

Iterator接口中定义了三个抽象方法:

boolean hasNext():如果被迭代的集合元素还没有被遍历,则返回true。

Object next():返回集合里的下一个元素。

void remove():删除集合中上一次next方法返回的元素。

注意:

Set和List接口的实现子类复写iterator()方法,其方法内部返回一个实现Iterator接口的子类,子类复写Iterator接口中的三个抽象方法。于是我们在使用迭代器时,Iterator it = al.iterator();其实是多态-子类对象指向接口父类引用。于是我们看到it.hasNext()、it.next(),运行时使用的是子类的方法。

五、 Map集合

Map集合的特点:

         Map集合保存具有映射关系的数据,因此Map集合里保存着两组值,一组值用于保存Map中的key,另外一组值用于保存Map集合中的value。key和value可以是任何引用类型的数据。Map的key不允许重复,即同一个Map对象的任何两个key通过equals方法比较总是返回false。

1、HashTable:底层数据结构是哈希表数据,是线程安全的。不可以存储null键、null值。

         |-Properties:在“黑马程序员:IO流”中有具体讲解。

2、HashMap :底层数据结构是哈希表数据结构,是线程不安全的。可以存储null键、null值,替代HashTable

3、TreeMap:底层数据结构是二叉树数据结构,可以对Map集合中的键进行指定顺序的排序。

Map集合中处了一些添加、获取、判断、删除方法外,有两个重要的方法:

Set<K>keySet()返回此映射中包含的键的Set 视图。

Set<Map.Entry<K,V>>entrySet():返回此映射中包含的映射关系的Set 视图。

因为Map集合中没有迭代器,怎么获取Map集合中的所有元素呢?把Map集合转换为Set集合,用Set集合中的Iterator迭代器间接取出Map集合的所有元素。

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

1、Set<K>keySet():将Map集合中的所有键存放在Set集合中,然后通过Set集合迭代器的方式取出所有的键,再根据get(key)方法获取到Map集合键对应的值。

2、Set<Map.Entry<K,V>>entrySet():将Map集合的映射关系存放在Set集合中,这个关系的类型是Map.Entry,然后根据getKey(),getValue()方法获取到Map集合的键和值。

取出Map集合元素的两种方式代码示例:

import java.util.*;
class MapDemo
{
	public static void main(String[] args)
	{
		Map<String,String> m = new HashMap<String,String>();
		
		m.put("01","DaZhuang01");
		m.put("04","DaZhuang04");
		m.put("02","DaZhuang02");
		m.put("03","DaZhuang03");
		
		//将Map集合中的所有键存放在Set集合中
		Set<String> keySet = m.keySet();
		
		Iterator<String> it = keySet.iterator();
		while(it.hasNext())
		{
			String key = it.next();//获取Map集合中的key
			String value = m.get(key);//通过键获取Map集合中的vlaue
			sop("key:"+key+"______"+"value:"+value);
		}
		
		
		//将Map集合的映射关系存放在Set集合中
		Set<Map.Entry<String,String>> entrySet = m.entrySet();
		
		Iterator<Map.Entry<String,String>> i = entrySet.iterator();
		while(i.hasNext())
		{
			Map.Entry<String,String> me = i.next();//把获取到的键值对存放在Map.Entry类型的引用变量中。
			String key = me.getKey();//获取key
			String value = me.getValue();//获取value
			sop("key:"+key+"......"+"value:"+value);
		}
	}
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}

六、  Collections工具类: 

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

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

Collections工具类的常用方法:

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

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

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

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

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

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

将线程不安全的集合进行同步控制的方法:

Collection coll= Collections.synchronizedCollection(new ArrayList());

List  list = Collections.synchronizedList(new ArrayList()); 

Map map = Collections.synchronizedMap(new Map()); 

七、 Arrays工具类: 

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

1、将数组转换成list集合: Arrays.asList(arr);

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

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

将数组转换成集合,有什么好处呢?用aslist方法,将数组变成集合;可以通过list集合中的方法来操作数组中的元素:isEmpty()、contains、indexOf、set;  

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

如果数组中存储的引用数据类型,数组元素直接作为集合的元素存在,就可以直接用集合方法操作。 如果数组中存储的是基本数据类型,asList会将数组实体作为集合元素存在。

2、集合变数组:toArray(); 

用的是Collection接口中的方法:toArray(); 

ArrayList<String> al = newArrayList<String>();               

al.add("abc1");

al.add("abc2");

al.add("abc3");

String[] arr = al.toArray(new String[3]);//创建长度为3的数组是最优的。

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

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

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

为避免误导初学者,本博客如有错误或者不严谨的地方,请在下方给予评论,以便及时修改!谢谢... ...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值