一、 集合框架
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相等的数组。 将集合变成数组:限定了对集合中的元素进行增删操作,只能获取这些元素。
为避免误导初学者,本博客如有错误或者不严谨的地方,请在下方给予评论,以便及时修改!谢谢... ...