数据结构

1.list,map,set之间的区别

数据结构_第1张图片

一,Conllection:

他有两个子接口,Set和List,

1,Set(公共特点;无序)

是一个无序的集合接口,并且元素不可重复,他有两个子类,一个是hashSet,还有一个是继承了SortedSet接口的TreeSet,这两个set集合有什么特点呢?

首先,hashSet的底层是hashmap,他有着hashmap中键的特性,那就是,无序,不可重复性,

其次,treeset实现了sortset接口,sortedset有排序能力,也就意味着treeset也有着排序的能力,他是使用二叉树进行排序的。

2,list(有序):

list接口也有两个子类,一个是arraylist,一个是linkedList,

首先arralist的底层是object[]是一个数组,也就意味着他有着数组的特性,但是和数组相比他比数组灵活,无需设置长度,他是有序的,所以查找块,增删比较慢,

和arralist其实有一个兄弟叫vector,他和arralist是一样的

Vector是线程安全的,也就是说是它的方法之间是线程同步的,而arralist线程是异步的也就是说他是不安全的,但是效率高,相比之下,建议使用arralist.,

linkedlist:

他底层是以链表的形式进行排序的,两个元素之间就如同链子一样前关联,如果进行增删操作的时候,只需要将某个元素替换然后将后边的关联简单修改就可以完成,所以说建议查询用arralist,增删用linkedlist;

二,map(公共特点特点:以键值对的形式存储,):

简单来讲常用的map类的集合也有三个,常用的有三个子类实现了map接口:map存在的意义就是为了快速查找,通过键的直接找到值,因为键是不可重复的。

1,hashMap和hashtable:

两者放在一起比较(底层都是hash表结构):hashmap线程是不安全的允许键值对为null,二hashtable线程是安全的不允许键值对为null,两者的其他属性是一样的,所以两者的使用要看具体的情况而定,

2,treemap(底层是二叉树)

线程不同步,可用于给Map集合中的键进行排序,虽然说有些集合是无序的,但是子类个别的也是按照另一种方式进行了排序,只不过不符合我们日常的排序方式。是按照某种规则进行的。

总结:三者的存在关系:

Map中元素,可以将key序列、value序列单独抽取出来。

使用keySet()抽取key序列,将map中的所有keys生成一个Set。

使用values()抽取value序列,将map中的所有values生成一个Collection。

为什么一个生成Set,一个生成Collection?那是因为,key总是独一无二的,value允许重复。

hashMap出现线程不安全的原因:

HashMap的实现里没有锁的机制,因此它是线程不安全的。

其实只要有锁的机制,可以通过锁实现线程安全,我们在读写HashMap对象的时候加锁,以保障这个对象的线程安全,但不代表HashMap本身是线程安全的,因为是外力(你自己加的锁)使然。

为啥不在HashMap内部加锁让它变成线程安全?

这样会增加单线程访问的资源消耗,即使没有多线程访问,也要每次检查、加锁、解锁。

实际上有线程安全的Map,Collections里面有个静态方法可以返回一个线程安全版本的HashMap

publicstatic Map synchronizedMap(Map m) {returnnewSynchronizedMap(m);  }

另外java5之后还提供了ConcurrentHashMap类,提供更高效的线程安全操作。

hashMap出现线程不安全的表现:

表现1:

多个线程同时操作一个hashmap就可能出现不安全的情况:

比如A B两个线程(A线程获数据 B线程存数据) 同时操作myHashMap

1.B线程执行存放数据

modelHashMap.put("1","2");

2.A线程执行get获取数据

modelHashMap.get("1")

A线程获取的值本来应该是2,但是如果A线程在刚到达获取的动作还没执行的时候,

线程执行的机会又跳到线程B,此时线程B又对modelHashMap赋值 如:modelHashMap.put("1","3");

然后线程虚拟机又执行线程A,A取到的值为3,这样map中第一个存放的值 就会丢失。。。

要保证值的准确,就要保证操作的原子性,就是保证A操作从头开始不能被打断。。所有要用同步关键字,或者使用java 1.5中的current新包中的ConcurrentHashMap,这是线程安全的,在java最新的并发包中,对之前非线程安全的工具,如hashMap List 都做了同步封转。

表现2:

一般我们声明HashMap时,使用的都是默认的构造方法:HashMap,看了代码你会发现,它还有其它的构造方法:HashMap(int initialCapacity, float loadFactor),其中参数initialCapacity为初始容量,loadFactor为加载因子,而之前我们看到的threshold = (int)(capacity * loadFactor); 如果在默认情况下,一个HashMap的容量为16,加载因子为0.75,那么阀值就是12,所以在往HashMap中put的值到达12时,它将自动扩容两倍,如果两个线程同时遇到HashMap的大小达到12的倍数时,就很有可能会出现在将oldTable转移到newTable的过程中遇到问题,从而导致最终的HashMap的值存储异常。

表现3:

构造entry单链表时,也会出现不安全的情况。


ArrayList VS Vector (都实现了 list 接口,都是数组实现)不安全 安全

扩容 50% 扩容 100%


ArrayList VS LinkedList

数组 链表

适合检索和在末尾插入 or 删除 适合在中间插入 or 删除

LinkedList 还实现了queue 接口,他还提供peek(),pool(),offer()等方法。


hashmap 和hashtable 底层原理(必问,每一个点都要清楚)给几个网址:http://zhangshixi.iteye.com/blog/672697

http://www.admin10000.com/document/3322.html http://www.cnblogs.com/skywang12345/p/3310887.html http://blog.csdn.net/chdjj/article/details/38581035


ArrayList 和LinkedList 的remove 和contains 方法都依赖equals 方法。

HashSet:底层hash 表,若两个元素的hash 值不同,则一定不是同一个元素,若hash 值相同,则判断euqals,若equals 相同,则是同一个对象,若equals 不同,则不是一个对象(hashset 的contains 和remove 依赖于和顺从的和equals)。

TreeSet 集合的特点是可以对其中的元素进行排序,换句话说,一种情况是放进treeset 中的元素必须具有比较性,集合的底层结构是二叉树,保证元素唯一性的方法是compareTo 方法返回0。另一种情况是元素没有比较性,但是集合有比较性,定义一个类,实现conparator 接口,实现一个比较器。


二叉平衡树:他是一颗空树or 他左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一颗平衡二叉树。


图的深度优先遍历和广度优先遍历。深:设x 是当前被访问的顶点,在对x 做过访问标

记后,选择一条从x 出发的未检测过的边(x,y),若发现顶点y 已经访问过,则重新选择另一条从x 出发的未检测的边,否则沿边(x,y)到达未曾访问过的y,对y 访问并将其标记为已经访问;然后从y 开始搜索,直到搜索完从y 出发的all 路径,即访问完al 从y 出发可达的顶点后,才回溯回x,并且再选择一条从x 出发的未检测的边。上述过程直至从x 出发的all 边都已经检测过为止。遍历结果不唯一。广:设x 和y 是两个相继要被访问的未访问过的节点,他们的邻节点分别是x1,x2,x3。。Xn 和y1,y2.。。Yn。为确保先访问的节点其邻节点先被访问,在搜索过程中使用FIFO 队列来保存已经访问过的顶点。当访问x 和y 时,这两个顶点相继入列。此后,当x 和y 相继出队时,我们分别从x 和y 出发搜索其邻节点x1,x2,x3。。Xn 和y1,y2.。。Yn。对其中未访问者进行访问并将其入队列。这种方法是将每个已经访问的顶点入队,故保证了每个顶点至多只有一次入队。广度优先遍历结果不唯一。

hashset 不是线程安全的。

若两个对象的equals 为true,则hashcode 为true。


B 树、B-树、B+树、B*树

1.B 树就是B-树,是一种多路索引树(并不是二叉的)1)任意非叶子节点最多只有m 个儿子,且m>2 。2)根节点的儿子数最多为[2,m]。3)除了跟节点以外的非叶子节点的儿子数为[m/2,m]。4)每个节点至少存放m/2-1 和最多m-1 个关键字。5)非叶子节点的关键字个数=儿子数-1。6)非叶子节点的关键字,k[1], k[2] ,k[3]…. k[m-1],且k[i]< k[i+1].7)非叶子节点的指针p[1], p[2], p[3]…..p[m],其中p[i]指向关键字< k[i]的子树,p[m]指向关键字> p[m-1]的子树,其他p[i]指向关键字属于(k[i-1],k[i])的子树。8)所有叶子节点位于同一层。

[if !supportLists]2. [endif]B+数:适合文件系统索引,1)其基本定义和B-树相同。2)非叶子节点的儿子数=关键字个数 3)非叶子节点的子树指针 p[i]指向关键字属于[k[i], k[i+1])的子树(B-树为开区间)。

4)all 叶子节点增加一个键指针。5)all 关键字都在叶子节点出现,且叶子节点本身依赖关键字的大小顺序排序。

查找某一个关键字时,B-树可能在非叶子节点中命中,B+树只有到达叶子节点才命中。

[if !supportLists]3. [endif]B* 树是B+树的变体,B*树中非跟和非叶子节点增加指向兄弟的指针。B*树中非叶子节点关键字个数>=2m/3,即块的最低使用率为2/3(B+树为1/2)。

B+树比b-树根适合OS 的文件索引和数据库索引。

B-树:有序数组+平衡多叉树。

B+树:有序数组链表+平衡多叉树

B*树:一个丰满的B+树。

Hashmap 不是线程安全的,所以如果在使用迭代器的过程中有其他线程修改了map,则会抛出ConcurrentModificationException,这就是所谓的fast-fail 策略,是通过modcount 实现,对Hashmap 内容的修改都将增加这个值,那么在迭代器初始化时会将这个值赋给迭代器的exceptedmodcount。在迭代过程中,判断modcount 和exceptedmodcount 是否相等,若不相等,则说明其他线程修改了map。Modcount 是volatile 的。

Hashtable 继承Dictionary 类,实现map、cloneable、serializable 接口。

Hashtable 键为null,则抛出NullPointException。他的clear 方法就是把数组元素都设为null。

Treeset:树总是平衡的,保证插入、删除、查询的性能O(logn).

在hashmap 的构造器中不会初始化entry 数组,而是在执行put 操作时,才真正创建table

数组。Put:1)先判断entry 数组是否为空,为空则创建entry 数组。2)判断是否为空。。。

4)codcount++;5)判断是否要扩容,当发生hash 冲突并且size>阈值时,扩容。

在单链表中,增加一个头结点是方便运算。哈夫曼树有左右子树之分。

稀疏矩阵,三分组的方法:非零元素所在的行,列以及他的值构成一个三元组(x,y,

v),然后按照某种规律存放三元组,这样可以节约空间。还要3 个成员来记录矩阵的行数、列数和总的元素数。


BFS:广度优先遍历,使用队列,先进先出,正拓扑排序。

DFS:深度优先遍历,使用栈,先进后出,逆拓扑排序。

若 try,finally 都有 return,则忽略 try 的 return 语句。求字符串的 next 数组 自己百度吧

快速排序在无序时效率最高,有序时效率低。

不受初始元素影响的排序:选择,堆,归并。

堆排序、基数排序、归并排序、选择排序的排序次数和初始化状态无关,即最好情况和最坏情况一样

知道中序,且知道前序或后序任何一个就可以确定一颗二叉树。

HashSet 基于hashmap 实现,hashset 的元素都放在hashmap 的可以上面,而value 统一为private static final object PERSENT=new Object();允许null 值,不允许重。Hashset 的add 调用的是hashmap 的put()。

Collection.sort(list list) 按自然顺序排序,元素必须有可比较性,实现comparable 接口。

Collection.sort(list list,comparator)


Listiterator 是一个更强大的iterator 子类型,只能用于各种list 访问。

Tree-set 将元素放在红黑树。

Linkedlist 是queue 的实现。

PriorityQueue:优先级越高,越在头,越早出来。0-

BlockingQueue:1)ArrayBlockingQueue 一个由数组实现的有界阻塞队列,其构造函数必须带一个int 参数,来指明其大小。其所含的对象是由FIFO 顺序排列的。2 )

linkedBlockingQueue:大小不一定,若构造函数传入int,则BlockingQueue 有大小限制,否则大小为Integer.MAX_VALUE,其所含的对象是由FIFO 顺序。3)PriorityBlockingQueue:类似于linkedBlockingQueue,但不是fifo,而是自然顺序或比较器的顺序。4)synchronizeQueue 对其的操作必须是放和取交替完成的.

B-tree 索引可以用于使用< <= = >= >或者between 运算符的列比较。如果Like 的参数是一个没有以通配符起始的常量字符串也可以使用这种索引。Hash 索引:1)只能够用于使用

=或<=>运算符的相比较,不能用于有范围的运算符,因为键经过hash 以后的值没有意义2)优化器不能使用hash 索引来加速order by 操作。这种类型的索引不能够用于按照顺序查找下一个条目3)mysql 无法使用hash 索引来估计两个值之间有多少行4)查找某行记录必须全键匹配,而且B-tree 索引,任何该键的左前缀都可以用以查找记录5)若将一张myisam 或innodb 表换为一个hash 索引的内存表,一些查询可能受影响。Interator 和collection、map 无关。

ThreadLocal 不继承Thread,也不实现Runable 接口,ThreadLocal 类为每一个线程都维护了自己独有的变量拷贝。每个线程都拥有自己独立的变量,其作用在于数据独立。

ThreadLocal 采用hash 表的方式来为每个线程提供一个变量的副本。

哈希表查找的时间复杂度与原始数量无关,hash 表在查找元素时是通过计算hash 值来定位元素的位置的,从而直接访问元素。所以hash 表的插入、删除、查找都是O(1)。

Arrays.sort 对于基本数据类型用快速排序,对象数组用改进的归并排序。

一致性hash:在移除or 添加一个cache 时,他能够尽可能小的改变已经存在key 映射关系。

Hash 冲突解决办法:1)链地址法2)开发地址法。

二分查找返回的是:-(插入点)-1

满二叉树:除叶子结点外的所有节点均有两个子节点,叶子节点数达到最大,所有叶子节点都在同一层;完全二叉树:满是完全的特例;哈夫曼树:每个节点要么没子节点点,要么有两个子节点。

链表倒数第k 个节点,见《剑指offer》108 页。

Hashmap 的实现用hash 表,tree-map 采用红黑树。

Hashmap,两次哈希,第一次直接调用key 的hashcode 方法,第二次再调用一个函数即hash(key.hashcode()),此方法加入高位计算,防止低位不变高位变化时造成的冲突。

Linkedhashset 和linkedhashmap 保持元素插入顺序。

任何collection 都可以生成interator,list 还可以生成listiterator。

若在一个方法内部抛出了异常,这个方法将在抛出异常的过程中结束。在编译时被强制检查的异常称为被检查异常。

一个出现在基类方法得当异常说明中的异常,不一定会出现在派类类方法的异常说明里。

二叉排序树又名二叉搜索树,左<根<右

在hashmap 中,若key 为null,则put 时调用putForNullKey(value)方法,该方法是在table[0]中查找

key 为null 的元素,若找到,则将value 重新赋值给这个元素的value,并且返回原来的value,若没有,则把

(null,value)添加到table[0]中。

线性结构分为顺序结构和链接存储结构。

Bit-map:用一个bit 位来表示某个元素对应的value,而key 为该元素,最多有多少个元素就要多少个bit 位。

iterator 遍历集合A 中的元素。在某个时刻线程y 修改了集合A 的结构(是结构上的修改,而不是简单的修改集合元素的内容)。则这时程序就会抛出

iterator 中的数据,修改完成后改变原有数据的引用即可。

ConcurrentModificationException,从而发生Fast-fail。若单线程违反该规则,也会抛出异常。因为

modcount 与ExceptedModcount

改变不同步。当iterator 执行next()、remove()时,都要判断modcount 与ExceptedModcount

是否相等,若不相等,则抛异常。

当hashmap 的get()方法返回null 时,既可以表示hashmap 中没有该键,也可以表示该键对应的值为null,所以hashmap 中不能用get()方法来判断是否存在某个键,而应该用

containskey()。

深度为k 的完全二叉树,最多有2k-1 个节点,第k 层最多有2k-1 个节点。

你可能感兴趣的:(数据结构)