上一期,我们讲到集合的知识的初步了解,这次我们就来讲解稍微完整版的
目录
ArrayList和Vector的区别(是否有序、是否重复、数据结构、底层实现)
集合框架图
定义以及接口的讲解
1、定义:Java集合类存放于java.util包,是存放对象的容器,长度可变,只能存放对象,可以存放不同的数据类型;
2、常用集合接口:
a、Collection接口:最基本的集合接口,存储不唯一,无序的对象,List接口和Set接口的父接口;
b、List接口:一个有序、可以重复的集合,常用实现类ArrayList和LinkedList;
1 // 底层数据结构是数组,查询快,增删慢,线程不安全,效率高
2 List arrayList = new ArrayList();
3 // 底层数据结构是数组,查询快,增删慢,线程安全,效率低,耗性能
4 List vector = new Vector();
5 // 底层数据结构是链表,查询慢,增删快,线程不安全,效率高
6 List linkedList = new LinkedList();
c、Set接口:一个无序、不可重复的集合,常用实现类HashSet、LinkedHashSet、TreeSet;
1 // 元素无序,不可重复,线程不安全,集合元素可以为 NULL
2 Set hashSet = new HashSet();
3 // 底层采用链表和哈希表的算法,保证元素有序,唯一性(即不可以重复,有序),线程不安全
4 Set linkedHashSet = new LinkedHashSet();
5 // 底层使用红黑树算法,擅长于范围查询,元素有序,不可重复,线程不安全
6 Set treeSet = new TreeSet();
d、Map接口:key-value的键值对,key不允许重复,value可以,key-value通过映射关系关联,常用实现类HashMap和TreeMap;
1 // 采用哈希表算法,key无序且不允许重复,key判断重复的标准是:key1和key2是否equals为true,并且hashCode相等
2 Map<String, String> hashMap = new HashMap<String, String>();
3 // 采用红黑树算法,key有序且不允许重复,key判断重复的标准是:compareTo或compare返回值是否为0
4 Map<String, String> treeMap = new TreeMap<String, String>();
3、Set和List的区别:
a、Set实例存储是无序的,不重复的数据;List实例存储的是有序的,可以重复的元素;
b、Set检索效率低下,删除和插入效率高,删除和插入不会引起元素位置改变;
c、List可以根据存储的数据长度自动增长List长度,查找元素效率高,插入删除效率低,插入和删除时会引起其他元素位置改变;
4、Map和Set的关系:
a、HashMap、HashSet 都采哈希表算法,TreeMap、TreeSet 都采用红黑树算法、LinkedHashMap、LinkedHashSet 都采用哈希表算法和红黑树算法;
b、分析Set的底层源码,Set 集合就是由Map集合的Key组成;
详细讲解:
List集合我们已经在上一篇文章里面讲到了, 所以这次我们就讲深入的知识,
List集合子类特点:
上面也提到了,这个是做的导图里面的内容;
在这里我做了一个小小的测试,将上面提到的6个特有方法都写了一遍
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList<String > LinkedList =new LinkedList<String >();
LinkedList.add("hello");
LinkedList.add("world");
LinkedList.add("java");
/* // public void addFirst(E e):在该列表开头插入指定的元素
//public void addLast(E e):将指定元素追加到列表的末尾
LinkedList.addFirst("javaee");
LinkedList.addLast("hhh");q87yh
// public E getFirst() :返回此列表中的第一个元素
// public E getLast(): 返回此列表中的最后一个元素
System.out.println(LinkedList.getFirst());
System.out.println(LinkedList.getLast());
//public E removeFirst(); 删除并返回第一个元素
//public E removeLast():删除并返回最后一个元素
System.out.println(LinkedList.removeFirst());
System.out.println(LinkedList.removeLast());*/
System.out.println(LinkedList);
}
}
大佬们也可以自己上机测试测试,方便理解其内容。
Set集合概述和特点:
set集合特点:1.不包含重复元素的集合
2.没有带索引的方法,所以不能使用普通for循环遍历
让我们用代码来观察其特点, 不包含重复元素
public class SetDemo {
public static void main(String[] args) {
//创建集合对象
Set<String> set=new HashSet<String>();
//添加元素
set.add("hello");
set.add("world");
set.add("java");
//不包含重复元素
set.add("world");
//遍历
for (String s:set){
System.out.println(s);
}
}
}
控制台输出截图:
讲完了Set集合,让我们来看看HashSet
HashSet集合概述和特点:
hashset集合特点:
①:底层的数据结构是哈希;
②:它对集合的迭代顺序不作任何保证;特别是它不能保证订单在一段时间内保持不变,这个类允许 null元素;
③:没有带索引的方法,所以不能使用普通for循环遍历
④:由于是set集合,所以是不包含重复元素的集合
hashset集合添加元素的过程:
如下图所示:
可能这个图的字有点小,大家委屈一下自己。
代码上机测试:
public class HashSetDemo {
public static void main(String[] args) {
//创建集合对象
HashSet<String> hs=new HashSet<String>();
//添加元素
hs.add("hello");
hs.add("world");
hs.add("java");
hs.add("world"); //就算你输入了两个, 但是 他是不能重复的
for (String s:hs){
System.out.println(s);
}
}
}
从上图,我们看到的,hs.add 里面写了俩个 world, 可是set集合的特点是不重复元素,并且得需要使用增强for来实现遍历,输出结果如下图所示:
中间穿插一点小片段,上面讲到了HashSet 所以我们来讲讲 哈希值。
哈希值:
1.定义:哈希值是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值;
2. public int hashCode():返回对象的哈希值!!! 需要了解;
3.哈希值的特点:
①:同一个对象多次调用hashCode()方法返回的哈希值是相同的;
②:默认情况下,不同的对象的哈希值是不同的。而重新hashCode()方法,可以实现让不同对象的哈希值相同
下面我们将上机实际通过代码测试:
public class HashDemo {
public static void main(String[] args) {
//创建学生对象
Student s1=new Student("林青霞",30);
System.out.println(s1.hashCode());//22307196
System.out.println(s1.hashCode());//22307196
//同一个对象多次调用hashcode()方法返回的哈希值是相同的
System.out.println("--------------");
//默认情况下,不同的对象的哈希值是不相同的
Student s2=new Student("林青霞",30);
System.out.println(s2.hashCode());//10568834
System.out.println(s2.hashCode());//10568834
System.out.println("-------------");
//通过方法重写,可以实现不同对象的哈希值是相同的;
System.out.println("hello".hashCode());//99162322
System.out.println("world".hashCode());//113318802
System.out.println("java".hashCode());//3254818
System.out.println("world".hashCode());//113318802
System.out.println("重地".hashCode());//1179395
System.out.println("通话".hashCode());//1179395
}
}
控制台打印输出:
哈希值这个东西,大家可以多了解了解,蛮有趣的一个东西。
TreeSet集合概述和特点:
TreeSet集合特点:
①:元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体的排序方式取决于构造方法;
两个方法:1.TreeSet():根据其元素的自然排序
2.TreeSet( Comparator comparator): 根据指定的比较器进行排序
②:没有带索引的方法,所以不能使用普通for循环遍历,但是可以用增强for进行遍历;
③:由于是set集合,所以不包含重复元素的集合;
Comparator的使用:
1.自然排序Comparable的使用:
①:TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
②:自然排序,就是让元素所属的类实现Comparable接口,重新compareTo(T o)方法
③:重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
2.比较器排序Comparator的使用:
①:用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
②:比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(To1, To2) 方法
代码上机测试:
public class TreeSetDemo {
public static void main(String[] args) {
//通过创建TreeSet集合对象, 通过比较器排序进行排序
TreeSet<Student> ts=new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
//主要条件!!!
int num=(s2.getSum()-s1.getSum());
//次要条件
int num2=num==0?s1.getChinese()-s2.getChinese():num;
return num2;
}
});
//创建学生对象
Student s1=new Student("林青霞",25,95,82);
Student s2=new Student("张曼玉",28,88,92);
Student s3=new Student("令狐冲",26,97,91);
Student s4=new Student("风清扬",29,95,96);
Student s5=new Student("东方不败",24,85,92);
Student s6=new Student("煮了吃",31,94,97);
Student s7=new Student("煮了吃",31,94,97);
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
for (Student s: ts){
System.out.println(s.getName()+","+s.getAge()+","+s.getChinese()+","+s.getMath()+","+s.getSum());
}
}
}
代码测试结果如下:
泛型:
泛型概述:它的本质是参数化类型, 顾名思义,就是将类型由原来的具体的类型参数化, 然后在使用/调用时传入具体的类型;
泛型定义格式:
1.<类型>:指定一种类型的格式,这里的类型可以看成是形参;
2.<类型1, 类型2>:指定多种类型的格式, 多种类型直接用逗号隔开。 这里的类型可以看成是形参;
3.将来具体调用时候给定的类型可以看成是形参,并且实参的类型只能是引用数据类型
泛型的好处:1.把运行时期的问题提前到了编译期间,2.避免了强制类型转换;
类型通配符:
不作具体的说,放上截图
好捏看完了上面,接下来需要看一点小重点知识MAP!!!
Map集合概述和使用:
Map集合概述:Interface Map<K, V> K:键的类型; V: 值的类型; 将键映射到值的对象;不能包含重复的键;每个键可以映射到最多一个值
创建Map集合的对象(两种方法):①多态的方式;②具体的实现类HashMap;
Map集合的基本功能:
Map集合的获取功能:
代码上机测试:
public class MapDemo03 {
public static void main(String[] args) {
Map<String ,String> map=new HashMap<String, String>();
//在map集合中, 前面的叫做键,后面的叫做值
map.put("张无忌","赵敏");
map.put("郭靖","黄蓉");
map.put("杨过","小龙女");
/* // v get(Object key):根据键获取值
System.out.println(map.get("张无忌"));
//如果键里面不存在那个值,则返回null
System.out.println(map.get("张三丰"));*/
/* //Set<K> keySet():获取所有键的集合
Set<String> keySet=map.keySet();
for (String key: keySet) {
System.out.println(key);
}*/
//Collection<V> values(): 获取所有值的集合
Collection<String > values=map.values();
for (String key:values){
System.out.println(key);
}
}
}
这里我们只展示,最后输出值的结果:
通过Set<Map.Entry<String ,String>> entrySet=map.entrySet();来获取所有的键值的对象!!!
public class MapDemo05 {
public static void main(String[] args) {
Map<String ,String> map=new HashMap<String, String>();
//添加元素
map.put("张无忌","赵敏");
map.put("郭靖","黄蓉");
map.put("杨过","小龙女");
//获取所有键值对对象的集合
Set<Map.Entry<String ,String>> entrySet=map.entrySet();
//遍历键值对对象的集合,得到每一个键值对对象
for (Map.Entry<String ,String> me:entrySet){
String key=me.getKey();
String value=me.getValue();
System.out.println(key+","+value);
}
}
}
补充内容:
集合框架底层数据结构总结:
Collection
1. List
- Arraylist: Object数组
- Vector: Object数组
- LinkedList: 双向链表(JDK1.6之前为循环链表,JDK1.7取消了循环)
2. Set
- HashSet(无序,唯一): 基于 HashMap 实现的,底层采用 HashMap 来保存元素
- LinkedHashSet: LinkedHashSet 继承与 HashSet,并且其内部是通过 LinkedHashMap 来实现的。有点类似于我们之前说的LinkedHashMap 其内部是基于 Hashmap 实现一样,不过还是有一点点区别的。
- TreeSet(有序,唯一): 红黑树(自平衡的排序二叉树。)
Map
- HashMap: JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间
- LinkedHashMap: LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。详细可以查看:《LinkedHashMap 源码详细分析(JDK1.8)》
- Hashtable: 数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的
- TreeMap: 红黑树(自平衡的排序二叉树)
如何选用集合?
主要根据集合的特点来选用,比如我们需要根据键值获取到元素值时就选用Map接口下的集合,需要排序时选择TreeMap,不需要排序时就选择HashMap,需要保证线程安全就选用ConcurrentHashMap.当我们只需要存放元素值时,就选择实现Collection接口的集合,需要保证元素唯一时选择实现Set接口的集合比如TreeSet或HashSet,不需要就选择实现List接口的比如ArrayList或LinkedList,然后再根据实现这些接口的集合的特点来选用。
特别注意事项:
List 和 Map 区别?(数据结构,存储特点)
这个要从两个方面来回答,一方面是List和Map的数据结构,另一方面是存储数据的特点。在数据结构方面,List存储的是单列数据的集合,而Map存储的是key、value类型的数据集合。在数据存储方面,List存储的数据是有序且可以重复的,而Map中存储的数据是无序且key值不能重复(value值可以重复)。
Arraylist与 LinkedList 异同:
- Arraylist 底层使用的是Object数组;LinkedList 底层使用的是双向循环链表数据结构;
- ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。插入末尾还好,如果是中间,则(add(int index, E element))接近O(n);LinkedList 采用链表存储,所以插入,删除元素时间复杂度不受元素位置的影响,都是近似 O(1)而数组为近似 O(n)。对于随机访问get和set,ArrayList优于LinkedList,因为LinkedList要移动指针。
- LinkedList 不支持高效的随机元素访问,而ArrayList 实现了RandmoAccess 接口,所以有随机访问功能。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。所以ArrayList随机访问快,插入慢;LinkedList随机访问慢,插入快。
- ArrayList的空 间浪费主要体现在在list列表的结尾会预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)。
HashMap 和 Hashtable 的区别:
相同点: 都是实现来Map接口(hashTable还实现了Dictionary 抽象类)。
不同点:
- 历史原因:Hashtable 是基于陈旧的 Dictionary 类的,HashMap 是 Java 1.2 引进的 Map 接口 的一个实现,HashMap把Hashtable 的contains方法去掉了,改成containsvalue 和containsKey。因为contains方法容易让人引起误解。
- 同步性:Hashtable 的方法是 Synchronize 的,线程安全;而 HashMap 是线程不安全的,不是同步的。所以只有一个线程的时候使用hashMap效率要高。
- 值:HashMap对象的key、value值均可为null。HahTable对象的key、value值均不可为null。
- 容量:HashMap的初始容量为16,Hashtable初始容量为11,两者的填充因子默认都是0.75。
- HashMap扩容时是当前容量翻倍即:capacity * 2,Hashtable扩容时是容量翻倍+1 即:capacity * 2+1。
ArrayList 与 Vector 的区别:
共同点: 都实现了List接口,都是有序的集合,我们可以按位置的索引号取出元素,其中数据都是可以重复的,这是与hashSet最不同的,hashSet不可以按照索引号去检索其中的元素,也不允许有重复的元素。
区别:
- 同步性:Vector是线程安全的,即线程同步,ArrayList是不安全的,如果有多个线程访问到集合,最好使用Vector,因为不需要我们自己再去考虑和编写线程安全的代码;如果只有一个线程会访问到集合,那最好是使用ArrayList,因为它不考虑线程安全,效率会高些。
- 数据增长:ArrayList 与 Vector 都有一个初始的容量大小,当存储进它们里面的元素的个数超过了容量时,就需要增加 ArrayList 与 Vector 的存储空间,每次要增加存储空间时,不是只增加一个存储单元,而是增加多个存储单元,每次增加的存储单元的个数在内存空间利用与程序效率之间要取得一定的平衡。Vector 默认增长为原来两倍,而 ArrayList 的增长策略在文档中没有明确规定(从源代码看到的是增长为原来的 1.5 倍)。ArrayList 与 Vector 都可以设置初始的空间大小,Vector 还可以设置增长的空间大小,而 ArrayList 没有提供设置增长空间的方法。
HaspMap与TreeMap的区别:
- HashMap通过hashcode对其内容进行快速查找,而TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用TreeMap(HashMap中元素的排列顺序是不固定的)。
- 在Map 中插入、删除和定位元素,HashMap是最好的选择。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。使用HashMap要求添加的键类明确定义了hashCode()和 equals()的实现。
HashMap 和 HashSet区别
如果你看过 HashSet
源码的话就应该知道:HashSet 底层就是基于 HashMap 实现的。(HashSet 的源码非常非常少,因为除了 clone()
、writeObject()
、readObject()
是 HashSet 自己不得不实现之外,其他方法都是直接调用 HashMap 中的方法。
HashMap和Hashtable的区别
HashMap和Hashtable都实现了Map接口,并且都是key-value的数据结构。它们的不同点主要在三个方面:
第一,Hashtable是Java1.1的一个类,它基于陈旧的Dictionary类。而HashMap是Java1.2引进的Map接口的一个实现。
第二,Hashtable是线程安全的,也就是说是线程同步的,而HashMap是线程不安全的。也就是说在单线程环境下应该用HashMap,这样效率更高。
第三,HashMap允许将null值作为key或value,但Hashtable不允许(会抛出NullPointerException)。
ArrayList和Vector的区别(是否有序、是否重复、数据结构、底层实现)
ArrayList和Vector都实现了List接口,他们都是有序集合,并且存放的元素是允许重复的。它们的底层都是通过数组来实现的,因此列表这种数据结构检索数据速度快,但增删改速度慢。
而ArrayList和Vector的区别主要在两个方面:
第一,线程安全。Vector是线程安全的,而ArrayList是线程不安全的。因此在如果集合数据只有单线程访问,那么使用ArrayList可以提高效率。而如果有多线程访问你的集合数据,那么就必须要用Vector,因为要保证数据安全。
第二,数据增长。ArrayList和Vector都有一个初始的容量大小,当存储进它们里面的元素超过了容量时,就需要增加它们的存储容量。ArrayList每次增长原来的0.5倍,而Vector增长原来的一倍。ArrayList和Vector都可以设置初始空间的大小,Vector还可以设置增长的空间大小,而ArrayList没有提供设置增长空间的方法。
Arraylist
不是同步的,所以在不需要保证线程安全时时建议使用Arraylist。
感谢各位大佬的收看 谢谢各位大佬!!!
文章评论