集合框架总结
Collection
List和Set
在实际开发中,需要将使用的对象存储于特定数据结构的容器中。JDK提供了这样的容器——集合(Collection)。
Collection是一个接口,定义了集合相关的操作方法,其中两个子接口:List和Set
List:可重复集合
Set:不可重复集合
注:元素是否重复,取决于equals()比较结果。
集合持有对象的引用
集合中存储的都是引用类型元素,并且集合只保存每个元素对象的引用,而并非将元素对象本身存入集合。
add方法
1)Collection定义了一个add方法用于向集合中添加新元素。——boolean add(E e)
2)该方法会将给定的元素添加进集合,若添加成功则返回true,否则返回false。
Contains方法
1)boolean contains(Object o)
2)该方法会用于判断给定的元素是否包含在集合中,若包含则返回true,否则返回false
注:集合在判断元素是否被包含在集合中是根据每个元素的equals()方法进行比较后的结果。
通常有必要重写equals()保证contains()方法的合理结果
size,clear,isEmpty
1)int size():该方法用于返回当前集合中的元素总和。
2)void clear():该方法用于清空当前集合。
3)boolean isEmpty():该方法判断当前集合中是否包不包含任何元素。
addAll和containsAll方法 (集合的批量操作)
1)boolean addAll(Collection<? extends E> c)
该方法需要我们传入一个集合,并将该集合中的所有元素添加到当前集合中。
如果此collection由于调用而发生改变则返回true。
2)boolean containsAll(Collection<? extends E> c)
该方法用于判断当前集合是否包含给定集合中的所有元素,若包含则返回true。
(1)判断当前集合是否包含给定集合的所有元素
(2)判断依据就是根据元素equals比较的
remove():删除集合元素
c.remove(p);
1)remove()方法会删除集合中与给定元素第一个
2)equals比较为true的元素。
Iterator[ hasNext,next方法 ]
迭代器用于遍历集合元素,获取迭代器可以使用collection定义的方法。
迭代器Iterator是一个接口。集合在重写collection的iterator()方法时利用内部类提供了迭代器的实现
Iterator提供了统一的遍历集合元素的方式,其提供了用于遍历集合的两个 方法:
——boolean hasNext():判断集合是否还有元素可以遍历。
——E next():返回迭代的下一个元素
remove方法
在使用迭代器遍历集合时,不能通过集合的remove方法删除集合元素,否则会抛出并发更改异常。我们可以
通过迭代器自身提供的remove()方法来删除通过next方法迭代出的元素。
——void remove()
迭代器的删除方法是在原集合中删除元素。
这一需要注意的是,在调用remove方法前必须通过迭代器的next()方法迭代过元素,那么删除的就是这个元素
。并且不能再次调用remove方法,除非再次调用next()方法方可再次调用。
pubic void testRemove(){ Collection<String> c=new HashSet<String>(); c.add("java"); c.add("C#"); c.add("php"); Iterator<String> it=c.iterator(); while(it.hasNext()){ String str=it.next(); if(str.indexOf('c')!=-1){ it.remove(); } } System.out.println(c); }
增强型for循环
java5.0后推出了一个新的特性,曾倩for循环,也称为新循环。该循环不通用于传统循环的工作,其只用于遍历集合和数组。
for(元素类型 e:集合或数组){
循环体
}
注意:新循环并非新的语法,而是在编译过程中,编译器会将新循环转换为迭代器模式。所以新循环本质上是一个迭代器
泛型机制
泛型在集合中的应用
1)泛型是JavaSE 5.0引入的特性,泛型的本质是参数化类型。在类,接口和方法的定义过程中,所操作的数据类型被传入的参数指定。
2)Java泛型机制广泛的应用在集合框架中。所有的集合类型都带有泛型参数,这样在创建集合时可以指定
放入集合中元素的类型。Java编译器可以据此进行类型检查。这样可以减少代码在运行时出现错误的可能性。
集合操作——线性表
List
ArrayList和LinkList
1)List接口是Collection的子接口,用于定义线性表数据结构。可以将List理解为存放对象的数组,只不过其
元素个数可以动态的增加或减少。
2)List接口的两个常见实现类为ArrayList和LinkedList,分别用动态数组和链表的方式实现了List接口。
3)可以认为ArrayList和LinkedList的方法在逻辑上完全一样。只是在性能上有一定的差别。ArrayList更适合于随机访问而LinkedList更适合于插入和删除。在性能要求不是特别
严格的情形下可以忽略这个差别。
get和set方法
1)List除了继承了Collection定义的方法外,还根据其线性表的数据结构定义了一系列方法,其中最常用的就是基于下标的get和set方法:
——E get(int index):获取集合中指定下标对应的元素,下标从0开始。
——E set(int index,E element):将给定元素存入给定位置,并将原位置的元素返回。
插入和删除
List根据下标的操作还支持插入与删除操作。
——void add(int index,E element):将给定的元素插入到指定位置,原位置及后续元素都顺序向后移动。
——E remove(int index):删除给定位置的元素,并将被删除的元素返回。
subList
list的subList方法用于获取子List
注意:subList获取的list与原List占有相同的存储空间,对子list操作会影响原List。
List<E> subList(int e1,int e2):e1和e2是截取子List的首尾下标(前包括后不包括)
List转换为数组
List的toArray方法用于将集合转换为数组。但实际上该方法是在Collection中定义的,所以所有的集合都有这个功能。
其有两个方法:
Object[ ] toArray()
<T> T[ ] toArray(T[ ] a)
其中第二个方法是比较常用的,我们可以传入一个指定类型的数组,该数组的元素类型应与集合的元素类型一致。返回值则是转换后的数组,该数组会保存集合中所有的元素。
数组转换为List
Arrays类中提供了一个静态方法asList,使用该方法我们可以将一个数组转换为对应的List集合。
其方法定义为: static <T>List<T> asList<T...a>
返回的List的集合元素类型由传入的数组的元素类型决定,并且要注意的是,返回的集合我们不能对其增删操作,否则会抛出异常。并且对集合的元素进行修改会影响数组对应的元素。
List排序
Collections.sort方法实现排序
1)Collections是集合的工具类,它提供了很多便于我们操作集合的方法,其中就有用于集合排序sort方法。
该方法的定义为
——void sort(List<T> list)
该方法的作用是对给定的集合元素进行自然排序。
public void testsort(){ List<Integer>list=new ArrayList<Integer>(); Random r=new Random(1); for(int i=0;i<10;i++){ list.add(r.nextInt(100)); } System.out.println(list); Collections.sort(list); System.out.println(list); }
Comparable
Collections的sort方法时对集合元素进行自然排序,那么两个元素对象之间就一定要有大小之分。这个大小之分是如何界定的?实际上,在使用Collections的sort方法排序的集合元素都必须是Comparable接口的实现类,该接口表示其子类是可比较的,因为事先该接口必修重写抽象方法:
——int compareTo(T t);
该方法用于使当前对象与给定对象进行比较。
——若当前对象大于给定对象,那么返回值应为>0的整数。
——若小于给定对象,那么返回值应为<0的整数。
——若两个对象相等,则返回0.
public void testComparable(){ /* cell实现了Comparable接口 CompareTo方法逻辑为按照row值的大小排序 public int compareTo(cell o){ return this.row-o.row; } */ List<cell> cells=new ArrayList<cell>(); cells.add(new cell(2,3)); cells.add(new cell(5,3)); cells.add(new cell(4,2)); cells.add(new cell(1,3)); Collections.sort(cells); }
Comparator
一旦Java实现了Comparable接口,其比较逻辑就已经确定;如果希望在排序的操作中临时指定比较规则,可以采用Comparator接口回调的方式。
——int compare(T o1,T o2);
该方法的返回值要求:
——若o1>o2则返回值应>0;
——若返回值o1<o2则返回值应为<0
——若o1==o2则返回值应为0
public void testComparator(){ List<cell> cells=new ArrayList<cell>(); cells.add(new cell(2,3)); cells.add(new cell(5,3)); cells.add(new cell(4,2)); cells.add(new cell(1,3)); Collections.sort(cells); //按照col值的大小排序 Collections.sort(cells,new Comparator<Cell>(){ public int compare(Cell o1,Cell o2){ return o1.col-o2.col; } }); }
队列和栈
Queue
1)队列是常用的数据结构,可以将队列看成特殊的线性表,队列限制了线性表的访问方式:只能从线性表的一端添加(offer)元素,从另一端取出(poll)元素。
2)队列遵循先进先出的原则。
3)JDK提供了Queue接口,同时使得LinkedList实现了该接口(选择LinkedList实现Queue的原因在于Queue经常要进行添加和删除的操作,二LinkedList在这方面效率较高)
Queue接口中主要方法如下:
boolean offer(E e):将一个对象添加至队尾,如果添加成功则返回true。
E poll():从队首删除并返回一个元素。
E peek();返回队首元素(但并不删除)。
public void testQueue(){ Queue<String> queue=new LinkedList<String>(); queue.offer("a"); queue.offer("b"); queue.offer("c"); System.out.println(queue); //[a,b,c] String str=queue.peek(); System.out.println(str); //a while(queue.size()>0){ str=queue.poll(); System.out.println(str);//a,b,c } }
Deque
1)Deque是Queue的子接口,定义了所谓“双端队列”即从队列的两端分别可以入队(offer)和出队(poll),LinkedList实现了该接口。
如果将Deque限制为只能从高一段入队和出队,则可实现“栈”(stack)的数据结构,对于栈而言,入栈称为push,出栈称之为pop.
2)栈遵循先进后出原则。
public void testStatck(){ Deque<String> stack=new LinkedList<String>(); stack.push("a"); stack.push("b"); stack.push("c"); System.out.println(stack);//[c,b,a] String str=stack.peek(); System.out.println(str);//c while(stack.size()>0){ str=stack.pop(); System.out.print(str+" ");//c,b,a } }
Map接口
1)Map接口定义的集合又称查找表,用于存储所谓“key-value”映射对。key可以看成是value的索引,作为key的对象在集合中不可以重复。
2)根据内部数据结构的不同,Map接口有多种实现类,其中常用的有内部为hash表实现的HashMap和内部为二叉树实现的TreeMap。
put()方法
1)Map接口中定义了向Map中存放元素的put方法:——V put(K key,V value)
2)将key-value对存入Map,如果在集合中已经包含该key,则操作将替换该key所对应的value,返回值为该key所对应的value(如果没有则返回null)
get()方法
1)Map接口中定义了从Map中获取元素的get方法:
——V get(Object key)
2)返回参数key所对应的value对象,如果不存在则返回null。
containsKey()方法
1)Map接口中定义了判断某个key是否在Map中存在:
——boolean containsKey(Object key);
若map中包含给定的key则返回true,否则返回false。
HashMap
1)从HashMap的原理中我们可以看到,key的hashCode()方法 的返回值对HashMap存储元素是会起着很重要的作用。
而hashCode()方法实际上是Object中定义的那么应当妥善重写该方法:
对于重写了equals方法的对象,一般要妥善的重写继承自Object类的hashCode方法(Object提供的hashCode方法将返回该对象所在的内存地址的整数形式)。
2)重写hashCode方法时需要注意两点:其一,与equals方法的一致性,即equals比较返回true的两个对象其hashCode方法返回值应当相同;其二,hashCode方法的数值应符合hash算法的要求,试想如果有很对对hashCode方法返回值都相同,则会大大降低hash表的效率,一般情况下可以使用IDE提供的自动生成的hashCode方法。
装载因子及HashMap优化
1)Capacity:容量,hash表里bucket(桶)的数量,也就是三列数组的大小
2)Initial capacity:初始容量,创建hash表时,初始bucket的数量,默认构建容量是16,也可以使用特定容量。
3)Size:大小,当前散列表中存储数据的数量。
4)Load factor:加载因子,默认值为0.75,当向散列表增加数据时如果size/capacity的值大于Loadfactor则发生扩容并且重新散列(rehash)。
5)性能优化:加载因子较小时,散列查询性能会提高,同时 也浪费了散列桶空间容量。0.75是性能和空间相对平衡结果,在创建散列表时指定合理容量,减少rehash提高性能
Map的遍历
Map提供了三种遍历方式:
——遍历所有的key
——遍历所有的key-value对
——遍历所有的value(不常用)
遍历所有 key的方法
——Set<k> keySet()
——该方法会将当前Map中所有的key存入一个Set集合后返回。
public void testKeySet(){ Set<String> keySet=map.keySet(); for(String key:keySet){ System.out.println(key); } }
使用entrySet()方法遍历所有键值对
——Set<Entry<K,V>> entrySet()
该方法会将当前Map中每一组key-value对封装为一个Entry对象并存入一个Set集合后返回。
LinkedHashMap实现有序Map
1)使用Map接口的哈希表和链表实现,具有可预知的迭代顺序。次实现与HashMap的不同之处在于:
LinkedHashMap维护着一个双向循环链表。次链表定义了迭代顺序,该迭代顺序通常就是存放元素的顺序。
2)如果在Map中重新存入已有的key。那么key的位置不会发生改变,只是将value值替换。
浙公网安备 33010602011771号