Java面试题—初级(9)
139. Java中的泛型是什么 ? 使用泛型的好处是什么?
泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
好处:
1、类型安全,提供编译期间的类型检测
2、前后兼容
3、泛化代码,代码可以更多的重复利用
4、性能较高,用GJ(泛型JAVA)编写的代码可以为java编译器和虚拟机带来更多的类型信息,这些信息对java程序做进一步优化提供条件。
140,Java的泛型是如何工作的 ? 什么是类型擦除 ?如何工作?
1、类型检查:在生成字节码之前提供类型检查
2、类型擦除:所有类型参数都用他们的限定类型替换,包括类、变量和方法(类型擦除)
3、如果类型擦除和多态性发生了冲突时,则在子类中生成桥方法解决
4、如果调用泛型方法的返回类型被擦除,则在调用该方法时插入强制类型转换
类型擦除:
所有类型参数都用他们的限定类型替换:
比如T->Object ? extends BaseClass->BaseClass
如何工作:
泛型是通过类型擦除来实现的,编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。例如 List<String>在运行时仅用一个List来表示。这样做的目的,是确保能和Java 5之前的版本开发二进制类库进行兼容。你无法在运行时访问到类型参数,因为编译器已经把泛型类型转换成了原始类型。根据你对这个泛型问题的回答情况,你会得到一些后续提问,比如为什么泛型是由类型擦除来实现的或者给你展示一些会导致编译器出错的错误泛型代码。
141,你可以把List<String>传递给一个接受List<Object>参数的方法吗?
对任何一个不太熟悉泛型的人来说,这个Java泛型题目看起来令人疑惑,因为乍看起来String是一种Object,所以 List<String>应当可以用在需要List<Object>的地方,但是事实并非如此。真这样做的话会导致编译错误。如果你再深一步考虑,你会发现Java这样做是有意义的,因为List<Object>可以存储任何类型的对象包括String, Integer等等,而List<String>却只能用来存储String s。
List<Object> objectList;
List<String> stringList;
objectList = stringList; //compilation error incompatible types
142,如何阻止Java中的类型未检查的警告?
如果你把泛型和原始类型混合起来使用,例如下列代码,java 5的javac编译器会产生类型未检查的警告,例如
List<String> rawList = newArrayList()
注意: Hello.java使用了未检查或称为不安全的操作;
这种警告可以使用@SuppressWarnings(“unchecked”)注解来屏蔽。
143,Java中List<Object>和原始类型List之间的区别?
原始类型和带参数类型<Object>之间的主要区别是,在编译时编译器不会对原始类型进行类型安全检查,却会对带参数的类型进行检查,通过使用Object作为类型,可以告知编译器该方法可以接受任何类型的对象,比如String或Integer。
这道题的考察点在于对泛型中原始类型的正确理解。它们之间的第二点区别是,你可以把任何带参数的类型传递给原始类型List,但却不能把List<String>传递给接受 List<Object>的方法,因为会产生编译错误。
144,编写一段泛型程序来实现LRU缓存?
对于喜欢Java编程的人来说这相当于是一次练习。给你个提示,LinkedHashMap可以用来实现固定大小的LRU缓存,当LRU缓存已经满了的时候,它会把最老的键值对移出缓存。
LinkedHashMap提供了一个称为removeEldestEntry()的方法,该方法会被put() 和putAll()调用来删除最老的键值对。当然,如果你已经编写了一个可运行的JUnit测试,你也可以随意编写你自己的实现代码。
145,Array中可以用泛型吗?
这可能是Java泛型面试题中最简单的一个了,当然前提是你要知道Array事实上并不支持泛型,这也是为什么Joshua Bloch在Effective Java一书中建议使用List来代替Array,因为List可以提供编译期的类型安全保证,而Array却不能。
146,如何编写一个泛型方法,让它能接受泛型参数并返回泛型类型?
编写泛型方法并不困难,你需要用泛型类型来替代原始类型,比如使用T, E or K,V等被广泛认可的类型占位符。最简单的情况下,一个泛型方法可能会像这样:
public V put(K key, V value) {
return cahe.put(key,value);
}
147,C++模板和java泛型之间有何不同?
java泛型实现根植于“类型消除”这一概念。当源代码被转换为Java虚拟机字节码时,这种技术会消除参数化类型。有了Java泛型,我们可以做的事情也并没有真正改变多少;他只是让代码变得漂亮些。鉴于此,Java泛型有时也被称为“语法糖”。
这和 C++模板截然不同。在 C++中,模板本质上就是一套宏指令集,只是换了个名头,编译器会针对每种类型创建一份模板代码的副本。
由于架构设计上的差异,Java泛型和C++模板有很多不同点:
C++模板可以使用int等基本数据类型。Java则不行,必须转而使用Integer。
在Java中,可以将模板的参数类型限定为某种特定类型。
在C++中,类型参数可以实例化,但java不支持。
在Java中,类型参数不能用于静态方法(?)和变量,因为它们会被不同类型参数指定的实例共享。在C++,这些类时不同的,因此类型参数可以用于静态方法和静态变量。
在Java中,不管类型参数是什么,所有的实例变量都是同一类型。类型参数会在运行时被抹去。在C++中,类型参数不同,实例变量也不同。
148,AJAX有哪些有点和缺点?
优点:
1、最大的一点是页面无刷新,用户的体验非常好。
2、使用异步方式与服务器通信,具有更加迅速的响应能力。
3、可以把以前一些服务器负担的工作转嫁到客户端,利用客户端闲置的能力来处理,减轻服务器和带宽的负担,节约空间和宽带租用成本。并且减轻服务器的负担,ajax的原则是“按需取数据”,可以最大程度的减少冗余请求,和响应对服务器造成的负担。
4、基于标准化的并被广泛支持的技术,不需要下载插件或者小程序。
缺点:
1、ajax不支持浏览器back按钮。
2、安全问题 AJAX暴露了与服务器交互的细节。
3、对搜索引擎的支持比较弱。
4、破坏了程序的异常机制。
5、不容易调试。
149,AJAX应用和传统Web应用有什么不同?
在传统的Javascript编程中,如果想得到服务器端数据库或文件上的信息,或者发送客户端信息到服务器,需要建立一个HTML form然后GET或者POST数据到服务器端。用户需要点击”Submit”按钮来发送或者接受数据信息,然后等待服务器响应请求,页面重新加载。
因为服务器每次都会返回一个新的页面, 所以传统的web应用有可能很慢而且用户交互不友好。
使用AJAX技术, 就可以使Javascript通过XMLHttpRequest对象直接与服务器进行交互。
通过HTTP Request, 一个web页面可以发送一个请求到web服务器并且接受web服务器返回的信息(不用重新加载页面),展示给用户的还是同一个页面,用户感觉页面刷新,也看不到到Javascript后台进行的发送请求和接受响应,体验非常好。
150,Ajax的实现流程是怎样的?
(1)创建XMLHttpRequest对象,也就是创建一个异步调用对象.
(2)创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息.
(3)设置响应HTTP请求状态变化的函数.
(4)发送HTTP请求.
(5)获取异步调用返回的数据.
(6)使用JavaScript和DOM实现局部刷新.
具体一点:
1,创建XNLHttpRequest对象
(不考虑ie)XMLHttpRequest request = new XMLHttprequest();
2,创建新的Http请求
XMLHttprequest.open(method,url,flag,name,password);
3,设置响应Http请求变化的函数
XMLHttprequest.onreadystatechange=getData;
function getData(){
if(XMLHttprequest.readyState==4){
获取数据
}
}
4,发送http请求
XMLHttprequest.send(data);
5,获取异步调用返回的对象
,function(data){
//异步提交后,交互成功,返回的data便是异步调用返回的对象,该对象是一个string类型的
}
6,使用js、DOM实现局部刷新
myDiv.innerHTML=''这是刷新后的数据''
151,简单说一下数据库的三范式?
第一范式:数据库表的每一个字段都是不可分割的
第二范式:数据库表中的非主属性只依赖于主键
第三范式:不存在非主属性对关键字的传递函数依赖关系
152,Java集合框架是什么?说出一些集合框架的优点?
每种编程语言中都有集合,最初的Java版本包含几种集合类:Vector、Stack、HashTable和Array。
随着集合的广泛使用,Java1.2提出了囊括所有集合接口、实现和算法的集合框架。在保证线程安全的情况下使用泛型和并发集合类,Java已经经历了很久。它还包括在Java并发包中,阻塞接口以及它们的实现。
集合框架的部分优点如下:
(1)使用核心集合类降低开发成本,而非实现我们自己的集合类。
(2)随着使用经过严格测试的集合框架类,代码质量会得到提高。
(3)通过使用JDK附带的集合类,可以降低代码维护成本。
(4)复用性和可操作性。
153,Java集合框架的基础接口有哪些?
Collection为集合层级的根接口。一个集合代表一组对象,这些对象即为它的元素。Java平台不提供这个接口任何直接的实现。
Set是一个不能包含重复元素的集合。这个接口对数学集合抽象进行建模,被用来代表集合,就如一副牌。
List是一个有序集合,可以包含重复元素。你可以通过它的索引来访问任何元素。List更像长度动态变换的数组。
Map是一个将key映射到value的对象.一个Map不能包含重复的key:每个key最多只能映射一个value。
一些其它的接口有Queue、Dequeue、SortedSet、SortedMap和ListIterator。
154,集合框架中的泛型有什么优点?
Java1.5引入了泛型,所有的集合接口和实现都大量地使用它。泛型允许我们为集合提供一个可以容纳的对象类型。
因此,如果你添加其它类型的任何元素,它会在编译时报错。这避免了在运行时出现ClassCastException,因为你将会在编译时得到报错信息。
泛型也使得代码整洁,我们不需要使用显式转换和instanceOf操作符。它也给运行时带来好处,因为不会产生类型检查的字节码指令。
155,Enumeration和Iterator接口的区别?
Enumeration的速度是Iterator的两倍,也使用更少的内存。Enumeration是非常基础的,也满足了基础的需要。
但是,与Enumeration相比,Iterator更加安全,因为当一个集合正在被遍历的时候,它会阻止其它线程去修改集合。
迭代器取代了Java集合框架中的Enumeration。迭代器允许调用者从集合中移除元素,而Enumeration不能做到。为了使它的功能更加清晰,迭代器方法名已经经过改善。
156,Iterater和ListIterator之间有什么区别?
1,我们可以使用Iterator来遍历Set和List集合,而ListIterator只能遍历List。
2,Iterator只可以向前遍历,而LIstIterator可以双向遍历。
3,ListIterator从Iterator接口继承,然后添加了一些额外的功能,比如添加一个元素、替换一个元素、获取前面或后面元素的索引位置。
157,我们如何对一组对象进行排序?
如果我们需要对一个对象数组进行排序,我们可以使用Arrays.sort()方法。如果我们需要排序一个对象列表,我们可以使用Collection.sort()方法。
两个类都有用于自然排序(使用Comparable)或基于标准的排序(使用Comparator)的重载方法sort()。
Collections内部使用数组排序方法,所有它们两者都有相同的性能,只是Collections需要花时间将列表转换为数组。
158,与Java集合框架相关的有哪些最好的实践?
1,根据需要选择正确的集合类型。比如,如果指定了大小,我们会选用Array而非ArrayList。如果我们想根据插入顺序遍历一个Map,我们需要使用TreeMap。如果我们不想重复,我们应该使用Set。
2,一些集合类允许指定初始容量,所以如果我们能够估计到存储元素的数量,我们可以使用它,就避免了重新哈希或大小调整。
3,基于接口编程,而非基于实现编程,它允许我们后来轻易地改变实现。
4,总是使用类型安全的泛型,避免在运行时出现ClassCastException。
5,使用JDK提供的不可变类作为Map的key,可以避免自己实现hashCode()和equals()。
6,尽可能使用Collections工具类,或者获取只读、同步或空的集合,而非编写自己的实现。它将会提供代码重用性,它有着更好的稳定性和可维护性。
159,什么是事务?,
事务是恢复和并发控制的基本单位
事务的四个基本特征
原子性,一致性,隔离性,持久性
原子性和一致性差不多,意思是要么全部成功,要么就失败
一致性是说,从一个一致性状态到另一个一致性状态
隔离性是说一个事务执行的过程中不能被另一个事务干扰
持久性也就是事务一旦提交,他对数据库中数据的改变就应该是永久的,不能变的(这里只是面试简单的说一下理解,详细理解问度娘)
161,Java内存模型是什么?
Java内存模型规定和指引Java程序在不同的内存架构、CPU和操作系统间有确定性地行为。它在多线程的情况下尤其重要。Java内存模型对一个线程所做的变动能被其它线程可见提供了保证,它们之间是先行发生关系。这个关系定义了一些规则让程序员在并发编程时思路更清晰。比如,先行发生关系确保了:
线程内的代码能够按先后顺序执行,这被称为程序次序规则。
对于同一个锁,一个解锁操作一定要发生在时间上后发生的另一个锁定操作之前,也叫做管程锁定规则。
前一个对volatile的写操作在后一个volatile的读操作之前,也叫volatile变量规则。
一个线程内的任何操作必需在这个线程的start()调用之后,也叫作线程启动规则。
一个线程的所有操作都会在线程终止之前,线程终止规则。
一个对象的终结操作必需在这个对象构造完成之后,也叫对象终结规则。
可传递性
更多介绍可以移步并发编程网:
(深入理解java内存模型系列文章:http://ifeve.com/java-memory-model-0)
162,Java中interrupted 和isInterruptedd方法的区别?
interrupted() 和 isInterrupted()的主要区别是前者会将中断状态清除而后者不会。Java多线程的中断机制是用内部标识来实现的,调用Thread.interrupt()来中断一个线程就会设置中断标识为true。当中断线程调用静态方法Thread.interrupted()来检查中断状态时,中断状态会被清零。
非静态方法isInterrupted()用来查询其它线程的中断状态且不会改变中断状态标识。简单的说就是任何抛出InterruptedException异常的方法都会将中断状态清零。无论如何,一个线程的中断状态都有可能被其它线程调用中断来改变。
163,Java中的同步集合与并发集合有什么区别?
同步集合与并发集合都为多线程和并发提供了合适的线程安全的集合,不过并发集合的可扩展性更高。在Java1.5之前程序员们只有同步集合来用且在多线程并发的时候会导致争用,阻碍了系统的扩展性。Java5介绍了并发集合像ConcurrentHashMap,不仅提供线程安全还用锁分离和内部分区等现代技术提高了可扩展性。
不管是同步集合还是并发集合他们都支持线程安全,他们之间主要的区别体现在性能和可扩展性,还有他们如何实现的线程安全上。
同步HashMap, Hashtable, HashSet, Vector, ArrayList 相比他们并发的实现(ConcurrentHashMap, CopyOnWriteArrayList, CopyOnWriteHashSet)会慢得多。造成如此慢的主要原因是锁, 同步集合会把整个Map或List锁起来,而并发集合不会。并发集合实现线程安全是通过使用先进的和成熟的技术像锁剥离。
比如ConcurrentHashMap 会把整个Map 划分成几个片段,只对相关的几个片段上锁,同时允许多线程访问其他未上锁的片段。
同样的,CopyOnWriteArrayList 允许多个线程以非同步的方式读,当有线程写的时候它会将整个List复制一个副本给它。
如果在读多写少这种对并发集合有利的条件下使用并发集合,这会比使用同步集合更具有可伸缩性。
164,什么是线程池? 为什么要使用它?
创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建的线程数有限。为了避免这些问题,在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程。从JDK1.5开始,Java API提供了Executor框架让你可以创建不同的线程池。比如单线程池,每次处理一个任务;数目固定的线程池或者是缓存线程池(一个适合很多生存期短的任务的程序的可扩展线程池)
线程池的作用,就是在调用线程的时候初始化一定数量的线程,有线程过来的时候,先检测初始化的线程还有空的没有,没有就再看当前运行中的线程数是不是已经达到了最大数,如果没有,就新分配一个线程去处理。
就像餐馆中吃饭一样,从里面叫一个服务员出来;但如果已经达到了最大数,就相当于服务员已经用尽了,那没得办法,另外的线程就只有等了,直到有新的“服务员”为止。
线程池的优点就是可以管理线程,有一个高度中枢,这样程序才不会乱,保证系统不会因为大量的并发而因为资源不足挂掉。
165,Java中活锁和死锁有什么区别?
活锁:一个线程通常会有会响应其他线程的活动。如果其他线程也会响应另一个线程的活动,那么就有可能发生活锁。同死锁一样,发生活锁的线程无法继续执行。然而线程并没有阻塞——他们在忙于响应对方无法恢复工作。这就相当于两个在走廊相遇的人:甲向他自己的左边靠想让乙过去,而乙向他的右边靠想让甲过去。可见他们阻塞了对方。甲向他的右边靠,而乙向他的左边靠,他们还是阻塞了对方。
死锁:两个或更多线程阻塞着等待其它处于死锁状态的线程所持有的锁。死锁通常发生在多个线程同时但以不同的顺序请求同一组锁的时候,死锁会让你的程序挂起无法完成任务。
166,如何避免死锁?
死锁的发生必须满足以下四个条件:
互斥条件:一个资源每次只能被一个进程使用。
请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
三种用于避免死锁的技术:
加锁顺序(线程按照一定的顺序加锁)
加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
死锁检测
(死锁原因及如何避免更深理解移步:http://blog.csdn.net/ls5718/article/details/51896159)
167,notify()和notifyAll()有什么区别?
1,notify()和notifyAll()都是Object对象用于通知处在等待该对象的线程的方法。
2,void notify(): 唤醒一个正在等待该对象的线程。
3,void notifyAll(): 唤醒所有正在等待该对象的线程。
两者的最大区别在于:
notifyAll使所有原来在该对象上等待被notify的线程统统退出wait的状态,变成等待该对象上的锁,一旦该对象被解锁,他们就会去竞争。
notify他只是选择一个wait状态线程进行通知,并使它获得该对象上的锁,但不惊动其他同样在等待被该对象notify的线程们,当第一个线程运行完毕以后释放对象上的锁,此时如果该对象没有再次使用notify语句,即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,继续处在wait状态,直到这个对象发出一个notify或notifyAll,它们等待的是被notify或notifyAll,而不是锁。
168,什么是可重入锁(ReentrantLock)?
Java.util.concurrent.lock 中的 Lock 框架是锁定的一个抽象,它允许把锁定的实现作为Java 类,而不是作为语言的特性来实现。这就为Lock 的多种实现留下了空间,各种实现可能有不同的调度算法、性能特性或者锁定语义。 ReentrantLock 类实现了Lock ,它拥有与synchronized 相同的并发性和内存语义,但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。(换句话说,当许多线程都想访问共享资源时,JVM可以花更少的时候来调度线程,把更多时间用在执行线程上。)
Reentrant 锁意味着什么呢?简单来说,它有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,然后锁需要被释放两次才能获得真正释放。这模仿了synchronized 的语义;如果线程进入由线程已经拥有的监控器保护的synchronized 块,就允许线程继续进行,当线程退出第二个(或者后续)synchronized块的时候,不释放锁,只有线程退出它进入的监控器保护的第一个synchronized 块时,才释放锁。
169,读写锁可以用于什么应用场景?
读写锁可以用于 “多读少写” 的场景,读写锁支持多个读操作并发执行,写操作只能由一个线程来操作
ReadWriteLock对向数据结构相对不频繁地写入,但是有多个任务要经常读取这个数据结构的这类情况进行了优化。ReadWriteLock使得你可以同时有多个读取者,只要它们都不试图写入即可。如果写锁已经被其他任务持有,那么任何读取者都不能访问,直至这个写锁被释放为止。
ReadWriteLock 对程序性能的提高主要受制于如下几个因素:
1,数据被读取的频率与被修改的频率相比较的结果。
2,读取和写入的时间
3,有多少线程竞争
4,是否在多处理机器上运行
170,堆和栈在内存中的区别是什么?
概念:
栈(stack)是为执行线程留出的内存空间。当函数被调用的时候,栈顶为局部变量和一些 bookkeeping 数据预留块。当函数执行完毕,块就没有用了,可能在下次的函数调用的时候再被使用。栈通常用后进先出的方式预留空间;因此最近的保留块通常最先被释放。这么做可以使跟踪堆栈变的简单;从栈中释放块只不过是指针的偏移而已。
堆(heap)是为动态分配预留的内存空间。和栈不一样,从堆上分配和重新分配块没有固定模式;你可以在任何时候分配和释放它。这样使得跟踪哪部分堆已经被分配和被释放变的异常复杂;有许多定制的堆分配策略用来为不同的使用模式下调整堆的性能。
区别:
内存分配:
栈:由编译器自动分配和释放,存放函数的参数、局部变量、临时变量、函数返回地址等。
堆:一般人为分配和释放,对Java而言由系统释放回收,但对于C++等,必须手动释放,如果没有手动释放会引起内存泄漏。
系统响应:
栈:只要栈的剩余空间大于所申请的空间,系统将为程序提供内存,否则将报异常提示栈溢出。
堆:在记录空闲内存地址的链表中寻找一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。
大小限制:
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 windows下,栈的大小是2M,如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。
结论:堆获得的空间比较灵活,也比较大。
分配效率:
栈:由系统自动分配,速度较快,无法人为控制。
堆:由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。
存储内容:
栈:在栈中,第一个进栈的是主函数下一条指令的地址,然后是函数的各个参数,在大多数编译器中,参数是由右往左入栈,然后是函数中的局部变量。注意,静态变量不入栈。出栈则刚好顺序相反。
堆:一般在堆的头部用一个字节存放堆的大小,具体内容受人为控制。
171,求出1000之内的所有水仙花数
概念:
水仙花数指的是一个三位整数,它的各位数字的立方和等于这个数本身.。
解法:
public class NumberOfDaffodils {
public static void main(String[] args) {
// TODO Auto-generated method stub
int hundred, ten, bits;
System.out.println("水仙花数为:");
for (int i = 100; i <= 999; i++)
{
hundred = i / 100;
ten = i % 100 / 10;
bits = i % 10;
if (i == hundred * hundred * hundred + ten * ten * ten + bits * bits * bits)
{
System.out.print(i + " ");
}
}
}
}
172,比较一下几种常用的排序算法,简单的写一下你知道的几种排序算法?
比较:
1.稳定性比较
插入排序、冒泡排序、二叉树排序、二路归并排序及其他线形排序是稳定的
选择排序、希尔排序、快速排序、堆排序是不稳定的
2.时间复杂性比较
插入排序、冒泡排序、选择排序的时间复杂性为O(n2)
其它非线形排序的时间复杂性为O(nlog2n)
线形排序的时间复杂性为O(n);
3.辅助空间的比较
线形排序、二路归并排序的辅助空间为O(n);
其它排序的辅助空间为O(1);
4.其它比较
*插入、冒泡排序的速度较慢,但参加排序的序列局部或整体有序时,这种排序能达到较快的速度,但是在这种情况下,快速排序反而慢了。
*当n较小时,对稳定性不作要求时宜用选择排序,对稳定性有要求时宜用插入或冒泡排序。
*若待排序的记录的关键字在一个明显有限范围内时,且空间允许是用桶排序。
*当n较大时,关键字元素比较随机,对稳定性没要求宜用快速排序。
*当n较大时,关键字元素可能出现本身是有序的,对稳定性有要求时,空间允许的情况下宜用归并排序。
*当n较大时,关键字元素可能出现本身是有序的,对稳定性没有要求时宜用堆排序。
常见的排序算法:
选择排序
public class SelectionSort {
public void selectionSort(int[] array) {
int temp;
for (int i = 0; i < array.length - 1; i++) {
for (int j = i + 1; j <= array.length - 1; j++) {
if (array[i] > array[j]) {
// 注意和冒泡排序的区别,这里是i和j比较。
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
} // 打印每趟排序结果
for (int m = 0; m <= array.length - 1; m++) {
System.out.print(array[m] + "\t");
}
System.out.println();
}
}
public static void main(String[] args) {
SelectionSort selectionSort = new SelectionSort();
int[] array = { 5, 69, 12, 3, 56, 789, 2, 5648, 23 };
selectionSort.selectionSort(array);
for (int m = 0; m <= array.length - 1; m++) {
System.out.print(array[m] + "\t");
}
}
}
插入排序
public class InsertSort {
public void insertSort(int[] array, int first, int last) {
int temp, i, j;
for (i = first + 1; i <= last - 1; i++) {
temp = array[i];
j = i - 1;
while (j >= first && array[j] > temp) {
array[j + 1] = array[j];
j--;
}
array[j + 1] = temp; // 打印每次排序结果
for (int m = 0; m <= array.length - 1; m++) {
System.out.print(array[m] + "\t");
}
System.out.println();
}
}
public static void main(String[] args) {
InsertSort insertSort = new InsertSort();
int[] array = { 5, 69, 12, 3, 56, 789, 2, 5648, 23 };
insertSort.insertSort(array, 0, array.length);
for (int i = 0; i <= array.length - 1; i++) {
System.out.print(array[i] + "\t");
}
}
}
快速排序
public class QuickSort {
public int partition(int[] sortArray, int low, int height) {
int key = sortArray[low];
while (low < height) {
while (low < height && sortArray[height] >= key)
height--;
sortArray[low] = sortArray[height];
while (low < height && sortArray[low] <= key)
low++;
sortArray[height] = sortArray[low];
}
sortArray[low] = key; // 打印每次排序结果
for (int i = 0; i <= sortArray.length - 1; i++) {
System.out.print(sortArray[i] + "\t");
}
System.out.println();
return low;
}
public void sort(int[] sortArray, int low, int height) {
if (low < height) {
int result = partition(sortArray, low, height);
sort(sortArray, low, result - 1);
sort(sortArray, result + 1, height);
}
}
public static void main(String[] args) {
QuickSort quickSort = new QuickSort();
int[] array = { 5, 69, 12, 3, 56, 789, 2, 5648, 23 };
for (int i = 0; i <= array.length - 1; i++) {
System.out.print(array[i] + "\t");
}
System.out.println();
quickSort.sort(array, 0, 8);
for (int i = 0; i <= array.length - 1; i++) {
System.out.print(array[i] + "\t");
}
}
}
希尔排序
public class ShellSort {
public void shellSort(int[] array, int n) {
int i, j, gap;
int temp;
for (gap = n / 2; gap > 0; gap /= 2) {
for (i = gap; i < n; i++) {
for (j = i - gap; j >= 0 && array[j] > array[j + gap]; j -= gap) {
temp = array[j];
array[j] = array[j + gap];
array[j + gap] = temp;
} // 打印每趟排序结果
for (int m = 0; m <= array.length - 1; m++) {
System.out.print(array[m] + "\t");
}
System.out.println();
}
}
}
public static void main(String[] args) {
ShellSort shellSort = new ShellSort();
int[] array = { 5, 69, 12, 3, 56, 789, 2, 5648, 23 };
shellSort.shellSort(array, array.length);
for (int m = 0; m <= array.length - 1; m++) {
System.out.print(array[m] + "\t");
}
}
}
《《--扫描二维码关注他!
【Java知音】公众号,每天早上8:30为您准时推送一篇技术文章
在Java知音公众号内回复“面试题聚合”,送你一份Java面试题宝典。