.Net转Java自学之路—基础巩固篇十三(集合)
集合:
集合是用于存储对象的一个工具。
集合与数组的特点
相同点:都是一个容器
不同点:
集合:可以存储对象,只能存储对象,集合长度可变。
数组:可以存储对象,也可以存储基本数据类型,数组长度固定。
容器对象有很多种,通过内部的数据结构来区分,数据结构就是一种数据存储方式。
在不断的将容器对象的相同点抽取的过程中,就出现了集合体系结构,该体系的结构框架称为集合框架。
最顶层的是一个接口:Collection
Collection 表示的是一组对象。
迭代器(Iterator):
Collection集合中对元素的获取方式:hasNext() 判断是否有值、next() 返回迭代的下一个元素、remover() 删除
集合容器都具备取出方式,取出方式并不是一个方法,因为一个方法并不足以完成取出,取出是由多个方法来完成的,为了方便使用,将这些方法封装称了对象;该对象在描述时,这些功能会直接使用到具体集合中的数据和集合特点。所以这个取出元素的描述类,就定义在集合的内部,也就是一个内部类。
因为这个类在直接访问集合中的数据,根据集合自身的数据结构特点,将具体的方法都定义完成后,需要将对象对外提供出去让用户使用,所以就暴露了一个方法,来完成对这个内部类对象的访问。
为了提高扩展性,发现只要是容器,就具备取出功能,但数据结构不同,取出的具体实现一样,这时为了扩展,就抽取了一个接口 Iterator (迭代器)。
Collection coll=new ArrayList(); coll.add("元素1"); coll.add("元素2"); coll.add("元素3"); Iterator iter=coll.iterator(); while(iter.haxNext()){ iter.next(); } //或 for(Iterator iter=coll.iterator();iter.haxNext()){ iter.next(); }
只要是在 Collection 集合体系中,迭代器就是通用的取出方式。
在进行迭代器使用时,next() 在循环中只能定义一个,定义多个会导致数据错误;当next()没有获取到元素时,会发生NoSushElementException异常。
用迭代器取出自定义对象的元素时,在 next() 后要进行类型的转换操作。
Collection coll=new ArrayList(); coll.add(new ClassName("元素1")); coll.add(new ClassName("元素2")); coll.add(new ClassName("元素3")); for(Iterator iter = coll.iterator(); iter.haxNext()){ ClassName cn=(ClassName)iter.next(); cn.ClassNameMethod(); }
集合List: extends Collection
该容器的元素有序,存储顺序和取出顺序一致。该集合中的元素都有索引(角标),可以存储重复的元素。
//List常见基本操作 List list=new ArrayList(); //增 list.add("元素"); list.add(1,"元素");//指定索引位插入元素,其他元素以此延续 //删 list.remover(1);//指定索引位删除元素。被删除的元素会被返回 //改 list.set(1,"New元素");//替换指定索引位元素。会返回被修改掉的元素。 //查 list.get(1);//获取索引位指定的元素。 list.indexOf("元素");//获取元素第一次出现的位置(索引) list.subList(int,int);//根据头尾角标获取子列表,返回List集合
列表迭代器:ListIterator
在进行迭代过程中,若出现了迭代器和容器同时对元素进行操作的情况,很容易引发ConcurrentModificationException(并发修改)异常。对于List集合解决此类异常就用 ListIterator 列表迭代器
ListIterator 时 Iterator 的子接口。并提供了更多的迭代过程中的操作方法;该迭代器只能用于 List 集合。
//ListIterator 常见基本功能 List list=new ArrayList(); list.add("元素1"); list.add("元素2"); list.add("元素3"); list.add("元素4"); ListIterator lit=list.listIterator(); lit.haxNext();//判断 是否有后一个元素 lit.haxPrevious();//判断 是否有前一个元素 lit.add();//添加 lit.set();//修改 lit.next();//取后一个元素 lit.previous();//取前一个元素
ArrayList
底层数据结构是数组结构。实现不同步;jdk1.2 版本 ArrayList 替换 Vector
ArrayList 对元素的查询快,增、删 慢
判断元素是否相同时,底层的依据时 equals() 方法;无论时 contains() 还是 remover() 都会去使用 equals() 来判断元素是否相同。所以在往 ArrayList 集合中存储自定义元素时,最好建立该元素特有的判断对象是否相同的依据。也就是覆盖 equals() 方法。
Vector
底层数据结构是数组结构。实现同步;jdk1.0版本
Vector 对元素的增、删、查 都很慢
elements()方法返回枚举(Enumeration)该功能与迭代器的功能重复;由于枚举(Enumeration)书写麻烦,故被迭代器替换掉。
Vector vt=new Vector(); vt.addElement("元素1"); vt.addElement("元素2"); vt.addElement("元素3"); vt.addElement("元素4"); for(Enumeration ent=vt.elements(); ent.hasMoreElements()){ ent.nextElement(); }
可变长度数组
ArrayList 内部封装了一个默认长度为10的数组。当超出长度时,集合内部会自动生成一个新的数组;将原数组中的数据复制到新的数组中;ArrayList 新的数组长度是原数组的50%延长;Vector 新的数组长度是原数组的100%延长。
LinkedList
底层数据结构是链表结构。实现不同步;jdk1.2 版本;
LinkedList 对元素的增、删效率高,但查询慢
//特有方法 //增 addFirst(); addLast() //获取元素,集合长度不变 getFirst(); getLast(); //jdk1.6 版本后被下面俩个方法替代。若集合中没有元素,该方法不会抛出异常,而返回null peekFirst(); peekLast(); //获取被删除的元素,集合长度改变 removeFirst(); removeLast(); //jdk1.6 版本后被下面俩个方法替代。若集合中没有元素,该方法不会抛出异常,而返回null offerFirst(); offerLast();
集合Set: extends Collection
该容器的元素无序;不可以重复元素。Set 接口的取出元素的方法只有迭代器。
!~ HashSet:
底层数据结构为哈希表。哈希表这种结构其实就是对哈希表的存储;而且每一个对象都有自己的哈希表。因为 Object 基类中有一个 hashCode() 方法。
存储自定义对象并且要保证元素的唯一性。HashSet 保证元素的唯一性主要依赖与 hashCode() 和 equals() 方法。
当元素的哈希值不同时,元素都有自己的独立位置,不需要再判断元素的 equals() 方法。
当元素的哈希值相同时,这时元素再哈希表中的位置相同,就需要判断一次元素的内容是否相同;调整元素的 equals() 方法进行一致比较;当这俩个元素不相同时,会存储在一个哈希值上;故、需覆盖 hashCode() 和 equals() 方法。而且需要依据对象的特有条件建立 hashCode() 和 equals() 的具体实现。
判断包含、删除都是依据 hashCode() 。当 hashCode() 相同时,再判断 equals() 方法
!~ TreeSet:
对 Set 集合中的元素进行排序。数据结构为二叉树结构;可以提高排序性能。
自定义元素本身不具备比较性,TreeSet 集合是无法对元素进行排序的。所以在自定义对象时,需要对象具备一个比较功能。而 Java 已经提供了接口,可以让自定义的对象具备比较性。需要实现 Comparable 接口。
根据比较方法 compareTo() 的返回值来确定元素的唯一性;返回0,则相同。自定义对象需要覆盖 compareTo() 方法。这称为:元素的自然排序。
如果元素本身的比较方式无法满足应用,那么 可以让集合本身具备比较性,就是定义一个类,实现 Comparator 接口,覆盖 compare() 方法。将Comparator 接口的子类对象作为参数传递给 TreeSet 集合的构造函数即可。
当元素自身具备比较性,同时 TreeSet 集合也具备比较器,这时以比较器为主。
泛型:
集合中存储了不同类型的对象,获取时,在运行时容易发生ClassCastException类型转换异常。所以在存储时明确集合要操作的数据类型来避免该异常的发生。
定义集合时,通过<>来明确元素的类型。这中定义的方式称为:泛型
优点:
1、运行时出现的ClassCastException 异常转移到编译时期。
2、泛型的出现避免了强制转换的麻烦。
泛型其实时 jdk1.5版本后出现的安全机制。
ArrayList<String> list=new ArrayList<String>(); list.add("元素"); list.add("元素1"); for(Iterator<String> iter=list.iterator();iter.hasNext()){ iter.next(); }
泛型的使用其实就是给<>传递实际参数,而这个参数就是一个具体的引用数据类型。
当类要操作的引用数据类型不确定时,可以使用泛型来定义,也就是定义一个类型参数,将具体的类型作为实际参数传递给<>
当泛型定义在类上,该泛型作用整个类。建立对象时,就明确了具体类型。那么凡是使用了类上定义的泛型的方法,操作的类也就固定了。
当类中定义 static 方法时,静态方法不可以直接访问类上的泛型;因为类上的泛型只有通过建立对象才可以明确泛型。那么静态方法若操作的引用数据类型不确定,只能将泛型定义在方法上。在静态方法上定义泛型必须定义在 static 关键字之后。
当发放中操作的应用数据类型不确定,而且和对应的对象执行的类型也不一定一致。这时就将泛型定义在方法上。
//泛型接口 interface Inter<T>{ public void method(T t); }
泛型通配符:?
? 代表任意类型。
public static void method(Collection<?> coll){ for(Iterator<?> iter=coll.iterator(); iter.hasNext()){ iter.next(); } }
定义T只能固定一种类型,定义 ? 可以是任意类型。
泛型限定:
? extends E 接收 E 类型 或 E 的子类型
? super E 接收 E 类型 或 E 的父类型
Map集合:
特点:
1、双列集合,Collection 是单列集合。
2、Map 一次存储一对元素,同是键值对的形式,键和值有对应关系。Collection 是一次存储一个元素。
//常见功能: //增 put(k,v);//返回的是v;将键和值存储 Map 集合,当存入相同的 k 时,新值 v 会覆盖原来的值,并返回原来的值。 putAll(map); //删 clear(); remove(k);//根据键删除对应的值,返回被删除的值。 //判 containsKey(Object key); containsValue(Object value); isEmpty(); //查 size();//获取Map 元素个数 get(k);//根据键获取值,还可以判断某个键是否存在的依据。 Collection values();//获取Map 集合中所有的值。 Set keySet();//获取Map 集合中所有的键 Set entrySet();//获取键值对的映射关系。将映射关系封装成对象存入Set 集合
Map 集合中没有迭代器,迭代器是在 Collection 集合中具备的。
Map 集合的取出元素的原理,就是将 Map 集合转成 Set 集合,再进行迭代。
HashTable:
底层是哈希表数据结构;实现同步。不允许 null 作为键、null 作为值。
Peoperties:用于配置文件的定义和操作,使用频率高,同时键和值都是字符串。是集合中可以和IO技术相结合的对象。详细见IO篇
HashMap:
底层是哈希表数据结构;实现不同步。允许 null 作为键、null 作为值。替代 HashTable
HashMap<Integer,String> hm=new HashMap<Integer,String>(); //若要保证HashMap有序。用LinkedHashMap HashMap<Integer,String> hm=new LinkedHashMap<Integer,String>(); hm.put(1,"元素1"); hm.put(2,"元素2"); hm.put(3,"元素3"); hm.put(4,"元素4"); hm.size();//元素个数 hm.get(1);//获取键1对应的vaue hm.remove(1);//删除键1对应的键值对 hm.clear();//清空 hm.containsKey(1);//是否包含键1 //取Map 集合中所有的键值 Set<Integer> hmKey=hm.keySet();//获取所有的键 for(Iterator<Integer> iter=hmKey.iterator(); iter.hasNext()){ Integer it=iter.next(); hm.get(it); } //取Map 集合中所有的键值。 Set<Map.Entry<Integer,String>> hmEntry=hm.entrySet();//获取Map 集合中所有的简直对应关系 for(Iterator<Map.Entry<Integer,String>> iter=hmEntry.iterator(); iter.hasNext()){ Map.Entry<Integer,String> me=iter.next(); Integer key=me.getKey(); String value=me.getValue(); }
TreeMap:
对Map 集合中的键进行排序。
/* 自定义集合的两种排序方式 */ //一、实现Comparable 接口。重写compareTo() 方法。 TreeMap<Student,String> tm=new TreeMap<Student,String>(); tm.put(new Student("姓名","年龄"),"Str元素"); tm.put(new Student("姓名","年龄"),"Str元素"); tm.put(new Student("姓名","年龄"),"Str元素"); Set<Student> tmkey=tm.keySet(); for(Iterator<Student> iter=tmkey.iterator(); iter.hasNext()){ Student stu=iter.next(); tm.get(stu); } Class Student implements Comparable<Student>{ /* Code... */ public int compareTo(Student stu){ int temp=this.age-stu.age; return temp==0?this.name.compareTo(stu.name):temp; } } //二、实现Comparator 接口,重写compare() 方法。 TreeMap<Student,String> tm=new TreeMap<Student,String>(new ComparaByName()); tm.put(new Student("姓名","年龄"),"Str元素"); tm.put(new Student("姓名","年龄"),"Str元素"); tm.put(new Student("姓名","年龄"),"Str元素"); Set<Student> tmkey=tm.keySet(); for(Iterator<Student> iter=tmkey.iterator(); iter.hasNext()){ Student stu=iter.next(); tm.get(stu); } Class ComparaByName implements Comparator<Student>{ public int compare(Student stu1,Student stu2){ int temp=stu1.getName().compareTo(stu2.getName()); return temp==0?new Interger(stu1.getAge()).compareTo(new Integer(stu2.getAge)):temp; } }
集合框架工具类:
Collections、Arrays 这两个工具类中的方法都是静态的,不需要创建对象,直接使用类名调用即可。
Collections:是集合对象的工具类,提供操作集合的工具方法。
Collections.sort(List<T>);//排序,升序 Collections.sort(List<T>,Collections.reverseOrder);//排序,降序 Collections.sort(List<T>,Collections.reverseOrder(Comparator<? super T>));//指定比较器排序,降序 Collections.max();//最大值 Collections.min();//最小值 Collections.binarySearch();//二分查找(折半查找),只作用于List集合 Collections.fill(List<T>,"");//将集合中的元素替换成指定元素 Collections.reverse(List<T>);//将集合元素反转,头尾对调 Collections.swap(List<T>,int,int);//对集合中元素进行位置替换 Collections.shuffle(List<T>);//对集合中的元素进行随机位置置换 Collections.synchronizadList(List<T>);//可以将一个不同步的集合转成同步的List集合。
Arrays:是数组的工具类。提供操作数组的工具方法。
Arrays.asList(Array); 数组转成集合
当数组中的元素是引用类型的数据时,变集合后数组中的元素作为集合中的元素存在。
当数组中的元素是基本数据类型时,变集合后该数组变成集合中的元素。
在 Collection 接口中有个集合变数组的方法:toArray();
ArrayList<String> al=new ArrayList<String>();
al.toArray(new String[al.size()]);
jdk1.5版本后,Collection 有个父接口 Iterable 。该接口的出现封装了迭代器 iterator() 方法,并提供了一个增强 for 循环。增强 for 循环只能遍历数组和 Collection 集合。简化迭代器。一般只用于遍历,而不对元素进行操作。
格式:
for(元素类型 变量:数组 或 Collection 集合){
循环体Code;
}
ArrayList<String> al=new ArrayList<String>(); al.add("元素1"); al.add("元素2"); al.add("元素3"); for(String str:al){ System.out.println(s); }
可变参数:
在指定数据类型的后面加三个点,其实就是数组类型的参数。
格式:
void method(int... arr)
若函数上有多个参数,可变参数的位置必须是最后一个参数。否则编译失败。
静态导入:
为简化集合框架工具类 Collections 的书写,导入一个类名中所有的静态成员 import static java.util.Collections.* 然后直接使用sort()、binarySearch() 等方法来简化Collections.sort()等写法。
但是当方法名称重复时,必须要指定所属的工具类名 Arrays.toString(arr);