上一篇:
2-2 HashTable-LinkedHashMap-各个map的区别-hashset
这里是easy的java基础面试
下面是总的阅览:
java基础
java集合
JVM
多线程
mysql_数据库
计算机网络
nosql_redis
设计模式
操作系统
消息中间件activeMq
SSM框架面试题
服务中间件Dubbo
Collection.Sort 排序通过泛化实现对所有类型的排序,对基础类型如int,string按照字符表,数字大小排序,对自定义类型,通过comparable接口 / Comparator 外比较器。但是comparable比comparator耦合度强了一些。
1.7
collection.sort —> 底层调用了Arrays.sort()
{ sort(Object) —> 归并算法
sort(int等基础类型)-------> 快排
}
1.8
collection.sort (List list, comparator super T> c) {
list.sort©; 底层调用—> Arrays.sort() 但是类外比较器可以自己指定。
}
答:Iterator 可用来遍历 Set 和 List 集合,但是 ListIterator 只能用来遍
历 List。Iterator 对集合只能是前向遍历,ListIterator 既可以前向也可以后向。
ListIterator 实现了 Iterator 接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。
答:Iterator 的安全失败是基于对底层集合做拷贝,因此,它不受源集合上修改 的 影 响 。 java.util 包 下 面 的 所 有 的 集 合 类 都 是 快 速 失 败的 , 而
java.util.concurrent 包下面的所有的类都是安全失败的。快速失败的迭代器会抛出 ConcurrentModificationException异常,而安全失败的迭代器永远不会抛出这样的异常。
答:Enumeration 速度是 Iterator 的 2 倍,同时占用更少的内存。但是,Iterator 远远比Enumeration 安全,因为其他线程不能够修改正在被iterator 遍历的集合里面的对象。
同时,Iterator 允许调用者删除底层集合里面的元素,这对Enumeration 来说是不可能的。
ArrayList 和 LinkedList 都实现了 List 接口,他们有以下的不同点:
ArrayList 是基于索引的数据接口,它的底层是数组。它可以以 O(1)时间复杂度对元素进行
随机访问。与此对应,LinkedList 是以元素列表的形式存储它的数据,每一个元素都和它的
前一个和后一个元素链接在一起,在这种情况下,查找某个元素的时间复杂度是 O(n)。
相对于 ArrayList,LinkedList 的插入,添加,删除操作速度更快,因为当元素被添加到集合
任意位置的时候,不需要像数组那样重新计算大小或者是更新索引。
LinkedList 比 ArrayList 更占内存,因为 LinkedList 为每一个节点存储了两个引用,一个指
向前一个元素,一个指向下一个元素。
ArrayList 就是动态数组,是 Array 的复杂版本,动态的增加和减少元素.当更多的元素加入到 ArrayList 中时,其大小将会动态地增长。它的元素可以通过 get/set 方法直接访问,因为 ArrayList 本质上是一个数组。初始容量为 10。
1.插入元素的时候可能扩容,删除元素时不会缩小容量。
2.扩容增长为 Arraylist 增长原来的 0.5 倍
3. 而 Arraylist 没有设置增长空间的方法。
4. 线程不同步
Vector 和 ArrayList 类似, 区别在于 Vector 是同步类(synchronized).因此,开销就比ArrayList 要大。初始容量为 10。实现了随机访问接口,可以随机访问。Vector 是内部是以动态数组的形式来存储数据的。
1.Vector 还可以设置增长的空间大小,
2. 及 Vector 增长原来的 1 倍
3. vector 线程同步
LinkedList 是一个双链表,在添加和删除元素时具有比 ArrayList 更好的性能.但在 get与 set 方面弱于 ArrayList.当然,这些对比都是指数据量很大或者操作很频繁的情况下的对比。它还实现了 Queue 接口,该接口比 List 提供了更多的方法,包括 offer(),peek(),poll()等.
答:常用的迭代器设计模式,iterator 方法返回一个父类实现的迭代器。
迭代器的 hasNext 方法的作用是判断当前位置是否是数组最后一个位置,相等为 false,否则为 true。
迭代器 next 方法用于返回当前的元素,并把指针指向下一个元素。
值得注意的是,每次使用 next 方法的时候,都会判断创建迭代器获取的这个容器的计数器 modCount是否与此
时 的 不 相 等 ?
不 相 等 说 明 集 合 的 大 小 被 修 改 过 , 如 果 是 会 抛 出ConcurrentModificationException 异常,
如果相等调用 get 方法返回元素即可。
答:不同点:定义上:Array 可以包含基本类型和对象类型,ArrayList 只能包含对象类型。
容量上:Array 大小固定,ArrayList 的大小是动态变化的。操作上:ArrayList 提供更多的方法和特性,如:addAll(),removeAll(),iterator()等等。
使用基本数据类型或者知道数据元素数量的时候可以考虑 Array; ArrayList 处理固定数量的基本类型数据类型时会自动装箱来减少编码工作量,但是相对较慢。
相同点:
( 1 )两者都是基于索引的,都是基于数组的。
(2)两者都维护插入顺序,我们可以根据插入顺序来获取元素。
(3)ArrayList 和 Vector 的迭代器实现都是 fail-fast 的。
(4)ArrayList 和 Vector 两者允许 null 值,也可以使用索引值对元素进行随机访问。
不同点:
(1)Vector 是同步,线程安全,而 ArrayList 非同步,线程不安全。对于 ArrayList,如果迭代时改变列表,应该使用 CopyOnWriteArrayList。
(2)但是,ArrayList 比 Vector 要快,它因为有同步,不会过载。
(3)在使用上,ArrayList 更加通用,因为 Collections 工具类容易获取同步列表和只读列
表。
在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增
加、删除、修改),则会抛出 Concurrent Modification Exception。
原理:
迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变 modCount 的值。每当迭代器使
用 hashNext()/next()遍历下一个元素之前,都会检测 modCount 变量是否为 expectedmodCount 值,是的话就返回遍历;否则抛出异常,终止遍历。
注意:
这里异常的抛出条件是检测到 modCount!=expectedmodCount 这个条件。如
果集合发生变化时修改 modCount 值刚好又设置为了 expectedmodCount 值,则异常不会
抛出。因此,不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检
测并发修改的 bug。
场景:
java.util 包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改)。
采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原
有集合内容,在拷贝的集合上进行遍历。
原理:
由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发 Concurrent Modification Exception。
缺点:
基于拷贝内容的优点是避免了 Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,
在遍历期间原集合发生的修改迭代器是不知道的。
场景:
java.util.concurrent 包下的容器都是安全失败,可以在多线程下并发使用,并发修改。
快速失败和安全失败是对迭代器而言的。
快速失败:当在迭代一个集合的时候,如果有另外一个线程在修改这个集合,就会抛出 ConcurrentModification 异常,java.util 下都是快速失败。
安全失败:在迭代时候会在集合二层做一个拷贝,所以在修改集合上层元素不会影
响下层。在 java.util.concurrent 下都是安全失败。
浅拷贝: 对于基本数据类型的成员变量,采用值传递。
对于引用类型的成员变量,(某个数组,类的对象等),引用传递(内存地址)
方式:
拷贝构造函数 Person p = new Person(p2);
重写clone() 方法进行浅拷贝 ,在clone方法的类中重写clone()方法,通过obj = super.clone() return obj;
实例: clone()方式
package com.xlg.clone.浅拷贝;
/**
* @program: designpattern
* @description: 浅拷贝
* @author: Mr.Wang
* @create: 2020-11-12 15:03
**/
public class CopyConstructor {
public static void main(String[] args) {
// 1. 拷贝构造方法 没 Student 加 implements Cloneable 之前
// Age a = new Age(20);
// Person p = new Person("p", a);
// Person p_cpy = new Person(p);
// System.out.println(p);
// System.out.println(p_cpy);
// 2. 重写 clone
Age b = new Age(21);
Student s1 = new Student("王哥", b, 183);
Student s1_cpy = (Student) s1.clone();
System.out.println(s1);
System.out.println(s1_cpy);
System.out.println("---------------------------------");
// 尝试修改stu1中的各属性,观察stu2的属性有没有变化
s1.setName("easy");
// 改变 age这个属性的值 ,
b.setAge(22);
// s1.setAge(new Age(22)) 这样 s1_cpy是不会变化的
s1.setHeight(184);
// 注意 此时 引用的 age变化了,,但是 s1_cpy 中name 和 height是不变的。
System.out.println(s1);
System.out.println(s1_cpy);
}
}
class Student implements Cloneable {
private String name;
private Age sage;
private Integer height;
public Student(String name, Age sage, Integer height) {
this.name = name;
this.sage = sage;
this.height = height;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Age getSage() {
return sage;
}
public void setSage(Age sage) {
this.sage = sage;
}
public Integer getHeight() {
return height;
}
public void setHeight(Integer height) {
this.height = height;
}
@Override
public Object clone() {
Object obj = null;
try {
obj = super.clone(); // 注意 此时是重新创建了 student内存空间 但是内部的age地址复制了一份
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sage=" + sage +
", height=" + height +
'}';
}
}
class Age {
private Integer age;
public Age(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Age{" +
"age=" + age +
'}';
}
}
class Person implements Cloneable{
private String name;
private Age age;
public Person(Person p) {
this.name = p.name;
this.age = p.age;
}
public Person() {
}
public Person(String name, Age age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Age getAge() {
return age;
}
public void setAge(Age age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
实例结果:
Student{name='王哥', sage=Age{age=21}, height=183}
Student{name='王哥', sage=Age{age=21}, height=183}
---------------------------------
Student{name='easy', sage=Age{age=22}, height=184}
Student{name='王哥', sage=Age{age=22}, height=183}
深拷贝: 深拷贝对引用数据类型的成员变量的对象图中所有的对象都开辟了内存空间。 而浅拷贝只是传递地址指向,新的对象并没有引用数据类型,创建了内存空间。
方式:
对象序列化
重写clone方法,注意: 引用类型的每个类型都要实现clonenble接口 重写方法clone() 记住: 是对象图内存重新分配哦。
实例: 重写clone方法
package com.xlg.clone.深拷贝;
/**
* @program: designpattern
* @description: 深拷贝
* @author: Mr.Wang
* @create: 2020-11-12 15:32
**/
public class DeepCopy {
public static void main(String[] args) {
// 1. clone()
Age b = new Age(21);
Student s1 = new Student("王哥", b, 183);
Student s1_cpy = (Student) s1.clone();
System.out.println(s1);
System.out.println(s1_cpy);
System.out.println("---------------------------------");
// 尝试修改stu1中的各属性,观察stu2的属性有没有变化
s1.setName("easy");
// 改变 age这个属性的值 ,
b.setAge(22);
// s1.setAge(new Age(22)) 这样 s1_cpy是不会变化的
s1.setHeight(184);
// 注意 此时 s1_cpy 中name height age 都不变 。
System.out.println(s1);
System.out.println(s1_cpy);
}
}
class Student implements Cloneable {
private String name;
private Age sage;
private Integer height;
public Student(String name, Age sage, Integer height) {
this.name = name;
this.sage = sage;
this.height = height;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Age getSage() {
return sage;
}
public void setSage(Age sage) {
this.sage = sage;
}
public Integer getHeight() {
return height;
}
public void setHeight(Integer height) {
this.height = height;
}
@Override
public Object clone() {
Object obj = null;
try {
obj = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
Student s = (Student)obj;
s.sage = (Age)s.getSage().clone();
return obj;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sage=" + sage +
", height=" + height +
'}';
}
}
class Age implements Cloneable {
private Integer age;
public Age(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Age{" +
"age=" + age +
'}';
}
@Override
public Object clone() {
Object obj = null;
try {
obj = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
}
结果:
Student{name='王哥', sage=Age{age=21}, height=183}
Student{name='王哥', sage=Age{age=21}, height=183}
---------------------------------
Student{name='easy', sage=Age{age=22}, height=184}
Student{name='王哥', sage=Age{age=21}, height=183}
package com.xlg.clone.深拷贝.oo;
import java.io.*;
/* 通过序列化实现深拷贝 */
public class DeepCopyBySerialization {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Age a=new Age(20);
Student stu1=new Student("摇头耶稣",a,175);
//通过序列化方法实现深拷贝
ByteArrayOutputStream bos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.writeObject(stu1);
oos.flush();
ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
Student stu2=(Student)ois.readObject();
System.out.println(stu1.toString());
System.out.println(stu2.toString());
System.out.println();
//尝试修改stu1中的各属性,观察stu2的属性有没有变化
stu1.setName("大傻子");
//改变age这个引用类型的成员变量的值
a.setAge(99);
stu1.setLength(216);
System.out.println(stu1.toString());
System.out.println(stu2.toString());
}
}
/*
* 创建年龄类
*/
class Age implements Serializable{
//年龄类的成员变量(属性)
private int age;
//构造方法
public Age(int age) {
this.age=age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return this.age+"";
}
}
/*
* 创建学生类
*/
class Student implements Serializable{
//学生类的成员变量(属性),其中一个属性为类的对象
private String name;
private Age aage;
private int length;
//构造方法,其中一个参数为另一个类的对象
public Student(String name,Age a,int length) {
this.name=name;
this.aage=a;
this.length=length;
}
//eclipe中alt+shift+s自动添加所有的set和get方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Age getaAge() {
return this.aage;
}
public void setaAge(Age age) {
this.aage=age;
}
public int getLength() {
return this.length;
}
public void setLength(int length) {
this.length=length;
}
//设置输出的字符串形式
public String toString() {
return "姓名是: "+this.getName()+", 年龄为: "+this.getaAge().toString()+", 长度是: "+this.getLength();
}
}
结果:
姓名是: 摇头耶稣, 年龄为: 20, 长度是: 175
姓名是: 摇头耶稣, 年龄为: 20, 长度是: 175
姓名是: 大傻子, 年龄为: 99, 长度是: 216
姓名是: 摇头耶稣, 年龄为: 20, 长度是: 175
下一篇:
1-3 IO-BIO和NIO和AIO-Select和epoll多路复用