1、数组和集合的比较java
数组不是面向对象的,存在明显的缺陷,集合弥补了数组的缺点,比数组更灵活更实用,并且不一样的集合框架类可适用不一样场合。以下:
1:数组能存放基本数据类型和对象,而集合类存放的都是对象的引用,而非对象自己!
2:数组容易固定没法动态改变,集合类容量动态改变。
3:数组没法判断其中实际存有多少元素,length只告诉了数组的容量,而集合的size()能够确切知道元素的个数
4:集合有多种实现方式和不一样适用场合,不像数组仅采用顺序表方式
5:集合以类的形式存在,具备封装、继承、多态等类的特性,经过简单的方法和属性便可实现各类复杂操做,大大提升了软件的开发效率面试
2、Java集合算法
此图可用Windows系统自带画图工具查看比较清晰数组
Collection和Map,是集合框架的根接口。安全
Collection的子接口:服务器
Set:接口 ---实现类: HashSet、LinkedHashSet
Set的子接口SortedSet接口---实现类:TreeSet
List:接口---实现类: LinkedList,Vector,ArrayList 数据结构
List集合并发
有序列表,容许存放重复的元素;
实现类:
ArrayList:数组实现,查询快,增删慢,轻量级;(线程不安全)
LinkedList:双向链表实现,增删快,查询慢 (线程不安全)
Vector:数组实现,重量级 (线程安全、使用少)app
ArrayList框架
底层是Object数组,因此ArrayList具备数组的查询速度快的优势以及增删速度慢的缺点。
而在LinkedList的底层是一种双向循环链表。在此链表上每个数据节点都由三部分组成:前指针(指向前面的节点的位置),数据,后指针(指向后面的节点的位置)。最后一个节点的后指针指向第一个节点的前指针,造成一个循环。
双向循环链表的查询效率低可是增删效率高。
ArrayList和LinkedList在用法上没有区别,可是在功能上仍是有区别的。
LinkedList
LinkedList是采用双向循环链表实现的。
利用LinkedList实现栈(stack)、队列(queue)、双向队列(double-ended queue )。
它具备方法addFirst()、addLast()、getFirst()、getLast()、removeFirst()、removeLast()等。
常常用在增删操做较多而查询操做不多的状况下:
队列和堆栈。
队列:先进先出的数据结构。
栈:后进先出的数据结构。
注意:使用栈的时候必定不能提供方法让不是最后一个元素的元素得到出栈的机会。
Vector
(与ArrayList类似,区别是Vector是重量级的组件,使用使消耗的资源比较多。)
结论:在考虑并发的状况下用Vector(保证线程的安全)。
在不考虑并发的状况下用ArrayList(不能保证线程的安全)。
ArrayList自动扩充机制
实现机制:ArrayList.ensureCapacity(int minCapacity)
首先获得当前elementData 属性的长度oldCapacity。
而后经过判断oldCapacity和minCapacity参数谁大来决定是否须要扩容, 若是minCapacity大于
oldCapacity,那么咱们就对当前的List对象进行扩容。
扩容的的策略为:取(oldCapacity * 3)/2 + 1和minCapacity之间更大的那个。而后使用数组拷
贝的方法,把之前存放的数据转移到新的数组对象中
若是minCapacity不大于oldCapacity那么就不进行扩容。
用LinkedList实现队列:
队列(Queue)是限定全部的插入只能在表的一端进行,而全部的删除都在表的另外一端进行的线性表。
表中容许插入的一端称为队尾(Rear),容许删除的一端称为队头(Front)。
队列的操做是按先进先出(FIFO)的原则进行的。
队列的物理存储能够用顺序存储结构,也能够用链式存储结构。
用LinkedList实现栈:
栈(Stack)也是一种特殊的线性表,是一种后进先出(LIFO)的结构。
栈是限定仅在表尾进行插入和删除运算的线性表,表尾称为栈顶(top),表头称为栈底(bottom)。
栈的物理存储能够用顺序存储结构,也能够用链式存储结构。
List经常使用方法:
void add(int index, Object element) :添加对象element到位置index上
boolean addAll(int index, Collection collection) :在index位置后添加容器collection中全部的元素
Object get(int index) :取出下标为index的位置的元素
int indexOf(Object element) :查找对象element 在List中第一次出现的位置
int lastIndexOf(Object element) :查找对象element 在List中最后出现的位置
Object remove(int index) :删除index位置上的元素
ListIterator listIterator(int startIndex) :返回一个ListIterator 跌代器,开始位置为startIndex
List subList(int fromIndex, int toIndex) :返回一个子列表List ,元素存放为从 fromIndex 到toIndex以前的一个元素
Set集合
扩展Collection接口
无序集合,不容许存放重复的元素;容许使用null元素
对 add()、equals() 和 hashCode() 方法添加了限制
HashSet和TreeSet是Set的实现
Set—》hashSet linkedHashSet
SortedSet —》 TreeSet
HashSet 的后台有一个HashMap;初始化后台容量;只不过生成一个HashSet的话,系统只提供key的访问; 若是有两个Key重复,那么会覆盖以前的;
实现类 :
HashSet:equals返回true,hashCode返回相同的整数;哈希表;存储的数据是无序的。
LinkedHashSet:此实现与 HashSet 的不一样以外在于,后者维护着一个运行于全部条目的双重连接列表。存储的数据是有序的。
HashSet类直接实现了Set接口, 其底层实际上是包装了一个HashMap去实现的。HashSet采用HashCode算法来存取集合中的元素,所以具备比较好的读取和查找性能。
不只不能保证元素插入的顺序,并且在元素在之后的顺序中也可能变化(这是由HashSet按HashCode存储对象(元素)决定的,对象变化则可能致使HashCode变化)
HashSet是线程非安全的
HashSet元素值能够为NULL
HashSet经常使用方法:
public boolean contains(Object o) :若是set包含指定元素,返回true
public Iterator iterator()返回set中元素的迭代器
public Object[] toArray() :返回包含set中全部元素的数组public Object[] toArray(Object[] a) :返回包含set中全部元素的数组,返回数组的运行时类型是指定数组的运行时类型
public boolean add(Object o) :若是set中不存在指定元素,则向set加入
public boolean remove(Object o) :若是set中存在指定元素,则从set中删除
public boolean removeAll(Collection c) :若是set包含指定集合,则从set中删除指定集合的全部元素
public boolean containsAll(Collection c) :若是set包含指定集合的全部元素,返回true。若是指定集合也是一个set,只有是当前set的子集时,方法返回true
实现Set接口的HashSet,依靠HashMap来实现的。
咱们应该为要存放到散列表的各个对象定义hashCode()和equals()。
前面说过,Set集合是不容许重复元素的,不然将会引起各类奇怪的问题。那么HashSet如何判断元素重复呢?
HashSet须要同时经过equals和HashCode来判断两个元素是否相等,具体规则是,若是两个元素经过equals为true,而且两个元素的hashCode相等,则这两个元素相等(即重复)。
因此若是要重写保存在HashSet中的对象的equals方法,也要重写hashCode方法,重写先后hashCode返回的结果相等(即保证保存在同一个位置)。全部参与计算 hashCode() 返回值的关键属性,都应该用于做为 equals() 比较的标准。
试想若是重写了equals方法但不重写hashCode方法,即相同equals结果的两个对象将会被HashSet看成两个元素保存起来,这与咱们设计HashSet的初衷不符(元素不重复)。
另外若是两个元素哈市Code相等但equals结果不为true,HashSet会将这两个元素保存在同一个位置,并将超过一个的元素以链表方式保存,这将影响HashSet的效率。
若是重写了equals方法但没有重写hashCode方法,则HashSet可能没法正常工做,好比下面的例子。
上面注释了hashCode方法,因此你将会看到下面的结果。
false
[R[count:9 # hashCode:14927396], R[count:5 # hashCode:24417480], R[count:-2 # hashCode:31817359], R[count:-3 # hashCode:13884241]]
取消注释,则结果就正确了 copy
true
[R[count:5 # hashCode:5], R[count:9 # hashCode:9], R[count:-3 # hashCode:-3], R[count:-2 # hashCode:-2]]
如何达到不能存在重复元素的目的?
“键”就是咱们要存入的对象,“值”则是一个常量。这样能够确保,咱们所须要的存储的信息
之是“键”。而“键”在Map中是不能重复的,这就保证了咱们存入Set中的全部的元素都不重复。
HashSet如何过滤重复元素
调用元素HashCode得到哈希码--》判断哈希码是否相等,不相等则录入
---》相等则判断equals()后是否相等,不相等在进行 hashcode录入,相等不录入
LinkedHashSet的特征
LinkedHashSet是HashSet的一个子类,LinkedHashSet也根据HashCode的值来决定元素的存储位置,但同时它还用一个链表来维护元素的插入顺序,插入的时候即要计算hashCode又要维护链表,而遍历的时候只须要按链表来访问元素。查看LinkedHashSet的源码发现它是样的,
在JAVA7中, LinkedHashSet没有定义任何方法,只有四个构造函数,它的构造函数调用了父类(HashSet)的带三个参数的构造方法,父类的构造函数以下,
由此可知,LinkedHashSet本质上也是从LinkedHashMap而来,LinkedHashSet的全部方法都继承自HashSet, 而它能维持元素的插入顺序的性质则继承自LinkedHashMap.
下面是一个LinkedHashSet维持元素插入顺序的例子,
输入以下
TreeSet实现了SortedSet接口,顾名思义这是一种排序的Set集合,查看jdk源码发现底层是用TreeMap实现的,本质上是一个红黑树原理。 正由于它是排序了的,因此相对HashSet来讲,TreeSet提供了一些额外的按排序位置访问元素的方法,例如first(), last(), lower(), higher(), subSet(), headSet(), tailSet().
TreeSet的排序分两种类型,一种是天然排序,另外一种是定制排序。
TreeSet 会调用compareTo方法比较元素大小,而后按升序排序。因此天然排序中的元素对象,都必须实现了Comparable接口,不然会跑出异常。对于TreeSet判断元素是否重复的标准,也是调用元素从Comparable接口继承而来额compareTo方法,若是返回0则是重复元素(两个元素I相等)。Java的常见类都已经实现了Comparable接口,下面举例说明没有实现Comparable存入TreeSet时引起异常的状况。
运行程序会抛出以下异常
将上面的Err类实现Comparable接口以后程序就能正常运行了
还有个重要问题是,由于TreeSet会调用元素的compareTo方法,这就要求全部元素的类型都相同,不然也会发生异常。也就是说,TreeSet只容许存入同一类的元素。例以下面这个例子就会抛出类型转换异常
运行结果
TreeSet还有一种排序就是定制排序,定制排序时候,须要关联一个 Comparator对象,由Comparator提供排序逻辑。下面就是一个使用Lambda表达式代替Comparator对象来提供定制排序的例子。 下面是一个定制排序的列子
固然将Comparator直接写入TreeSet初始化中也能够。以下。
TreeSet是依靠TreeMap来实现的。
TreeSet是一个有序集合,TreeSet中元素将按照升序排列,缺省是按照天然顺序进行排列,意味着TreeSet中元素要实现Comparable接口
咱们能够在构造TreeSet对象时,传递实现了Comparator接口的比较器对象。
Comparable和Comparator
Comparable 接口以提供天然排序顺序。
对于那些没有天然顺序的类、或者当您想要一个不一样于天然顺序的顺序时,您能够实现
Comparator 接口来定义您本身的排序函数。能够将Comparator传递给Collections.sort或Arrays.sort。
Comparator接口
当一个类并未实现Comparable,或者不喜欢缺省的Comaparable行为。能够实现Comparator接口
直接实现Comparator的compare接口完成自定义比较类。
例:Arrays.sort(results, new Comparator<RepDataQueryResultVO>() 数组排序 RepDataQueryExecutor
例:Collections.sort(lst,new Comparator<TaskPrintSchemeVO>()
EnumSet顾名思义就是专为枚举类型设计的集合,所以集合元素必须是枚举类型,不然会抛出异常。 EnumSet集合也是有序的,其顺序就是Enum类内元素定义的顺序。EnumSet存取的速度很是快,批量操做的速度也很快。EnumSet主要提供如下方法,allOf, complementOf, copyOf, noneOf, of, range等。注意到EnumSet并无提供任何构造函数,要建立一个EnumSet集合对象,只须要调用allOf等方法,下面是一个EnumSet的例子。
执行结果
几种Set的比较:
HashSet外部无序地遍历成员。
成员可为任意Object子类的对象,但若是覆盖了equals方法,同
时注意修改hashCode方法。
TreeSet外部有序地遍历成员;
附加实现了SortedSet, 支持子集等要求顺序的操做
成员要求实现Comparable接口,或者使用Comparator构造
TreeSet。成员通常为同一类型。
LinkedHashSet外部按成员的插入顺序遍历成员
成员与HashSet成员相似
HashSet是基于Hash算法实现的,其性能一般都优于TreeSet。咱们一般都应该使用HashSet,在咱们须要排序的功能时,咱们才使用TreeSet。
HashSet的元素存放顺序和咱们添加进去时候的顺序没有任何关系,而LinkedHashSet 则保持元素的添加顺序。TreeSet则是对咱们的Set中的元素进行排序存放。
通常来讲,当您要从集合中以有序的方式抽取元素时,TreeSet 实现就会有用处。为了能顺利进行,添加到 TreeSet 的元素必须是可排序的。 而您一样须要对添加到TreeSet中的类对象实现 Comparable 接口的支持。通常说来,先把元素添加到 HashSet,再把集合转换为 TreeSet 来进行有序遍历会更快。
HashSet和TreeSet是Set集合中用得最多的I集合。HashSet老是比TreeSet集合性能好,由于HashSet不须要额维护元素的顺序。
LinkedHashSet须要用额外的链表维护元素的插入顺序,所以在插入时性能比HashSet低,但在迭代访问(遍历)时性能更高。由于插入的时候即要计算hashCode又要维护链表,而遍历的时候只须要按链表来访问元素。
EnumSet元素是全部Set元素中性能最好的,可是它只能保存Enum类型的元素
Map
集合框架的第二类接口树。
它提供了一组键值的映射。其中存储的每一个对象都有一个相应的关键字(key),关键字决定了对象在Map中的存储位置。
关键字应该是惟一的,每一个key 只能映射一个value。
实现类:
HashMap、TreeMap、LinkedHashMap、Hashtable等
HashMap:键值对,key不能重复,可是value能够重复;key的实现就是HashSet;value对应着放;容许null的键或值;
Hashtable:线程安全的,不容许null的键或值;
Properties::key和value都是String类型,用来读配置文件;
TreeMap:对key排好序的Map; key 就是TreeSet, value对应每一个key; key要实现Comparable接口或TreeMap有本身的构造器;
LinkedHashMap: 此实现与 HashMap 的不一样之处在于,后者维护着一个运行于全部条目的双重连接列表。存储的数
据是有序的。
HashMap:
Map 主要用于存储键(key)值(value)对,根据键获得值,所以键不容许重复,但容许值重复。
HashMap 是一个最经常使用的Map,它根据键的HashCode 值存储数据,根据键能够直接获取它的值,具备很快的访问速度。
HashMap最多只容许一条记录的键为Null;容许多条记录的值为 Null;
HashMap不支持线程的同步,即任一时刻能够有多个线程同时写HashMap;可能会致使数据的不一致。若是须要同步,能够用 Collections的synchronizedMap方法使HashMap具备同步的能力。
HashMap实现原理---散列
Hash哈希算法的意义在于提供了一种快速存取数据的方法,它用一种算法创建键值与真实值之间的对应关系。散列表又称为哈希表。散列表算法的基本思想是:以结点的关键字为自变量,经过必定的函数关系(散列函数)计算出对应的函数值,以这个值做为该结点存储在散列表中地址。
当散列表中的元素存放太满,就必须进行再散列,将产生一个新的散列表,全部元素存放到新的散列表中,原先的散列表将被删除。在Java语言中,经过负载因子(load factor)来决定什么时候对散列表进行再散列。例如:若是负载因子0.75,当散列表中已经有75%位置已经放满,那么将进行再散列。
负载因子越高(越接近1.0),内存的使用效率越高,元素的寻找时间越长。负载因子越低(越接近0.0),元素的寻找时间越短,内存浪费越多。
什么时候需重写equals?
当一个类有本身特有的“逻辑相等”概念(不一样于对象身份的概念);
Object类仅仅提供了一个对引用的比较,若是两个引用不是同一个那就返回false,这是没法知足大多数对象比较的须要的,因此要覆盖;
使用==操做符检查实参是否为指向对象的引用”
使用instanceof操做符检查实参是否为正确的类型
把实参转换到正确的类型;
对于该类中每个“关键”域,检查实参中的域与当前对象中对应的域值是否匹 配。对于既不是float也不是double类型的基本类型的域,可使用==操做符 进行比较;对于对象引用类型的域,能够递归地调用所引用的对象的equals方法,对于float和double类型的域,先转换成int或long类型的值,而后使用==操做符比较;
当你编写完成了equals方法以后,应该问本身三个问题:它是不是对称的、传 递的、一致的? 若是答案是否认的,那么请找到 这些特性未能知足的缘由,再修改equals方法的代码
equals()和hashCode()同时覆写
尤为强调当一个对象被看成键值(或索引)来使用的时候要重写这两个方法;
覆写equals后,两个不一样实例可能在逻辑上相等,可是根据Object.hashCode方法却产生不一样的散列码,违反“相等的对象必须具备相等的散列码”。
致使,当你用其中的一个做为键保存到hashMap、hasoTable或hashSet中,再以“相等的”找另 一个做为键值去查找他们的时候,则根本找不到
不一样类型的hashCode取值
若是该域是布尔型的,计算(f?0:1)
若是是char,short,byte或int,计算(int)f
若是是long类型,计算(int)(f^(f>>>32))
若是是float类型,计算Float.floatToIntBits(f)
若是是double类型,计算Dobule.doubleToLongBits(f)
若是该域是一个对象引用,递归调用hashCode
若是该域是一个数组,则把每一个元素当作单独的域来处理,对每一个重要的元素计算一个散列码,
Map集合比较:
HashMap的存入顺序和输出顺序无关。
LinkedHashMap 则保留了键值对的存入顺序。
TreeMap则是对Map中的元素进行排序。
由于HashMap和LinkedHashMap 存储数据的速度比直接使用TreeMap 要快,存取效率要高。
当完成了全部的元素的存放后,咱们再对整个的Map中的元素进行排序。这样能够提升整个程序的运行的效率,缩短执行时间。
注意:TreeMap中是根据键(Key)进行排序的。而若是咱们要使用TreeMap来进行正常的排序的话,Key 中存放的对象必须实现Comparable 接口。
Map经常使用方法:
Object put(Object key,Object value):用来存放一个键-值对Map中
Object remove(Object key):根据key(键),移除键-值对,并将值返回
void putAll(Map mapping) :将另一个Map中的元素存入当前的Map中
void clear() :清空当前Map中的元素
Object get(Object key) :根据key(键)取得对应的值
boolean containsKey(Object key) :判断Map中是否存在某键(key)
boolean containsValue(Object value):判断Map中是否存在某值(value)
public Set keySet() :返回全部的键(key),并使用Set容器存放
public Collection values() :返回全部的值(Value),并使用Collection存放
public Set entrySet() :返回一个实现 Map.Entry 接口的元素 Set
集合遍历
一、加强for循环 for(Obj o:c){syso(o)}
二、使用iterator , Iterator it=c.iterator;
while(it.hasNext()){Object o = it.next()}
三、普通循环:for(Iterator it=c.iterator();it.hasNext();){it.next() }
代码实例
总结与面试
1.ArrayList: 元素单个,效率高,多用于查询
2.Vector: 元素单个,线程安全,多用于查询
3.LinkedList:元素单个,多用于插入和删除
4.HashMap: 元素成对,元素可为空
5.HashTable: 元素成对,线程安全,元素不可为空
HashMap和Hashtable的区别:
HashMap和Hashtable都是java的集合类,均可以用来存放java对象,这是他们的相同点
如下是他们的区别:
1.历史缘由:
Hashtable是基于陈旧的Dictionary类的,HashMap是java 1.2引进的Map接口的一个现实。
2.同步性:
Hashtable是同步的,这个类中的一些方法保证了Hashtable中的对象是线程安全的,而HashMap则是异步的,所以HashMap中的对象并非线程安全的,由于同步的要求会影响执行的效率,因此若是你不须要线程安全的结合那么使用HashMap是一个很好的选择,这样能够避免因为同步带来的没必要要的性能开销,从而提升效率,咱们通常所编写的程序都是异步的,但若是是服务器端的代码除外。
3.值:
HashMap可让你将空值做为一个表的条目的key或value
Hashtable是不能放入空值(null)的
ArrayList和Vector的区别:
ArrayList与Vector都是java的集合类,都是用来存放java对象,这是他们的相同点,
区别:
1.同步性:
Vector是同步的,这个类的一些方法保证了Vector中的对象的线程安全的,而ArrayList则是异步的,所以ArrayList中的对象并不 是线程安全的,由于同步要求会影响执行的效率,因此你不须要线程安全的集合那么使用ArrayList是一个很好的选择,这样能够避免因为同步带来的没必要 要的性能开销。
2.数据增加:
从内部实现的机制来说,ArrayList和Vector都是使用数组(Array)来控制集合中的对象,当你向两种类型中增长元素的时候,若是元素的数目超过了内部数组目前的长度他们都须要扩展内部数组的长度,Vector缺省状况下自动增加原来一倍的数组长度,ArrayList是原来的50%,因此最后你得到的这个集合所占的空间老是比你实际须要的要大,因此若是你要在集合中保存大量的数据,那么使用Vector有一些优点,由于你能够经过设置集合的初始大小来避免没必要要的资源开销。
总结:
1)若是要求线程安全,使用Vector,Hashtable
2)若是不要求线程安全,使用ArrayList,LinkedList,HashMap
3)若是要求键值对,则使用HashMap,Hashtable
4)若是数据量很大,又要求线程安全考虑Vector
arraylist和linkedlist联系与区别
1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2.对于随机访问get和set,ArrayList以为优于LinkedList,由于LinkedList要移动指针。
3.对于新增和删除操做add和remove,LinedList比较占优点,由于ArrayList要移动数据。 这一点要看实际状况的。若只对单条数据插入或删除,ArrayList的速度反而优于LinkedList。但如果批量随机的插入删除数据,LinkedList的速度大大优于ArrayList. 由于ArrayList每插入一条数据,要移动插入点及以后的全部数据。
HashMap与TreeMap联系与区别
一、 HashMap经过hashcode对其内容进行快速查找,而TreeMap中全部的元素都保持着某种固定的顺序,若是你须要获得一个有序的结果你就应该使用TreeMap(HashMap中元素的排列顺序是不固定的)。
二、在Map 中插入、删除和定位元素,HashMap是最好的选择。但若是您要按天然顺序或自定义顺序遍历键,那么TreeMap会更好。使用HashMap要求添加的键类明肯定义了hashCode()和 equals()的实现。
两个map中的元素同样,但顺序不同,致使hashCode()不同。
一样作测试:
在HashMap中,一样的值的map,顺序不一样,equals时,false;
而在treeMap中,一样的值的map,顺序不一样,equals时,true,说明,treeMap在equals()时是整理了顺序了的。
https://mp.weixin.qq.com/s/31fbwBmeFYn_yzQVx8520g