今天复习集合,走起!
结构图
Collection接口
查看jdk源代码:
public interface Collection<E> extends Iterable<E>
可以发现Collection是一个接口类,其继承了java迭代接口Iterable。
Collection接口有两个主要的子接口List和Set,注意Map不是Collection的子接口这个要牢记。
Collection中可以存储的元素间无序,可以重复组各 自独立的元素, 即其内的每个位置仅持有一个元素,同时允许有多个null元素对象。
List接口
源代码如下
public interface List<E> extends Collection<E>
List中存储的元素实现排序,而且可以重复的存储相关元素。
List接口两个常用的实现类ArrayList和LinkedList
ArrayList
- ArrayList数组线性表的特点为:类似数组的形式进行存储,因此它的随机访问速度极快。
- ArrayList数组线性表的缺点为:不适合于在线性表中间需要频繁进行插入和删除操作。因为每次插入和删除都需要移动数组中的元素。
注意:
a.如果在初始化ArrayList的时候没有指定初始化长度的话,默认的长度为10.
b.ArrayList在增加新元素的时候如果超过了原始的容量的话,ArrayList扩容ensureCapacity的方案为“原始容量*3/2+1"
c.ArrayList是线程不安全的,在多线程的情况下不要使用。
遍历的三种方式
package org.lq.test;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* 测试类
* @author asu
* @创建日期 2019-7-22
*/
public class Test{
public static void main(String[] args) {
List<String> list=new ArrayList<String>();
list.add("Hello");
list.add("World");
list.add("HAHAHAHA");
//第一种遍历方法使用foreach遍历List
for (String str : list) { //也可以改写for(int i=0;i<list.size();i++)这种形式
System.out.println(str);
}
//第二种遍历,把链表变为数组相关的内容进行遍历
String[] strArray=new String[list.size()];
list.toArray(strArray);
for(int i=0;i<strArray.length;i++) //这里也可以改写为foreach(String str:strArray)这种形式
{
System.out.println(strArray[i]);
}
//第三种遍历 使用迭代器进行相关遍历
Iterator<String> ite=list.iterator();
while(ite.hasNext())
{
System.out.println(ite.next());
}
}
}
Vector
如果一定要在多线程使用List,可以选择Vector
如果一定在多线程使用List的,您可以使用Vector,因为Vector和ArrayList基本一致,区别在于Vector中的绝大部分方法都使用了同步关键字修饰,这样在多线程的情况下不会出现并发错误,还有就是它们的扩容方案不同,ArrayList是通过原始容量*3/2+1,而Vector是允许设置默认的增长长度,Vector的默认扩容方式为原来的2倍。切记Vector是ArrayList的多线程的一个替代品。
LinkedList
a. LinkedList的链式线性表的特点为: 适合于在链表中间需要频繁进行插入和删除操作。
b.LinkedList的链式线性表的缺点为: 随机访问速度较慢。查找一个元素需要从头开始一个一个的找。速度你懂的。
a.LinkedList和ArrayList的区别和联系
ArrayList数组线性表的特点为:类似数组的形式进行存储,因此它的随机访问速度极快。
ArrayList数组线性表的缺点为:不适合于在线性表中间需要频繁进行插入和删除操作。因为每次插入和删除都需要移动数组中的元素。
LinkedList的链式线性表的特点为: 适合于在链表中间需要频繁进行插入和删除操作。
LinkedList的链式线性表的缺点为: 随机访问速度较慢。查找一个元素需要从头开始一个一个的找。速度很慢。
b.LinkedList的内部实现
LinkedList的内部是基于双向循环链表的结构来实现的。在LinkedList中有一个类似于c语言中结构体的Entry内部类。
在Entry的内部类中包含了前一个元素的地址引用和后一个元素的地址引用类似于c语言中指针。
c.LinkedList线程不安全
注意LinkedList和ArrayList一样也不是线程安全的
d.LinkedList可以被当作堆栈使用
由于LinkedList实现了接口Dueue,所以LinkedList可以被当做堆栈来使用
Set接口
源代码
public interface Set<E> extends Collection<E>
Set中的元素实现了不重复,有点象集合的概念,无序,不允许有重复的元素,最多允许有一个null元素对象。
需要注意的是:虽然Set中元素没有顺序,但是元素在set中的位置是有由该元素的HashCode决定的,其具体位置其实是固定的。
此外需要说明一点,在set接口中的不重复是由特殊要求的。
举一个例子:对象A和对象B,本来是不同的两个对象,正常情况下它们是能够放入到Set里面的,但是 如果对象A和B的都重写了hashcode和equals方法,并且重写后的hashcode和equals方法是相同的话。那么A和B是不能同时放入到
Set集合中去的,也就是Set集合中的去重和hashcode与equals方法直接相关。
例如:
package org.lq.test;
import java.util.HashSet;
import java.util.Set;
/**
* 测试类
* @author asu
* @创建日期 2019-7-22
*/
public class Test{
public static void main(String[] args) {
Set<String> set=new HashSet<String>();
set.add("Hello");
set.add("world");
set.add("Hello");
System.out.println("集合的尺寸为:"+set.size());
System.out.println("集合中的元素为:"+set.toString());
}
}
由于String类中重写了hashcode和equals方法,第二个hello是加不进去的。
HashSet
HashSet是Set接口的最常见的实现类了。其底层是基于Hash算法进行存储相关元素的。
源代码
public HashSet() {
map = new HashMap<E,Object>();
}
底层是由HashMap实现的,
使用和理解容易出现的误区:
a.HashSet中存放null值
HashSet中时允许出入null值的,但是在HashSet中仅仅能够存入一个null值哦。
b.HashSet中存储元素的位置是固定的
HashSet中存储的元素的是无序的,这个没什么好说的,但是由于HashSet底层是基于Hash算法实现的,使用了hashcode,所以HashSet中相应的元素的位置是固定的哦。
TreeSet
TreeSet是一种排序二叉树。存入Set集合中的值,会按照值的大小进行相关的排序操作,底层算法是基于红黑树来实现的。
TreeSet和HashSet的主要区别在于TreeSet中的元素会按照相关的值进行排序。
HashSet和TreeSet的区别和联系
1. HashSet是通过HashMap实现的,TreeSet是通过TreeMap实现的,只不过Set用的只是Map的key
2. Map的key和Set都有一个共同的特性就是集合的唯一性.TreeMap更是多了一个排序的功能.
3. hashCode和equal()是HashMap用的, 因为无需排序所以只需要关注定位和唯一性即可.
a. hashCode是用来计算hash值的,hash值是用来确定hash表索引的.
b. hash表中的一个索引处存放的是一张链表, 所以还要通过equal方法循环比较链上的每一个对象才可以真正定位到键值对应的Entry.
c. put时,如果hash表中没定位到,就在链表前加一个Entry,如果定位到了,则更换Entry中的value,并返回旧value
4. 由于TreeMap需要排序,所以需要一个Comparator为键值进行大小比较.当然也是用Comparator定位的.
a. Comparator可以在创建TreeMap时指定
b. 如果创建时没有确定,那么就会使用key.compareTo()方法,这就要求key必须实现Comparable接口.
c. TreeMap是使用Tree数据结构实现的,所以使用compare接口就可以完成定位了.
Map
说到Map接口的话大家也许在熟悉不过了。Map接口实现的是一组Key-Value的键值对的组合。 Map中的每个成员方法由一个关键字(key)和一个值(value)构成。Map接口不直接继承于Collection接口(需要注意啦),因为它包装的是一组成对的“键-值”对象的集合,而且在Map接口的集合中也不能有重复的key出现,因为每个键只能与一个成员元素相对应。在我们的日常的开发项目中,我们无时无刻不在使用者Map接口及其实现类。Map有两种比较常用的实现:HashMap和TreeMap等。HashMap也用到了哈希码的算法,以便快速查找一个键,TreeMap则是对键按序存放,因此它便有一些扩展的方法,比如firstKey(),lastKey()等,你还可以从TreeMap中指定一个范围以取得其子Map。键和值的关联很简单,用pub(Object key,Object value)方法即可将一个键与一个值对象相关联。用get(Object key)可得到与此key对象所对应的值对象。