List接口:
|---->Collection接口:单列集合,用于存储一个一个的对象
|---->List接口,存储有序的可以重复的数据{可用于替代数组} ---->“动态”数组,替换原有数组
|---->ArrayList:作为List接口的主要实现类;线程不安全,执行效率高;底层使用Object[] elementData存储
|---->LinkedList:对于频繁的增加、删除操作,使用此类比ArrayList效率高,底层使用双向链表存储
|---->Vector:作为List接口的古老实现类;线程安全的,执行效率低;底层使用Object[] elementData存储。其扩容方式,当底层容量不足,则直接扩容成原来的2倍,一开始初始化为10,是栈的父类
一、List接口概述:
java.util.list接口 extends Collection接口
其实现类:Vector集合,ArrayList集合,LinkedList集合
List接口特点:
1.有序的接口,存储,取出元素顺序一致(存123,取123)
2.有索引,包含了一些索引方法,可以使用普通的for循环
3.允许存储重复元素
二、ArrayList:
1.ArrayList源码分析:
JDK7中{ArrayList list=new ArrayList()底层创建了长度是10的Object[]数组elementDatalist.add(123);相当于在elementData[0]=new Integer(123);...list.add(11)若此次添加导致底层elementData数组容量不够,则扩容,默认情况下,扩容为原来容量的1.5倍,同时需要将原有数组数据赋值到已经扩容的数组中,即不断造新对象抛弃旧对象建议开发中使用带参构造器:ArrayList list =new ArrayList(int capacity)
}JDK8中ArrayList的变化{ArrayList list=new ArrayList()底层Object[] elementData初始化为{},并没有创建长度为10的数组list.add(123);第一次调用sdd()方法执行添加操作时,第层才创建了长度为10的数组,并加数据123添加到elementData[0]当中,相当于elementData[0]=new Integer(123);...list.add(11)若此次添加导致底层elementData数组容量不够,则扩容,默认情况下,扩容为原来容量的1.5倍,同时需要将原有数组数据赋值到已经扩容的数组中,即不断造新对象抛弃旧对象建议开发中使用带参构造器:ArrayList list =new ArrayList(int capacity)
}小节:JDK7中的ArrayList的对象创建类似于单列模式中的饿汉式,而JDK8中的ArrayList的对象创建类似于单列模式中的懒汉式,延迟了数组的创建,节省内存
2.数组长度不可改变,ArrayList集合长度可以随意改变。对于ArrayList来说有一个<E>表示泛型,直接打印ArrayList得到的不是地址值,而是内容,若内容是空,则得到[]
3.ArrayList底层是数组结构,查询较多可使用ArrayList; 增删较多则尽量少用ArrayList
4.ArrayList集合的直接打印的是集合中的内容而非地址,然而ArrayList集合保存的却是元素的地址。因此这也决定了,向ArrayList集合传递的数据类型只能是泛型(<E>),而泛型只能是引用类型数据,不能是基类型数据,因为基本类型数据无地址值。若非要向ArrayList集合传递基本类型数据,那么可以选择向ArrayList集合传递基本类型的包装类。
三、LinkedList
1.LinkedList的源码分析
{LinkList list = new LinkList();内部声明了Node类型的first和last属性,默认值为null;list.add(123)将123封装到Node当中,创建了Node对象其种Node定义为{//体现了LinkedList的双向链表的说法private static class Node<E> {E item;Node<E> next;Node<E> prev;Node(Node<E> prev, E element, Node<E> next) {this.item = element;this.next = next;this.prev = prev;}}}
2.不是同步就是多线程,多线程运行效率高运行速度快。linklist是底层为链表,查询慢增删快,是多线程不同步的
3.java.util.linkedList集合 implements List接口
linkedList集合特点:
1.底层是链表:增删快查询慢
2.里面包含大量操作首尾元素的方法
注:使用linkedList集合特有方法,不能使用多态
4.常用方法:
{-public void addFirst(E e);将指定元素插入链表开头-public void addLast(E e);将指定元素插入链表结尾-public void push(E e);将元素压栈-public void clear();清空集合元素-public E getFirst();返回链表第一个值-public E getLast();返回链表最后一个值-public E removeFirst();移除并返回链表第一个值-public E removeLast();移除并返回链表最后一个值-public E pop();从此链表所表示的堆栈处弹出一个元素-public boolean isEmpty();判空,若空则返回true
}
四、Vector
1.Vector源码分析{
JDK7和JDK8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组,在扩容方面,默认扩容为原来的数组长度的2倍
}
2.public class vector底层实现为数组,是同步单线程的,速度慢,被ArrayList取代
五、比较ArrayList、LinkedList、Vector三者的异同:
同:三个类都是实现了List接口,存储数据的特点相同:存储有序的可以重复的数据
不同:见上文
六、List接口中额外定义了一些新方法,使用的是Collection接口中定义的方法和额外定义了一些新方法
list接口中的常用方法:
void add(int index,Object ele);在index位置插入ele元素boolean addAll(int index,Collection eles);从index位置将eles中的所有元素添加进来;区分add(只加一个元素)和addAll(加一个集合);可以存储null值Object get(int index);获取指定index位置的元素int indexOf(Object obj);返回obj集合中首次出现的位置,如果不存在返回-1int lastIndexOf(Object obj);返回obj在当前集合中末次出现的位置,如果不存在返回-1Object remove(int index);移除指定index位置的元素,并返回此元素Object set(int index,Object ele);设置指定index位置的元素为ele;替换指定元素的值,返回替换的目标值List sublist(int fromIndex,int toIndex);返回从fromIndex到toIndex位置的子集合int size();获取集合的长度,返回值即集合包含元素个数
注:
1.在list中remove(int index)方法和remove(Object obj)若要使用Object则要求造对象,否则默认使用index
2.防止索引越界:
indexOutOfBoundsException;集合索引越界
ArrayIndexOutOfBoundsException;数组索引越界
StringIndexOutOfBoundsException;字符串索引越界
小节常用方法:
增:void add(Object ele);
删:Object remove(int index);/Object remove(Object obj);
改:Object set(int index,Object ele);
查:Object get(int index);
插:void add(int index,Object ele);
长度:size();
遍历:1):Iterator迭代器方式;2)增强for循环;3)普通循环
tip:一般把顺序表和链表看做最基本的数据结构(或真实的数据结构);把栈、队列、数和图看做抽象数据结构,简称ADT
Set接口:
|---->Collection接口:单列集合,用于存储一个一个的对象
|---->Set接口,存储无序的不可重复的数据---->离散数学中的集合
|---->HashSet:
作为Set接口的主要实现类;
线程不安全;可以存储null值;
底层使用数组存储(在jdk7中一开始就指定数组长度是16,在jdk8中在调用add()方法时才指定了数组的初始化长度为16)
其底层实现是数组+链表(针对jdk7),在jdk8中底层是hashMap
|---->LinkedHashSet:
作为HashSet的子类;遍历子内部数据时可以按照添加的顺序遍历(因为其底层实现了双向链表)。
对于频繁地遍历操作,LinkedHashSet效率高于HashSet
|---->TreeSet:底层利用红黑树存储数据,可以按照添加的对象指定属性,进行排序
一、Set接口概述:
1、Set接口继承了collection接口,是不包含重复元素的collection即存储无序的不可重复的数据,此接口没有索引(不能使用普通for循环):
java.util.set接口 extends collection接口
其实现类:TreeSet集合HashSet集合【其中LinkedHashSet集合(有序的集合)继承HashSet集合】
2、set接口特点:
1.不允许有重复元素
2.无索引,没有带索引的方法,不能用普通的for循环遍历
以HashSet为例子说明
1.无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引顺序添加,而是根据数据的Hash值决定
2.不可重复性:保证添加的元素按照equals()方法判断时,不能返回true.即相同的元素只能添加一个
3、Set接口中没有额外定义新方法,使用的均是Collection接口中定义的方法
4、要求:向Set(主要指:HashSet、LinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()方法和equals()方法;重写的hashCode()和equals()尽可能保持一致性,以实现对象相等原则,即:“相等的对象必须具有相等的散列码”。为了保证一直性在hashCode中用到的属性在equals中也建议重复使用一保持一致性的特点。所以才有:Set集合存储元素不重复的元素的前提:存储的元素必须重写hashcode和equals方法
5.对于set来说,其比较对象的方式是(先利用对象所在类的hashCode()方法通过对象的具体属性得出此对象的hash值)先比较对象的hash值,当其hash值相同,则此对象无法加入set,当hash值不同时再利用对象所在类的equals()方法比较对象的内容,当对象内容一致时对象无法加入set,当对象内容不一致时便可以加入set
tip:每个对象都有一个属于自己的hash值
二、添加元素的过程
Set集合不允许存储重复元素的原理
{Set集合在调用add方法的时候,add方法会调用元素的hashcode方法和equals方法,判断元素是否重复set.add(s1);add方法会调用s1的hashcode方法,计算字符串“abc”的hash值:96354,在集合中找有没有96354这个hash值的元素,发现没有就把s1存储到集合中set.add(s2);add方法会调用s2的hashcode方法,计算字符串“abc”的hash值:96354,在集合中找有没有96354这个hash值的元素,发现有(hash冲突)s2调用equals方法和hash值相同的元素进行比较s2.equals(s1),返回true.两元素hash值相同,equals返回true,认定两个元素相同,就不会把s2存储到集合中set.add("重地");add方法会调用"重地"的hashcode方法,计算字符串“重地”的hash值:1179395,在集合中找有没有1179395这个hash值的元素,发现没有就把“重地”存储到集合中set.add("通话");add方法会调用"通话"的hashcode方法,计算字符串“通话”的hash值:1179395,在集合中找有没有1179395这个hash值的元素,发现有(hash冲突),“通话”调用equals方法和hash值相同的元素进行比“通话”.equals(“重地”),返回false.两元素hash值相同,equals返回false,认定两个元素不相同.就把“通话”存储到集合中
}
以HashSet为列:
向HashSet中添加元素a,先调用元素a所在的类的hashCode()方法,计算元素a的hash值,此hash值接着通过某种算法计算出在HashSet底层数组中存放的位置(即:索引位置),判断此数组上的位置是否已进有元素:
若此位置上没有其他元素,则元素a添加成功---->情况1
若此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与b的hash值:
若hash值不同,则元素a添加成功。---->情况2
若hash值相同,进而需要调用语速a所在类的equals()方法:
equals()返回true,元素a添加失败
equals()返回false,元素a添加成功---->情况3
对于添加成功情况2和情况3而言:元素a与已经存在指定索引位置上的元素以链表的方式存储
jdk7:元素a放到数组中,指向原来的元素
jkd8:原来的元素在数组中指向元素a
小节:7上8下
实例一
{在在添加对象的本类中重写了HashCode方法用于确保添加元素的正确性public static int hashCode(Object a[]) {if (a == null)return 0;int result = 1;for (Object element : a)result = 31 * result + (element == null ? 0 : element.hashCode());是用31原因:31=2<<5-1;系数选择要大同时要考虑内存溢出的问题return result;}
}
三、HashSet:
1、java.util.HashSet集合 implement Set集合
HashSet集合特点:
1.不允许有重复元素
2.无索引,没有带索引的方法,不能用普通的for循环遍历
3.是一个无序的集合,存取元素顺序可能不一致
4.底层是Hash表结构(查询速度特别快)
5.HashSet存储自定义类型元素
2、哈希值:是一个十进制整数由系统自动给出(即对象的地址值,是一个逻辑地址,是模拟出来得到的地址,不是数据实际存储的物理地址)在object类中有一个方法,可以获取对象哈希值
int HashCode()返回该对象的哈希值
hashcode方法的源码:
public native int hashCode();
native:代表该方法调用的是本地操作系统的方法
3、HashSet集合存储数据的结构(哈希表)
jdk1.8版本以前:哈希表=数组+链表
jdk1.8版本以后:哈希表=数组+链表,哈希表=数组+红黑树(提高查询速度,如果链表的长度超过8位,就将链表转换为为红黑树)哈希表特定速度快,哈希冲突(两个元素不同但哈希值相同)。存储数据到集合中先计算元素的哈希值,哈希表的数组结构:把元素进行分组(相同哈希值的元素是一组)链表/红黑树结构把相同哈希值的元素连接在一起。
4、set集合报错元素唯一:
存储的元素(String,Integer,...,Student,Person,..),必须重写hashcode和equals方法
四、LinkedHashSet的使用:
1.LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据。即java.util.LinkedHashSet集合 extends HashSet集合
优点:
对于频繁地遍历操作,LinkedHashSet效率高于HashSet
LinkedHashSet集合特点:
底层是hash表(数组+链表/红黑树)+链表;多了一条链表(记录元素的存储顺序),保证元素有序
五、ThreeSet的使用:
1.向ThreeSet中添加的数据,要求是由同一个类提供的,即要求是同一个类的对象
2.两种排序方式,自然排序(实现Comparable接口)和定制排序(Comparator)
在自然排序中去比较两对象是否相同的标准为:compareTo()方法返回0,不在是equals()方法。即TreeSet比较元素时只看
compareTo()方法,因此,当compareTo()方法仅处理姓名时,使得不同属性的同名对象被当做一个对象处理,若要严格区分同名
的不同对象,则需要对compareTo()方法的逻辑进行改进
在定制排序中去比较两对象是否相同的标准为:compare()方法返回0,不在是equals()方法 实例分析一
{@Testpublic void test04(){HashSet<Object> set = new HashSet<>();B0P1D5Person01 p1=new B0P1D5Person01(1001,"AA");B0P1D5Person01 p2=new B0P1D5Person01(1002,"BB");创造出p1和p2两个对象set.add(p1);set.add(p2);通过两个属性(id,name)参与的hash算法得出两个对象的hash值,再利用此hash值通过某些确定的算法确定两个对象在set中存储的位置(存储索引),此位置(存储索引)不一定是连续存储位置(存储索引)System.out.println(set);打印p1,p2对象的相应信息p1.name="CC";将p1对象名称改为cc,此时在set中存在p1,p2两个对象set.remove(p1);首先通过p1所在位置(索引)找到p1(此时p1的name属性从原来的aa变为现在的cc),此时对set来说要调用remove方法删除目标元素则先要找到目标元素的hash值,那么进行p1现有两个属性参与的hash值计算出现在p1对象的hash值,再利用此hash值通过某些确定的算法确定此时的p1对象应该存储在set的什么地方(即现有p1的存储索引)显然计算出的存储位置和p1现在的存储位置不一致,于是在对应的地方(对应存储索引处)将p1删除。而此时经过某些确定的算法的出的存储位置(存储索引处)上没有元素,所以此方法执行结果无效System.out.println(set);输出p1,p2信息set.add(new B0P1D5Person01(1001,"CC"));此时直接通过新new出的对象中的属性id和name,进行此对象的hash值计算,得到此对象的hash值后再通过某些确定的算法确定此新对象在set的存储位置(存储索引),由于此存储位置是空的,所以将此对象存储在此System.out.println(set);此时在set中存放了p1,p2和新new出来的对象set.remove(p1);先计算出此时p1的hash值,再利用此hash值通过某些确定的算法确定p1理论上的存储位置,然而在这个存储位置上并不存在,存在的是新new出来的对象,所以把新new出来的对象给删除l,于是此时在set中存储的仍是p1,p2,新new出的对象已进被删除System.out.println(set);打印set中现有的p1,p2set.add(new B0P1D5Person01(1001,"AA"));此时新new出来的对象,经过其两个属性参与计算出其自身的hash值,此hash值再通过某些确定的算法确定此新对象在set的存储位置(存储索引),此时的出的此新对象的存储索引和p1的存储索引一直,于是将此对象和p1对象进行equals对比,发现此对象和p1对象不同名所以将此对象添加到set中,即jdk7中让新new对象存放在p1索引处,同时让新new对象指向p1对象;或者在jdk8中让p1指向新new对象System.out.println(set);打印set中现有的p1,p2和新new对象}
}
以上是本篇小节,不喜勿喷,感谢理解
相关链接:
【JAVASE(8)】JAVASE学习--集合篇三(Map接口)_lixxkv的博客-CSDN博客
【JAVASE(8)】JAVASE学习--集合篇一(集合概述)_lixxkv的博客-CSDN博客