1.JDK 和 JRE 有什么区别
- JDK:Java Development Kit 的简称,java 开发工具包,提供了 java 的开发环境和运行环境。
- JRE:JavaRuntime Environment 的简称,java 运行环境,为 java 的运行提供了所需环境。
具体来说 JDK 其实包含了 JRE,同时还包含了编译 java 源码的编译器 javac,还包含了很多 java 程序调试和分析的工具。简单来说:如果你需要运行 java 程序,只需安装 JRE 就可以了,如果你需要编写 java 程序,需要安装 JDK。
2.== 和 equals 的区别是什么
- == 解读
对于基本类型和引用类型 == 的作用效果是不同的,如下所示:- 基本类型:比较的是值是否相同;
- 引用类型:比较的是引用是否相同;
String x = "string";
String y = "string";
String z = new String("string");
System.out.println(x==y); // true
System.out.println(x==z); // false
System.out.println(x.equals(y)); // true
System.out.println(x.equals(z)); // true
代码解读:因为 x 和 y 指向的是同一个引用,所以"== "也是 true,而 new String()方法则重写开辟了内存空间,所以 "== "结果为 false,而 equals 比较的一直是值,所以结果都为 true。
- equals 解读
equals 本质上就是 ==,只不过 String 和 Integer 等重写了 equals 方法,把它变成了值比较。看下面的代码就明白了。
首先来看默认情况下 equals 比较一个有相同值的对象,代码如下:
1 class Cat {
2 public Cat(String name) {
3 this.name = name;
4 }
5
6 private String name;
7
8 public String getName() {
9 return name;
10 }
11
12 public void setName(String name) {
13 this.name = name;
14 }
15 }
16
17 Cat c1 = new Cat("王磊");
18 Cat c2 = new Cat("王磊");
19 System.out.println(c1.equals(c2)); // false
输出结果出乎我们的意料,竟然是 false?这是怎么回事,看了 equals 源码就知道了,源码如下:
1 public boolean equals(Object obj) {
2 return (this == obj);
3 }
原来 equals 本质上就是 ==。
那问题来了,两个相同值的 String 对象,为什么返回的是 true?代码如下:
1 String s1 = new String("老王");
2 String s2 = new String("老王");
3 System.out.println(s1.equals(s2)); // true
同样的,当我们进入 String 的 equals 方法,找到了答案,代码如下:
1 public boolean equals(Object anObject) {
2 if (this == anObject) {
3 return true;
4 }
5 if (anObject instanceof String) {
6 String anotherString = (String)anObject;
7 int n = value.length;
8 if (n == anotherString.value.length) {
9 char v1[] = value;
10 char v2[] = anotherString.value;
11 int i = 0;
12 while (n-- != 0) {
13 if (v1[i] != v2[i])
14 return false;
15 i++;
16 }
17 return true;
18 }
19 }
20 return false;
21 }
原来是 String 重写了 Object 的 equals 方法,把引用比较改成了值比较。
总结 :== 对于基本类型来说是值比较,对于引用类型来说是比较的是引用;而 equals 默认情况下是引用比较,只是很多类重写了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。
3.两个对象的 hashCode(),equales的关系
两个对象的 hashCode()相同,equals()不一定 true。
1.若重写了equals(Object obj)方法,则有必要重写hashCode()方法。
2.若两个对象equals(Object obj)返回true,则hashCode()有必要也返回相同的int数。
3.若两个对象equals(Object obj)返回false,则hashCode()不一定返回不同的int数。
4.若两个对象hashCode()返回相同int数,则equals(Object obj)不一定返回true。
5.若两个对象hashCode()返回不同int数,则equals(Object obj)一定返回false。
6.同一对象在执行期间若已经存储在集合中,则不能修改影响hashCode值的相关信息,否则会导致内存泄露问题。
4.final 在 java 中有什么作用
final 修饰的类叫最终类,该类不能被继承。
final 修饰的方法不能被重写。
final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。
5.java 中的 Math.round(-1.5)的用法
Math.round(-1.5) = -1,因为在数轴上取值时,中间值(0.5)向右取整,所以正 0.5 是往上取整,负 0.5 是直接舍弃。
6. java基本数据类型
- 内置数据类型
Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。
名称 | 描述 | 属性 |
---|---|---|
byte | byte 数据类型是8位、有符号的,以二进制补码表示的整数 | 最小值是 -128(-2^7) 最大值是 127(2^7-1) 默认值是 0 |
short | short 数据类型是 16 位、有符号的以二进制补码表示的整数 | 最小值是 -32768(-2^15);最大值是 32767(2^15 - 1);Short 数据类型也可以像 byte 那样节省空间。一个short变量是int型变量所占空间的二分之一;默认值是 0; |
int | int 数据类型是32位、有符号的以二进制补码表示的整数 | 最小值是 -2,147,483,648(-2^31);最大值是 2,147,483,647(2^31 - 1);一般地整型变量默认为 int 类型;默认值是 0 ; |
long | long 数据类型是 64 位、有符号的以二进制补码表示的整数 | 最小值是 -9,223,372,036,854,775,808(-2^63)最大值是 9,223,372,036,854,775,807(2^63 -1)这种类型主要使用在需要比较大整数的系统上 默认值是 0L |
float | float 数据类型是单精度、32位、符合IEEE 754标准的浮点数 | float 在储存大型浮点数组的时候可节省内存空间 默认值是 0.0f;浮点数不能用来表示精确的值,如货币; |
double | double 数据类型是双精度、64 位、符合 IEEE 754 标准的浮点数 | 浮点数的默认类型为 double 类型 double类型同样不能表示精确的值,如货币;默认值是 0.0d |
boolean | boolean数据类型表示一位的信息 | 只有两个取值:true 和 false;这种类型只作为一种标志来记录 true/false 情况;默认值是 false; |
char | char 类型是一个单一的 16 位 Unicode 字符 | 最小值是 \u0000(十进制等效值为 0);最大值是 \uffff(即为 65535);char 数据类型可以储存任何字符; |
- 引用类型
在Java中,引用类型的变量非常类似于C/C++的指针。引用类型指向一个对象,指向对象的变量是引用变量。这些变量在声明时被指定为一个特定的类型,比如 Employee、Puppy 等。变量一旦声明后,类型就不能被改变了。
对象、数组都是引用数据类型。
所有引用类型的默认值都是null
。
一个引用变量可以用来引用任何与之兼容的类型。 - Java 常量
常量在程序运行时是不能被修改的
在 Java 中使用 final 关键字来修饰常量,声明方式和变量类似
7. java 中操作字符串的类
String | StringBuffer | StringBuilder |
---|---|---|
String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且浪费大量的内存空间 | StringBuffer是可变类,线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量 | 可变类,速度更快 |
不可变 | 可变 | 可变 |
线程安全 | 线程不安全 |
8.java中的String类
- charAt(int index)
public char charAt(int index)
charAt() 方法用于返回指定索引处的字符。索引范围为从 0 到 length() - 1
public class Test {
public static void main(String args[]) {
String s = "www.runoob.com";
char result = s.charAt(6);
System.out.println(result);//n
}
}
- compareTo()
字符串与对象进行比较
int compareTo(Object o)
参数 o – 要比较的对象。
按字典顺序比较两个字符串
int compareTo(String anotherString)
参数 anotherString – 要比较的字符串
compareTo(String)方法中其实是从头开始,一个一个字符的比对原字符串和参数字符串中的字符,如果相同就下一个,直到字符出现不同(包括某一个字符串结束了)就计算这两个不同字符的ASCII码的差,作为返回值。(或是直到最后都相同就返回0)
也正是因为这样的计算才使得原字符串更长的时候(假设前几位相同的情况下),返回值会大于零(因为那一位是某个字符的ASCII码减去0,ASCII码都是正数),而原字符串更短的时候,返回值会小于0(那一位是0减去某个字符的ASCII码)
不要忽略了一种情况就是两个字符串虽然长度相等但是中间有字母不同
String str1="abcde";
String str2="abdde";
str1.compareTo(str2);
这时的返回值是-1,即是c的ASCII码(99)减去了d的ASCII码(100)所得
还有一种情况字符串前面的字符相同但字符串长度不同时返回字符串相差位数而并非 ASCII 码差值
String str1 = "abc";
String str2 = "abcdef";
System.out.println(str1.compareTo( str2 ));// -3
- concat()
public String concat(String s)
concat() 方法用于将指定的字符串参数连接到字符串上。
public class Test {
public static void main(String args[]) {
String s = "菜鸟教程:";
s = s.concat("www.runoob.com");
System.out.println(s);
}
}
- copyValueOf
public static String copyValueOf(char[] data): 返回指定数组中表示该字符序列的字符串。
public static String copyValueOf(char[] data, int offset, int count): 返回指定数组中表示该字符序列的 字符串。
参数 data – 字符数组。
参数 offset – 子数组的初始偏移量。
参数 count – 子数组的长度。
public class Test {
public static void main(String args[]) {
char[] Str1 = {'h', 'e', 'l', 'l', 'o', ' ', 'r', 'u', 'n', 'o', 'o', 'b'};
String Str2 = "";
Str2 = Str2.copyValueOf( Str1 );
System.out.println("返回结果:" + Str2);//hello runoob
Str2 = Str2.copyValueOf( Str1, 2, 6 );
System.out.println("返回结果:" + Str2);//llo ru
}
}
9. 接口和抽象类
- 抽象类
抽象类用abstract
来修饰
package com.test.abstractaaa;
public abstract class TestAbstract {
}
抽象类是用来捕捉子类的通用性的,它不能被实例化,只能用作子类的超类,抽象类是被用来创建继承层级里子类的模板
- 接口
接口是抽象方法的集合,如果一个类实现了某个接口,那么它就继承了这个接口的抽象方法,就像契约模式,如果实现了这个接口,那么就必须保证使用这些方法,并且实现这些方法,接口是一种形式,接口自身不能做任何事情,接口里面的方法默认都是
abstract
的,
- 抽象类和接口的比较
参数 | 抽象类 | 接口 |
---|---|---|
默认的方法实现 | 可以有默认的方法实现 | 完全抽象,根本不存在方法的实现(jdk8以后接口中可以有静态的方法,和默认的方法) |
实现方式 | 子类用extends关键字来继承抽象类,如果子类不是抽象类的话,它需要实现父级抽象类中所有抽象方法,父类中非抽象方法可重写也可不重写 | 子类用implements去实现接口,需要实现接口中所有方法 |
构造器 | 抽象类可以有构造器(构造器不能用abstract修饰) | 接口不能有构造器 |
与正常Java类的区别 | 正常Java类可被实例化,抽象类不能被实例化,其他区别见上下文 | 接口和正常java类是不同的类型 |
访问修饰符 | 抽象方法可以用public、protected、default修饰 | 接口默认是public、不能用别的修饰符去修饰 |
main方法 | 抽象类中可以有main方法,可以运行它 | 接口中不能有main方法,因此不能运行它(jdk8以后支持) |
多继承 | 抽象类可继承一个类和实现多个接口 | 接口只能继承一个或者多个接口 |
速度 | 如果在抽象类中添加新非abstract的方法,可以直接添加,因为非abstract方法无需在子类中实现,如果是abstact方法,则需要改变子类的代码,也要实现这个方法 | 只要在接口中添加方法,实现它的类就要改变,去实现这个新添加的方法 |
添加新方法 | 如果往抽象类中添加方法,可以给该方法提供默认的实现,而不需要改变现有的代码逻辑 | 如果往接口中添加方法,必须改动所有实现该接口的类 |
- 设计层面上的区别
抽象类是对一种事务的抽象,是对整个类进行抽象,包括属性,行为(方法)。接口是对行为(行为)的抽象。如果一个类继承或实现了某个抽象类,那么一定是抽象类的种类(拥有同一种属性或行为的类)。
设计层面不同,抽象类作为很多子类的父类,是一种模板设计
,而接口是一种规范
,它是一种辐射式设计,也就是说对于抽象类,如果需要添加新的方法,可以直接在抽象方法中添加实现,子类可以不用变更,而对于接口不行,如果接口进行了变更,那么实现它的类都需要做变更。
- 接口和抽象类分别在什么时候使用
如果拥有一些方法,并想让他们中的一些有默认的具体实现,请选择抽象类
如果想实现多重继承,那么请使用接口,由于java不支持多继承,子类不能继承多个类,但一个类可以实现多个接口,因此可以使用接口来解决。
如果基本功能在不断变化,那么就使用抽象类,如果使用接口,那么每次变更都需要相应的去改变实现该接口的所有类。
- JDK8 中接口的默认方法和静态方法
JDK8中,Oracle引入默认方法和静态方法,用来减少抽象类和接口的差异,可以在接口中提供默认的实现方法并且实现该接口的类不用强制去实现这个方法。JDK8中接口的静态方法只能通过接口名直接去调用,接口中的默认方法因为不是abstract的,所以可重写,也可以不重写。
10.java 中 IO流
11.java 容器
常用容器如下图:
1.Collection 和 Collections 有什么区别
java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set。
Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。
2.Collections中常用的方法
- addAll
public static <T> boolean addAll(Collection<? super T> c,
T... elements)
将所有指定的元素添加到指定的集合。 要添加的元素可以单独指定或作为数组指定。 这种方便方法的行为与c.addAll(Arrays.asList(elements)) 相同 ,但是在大多数实现中,这种方法可能会显着加快。
单独指定元素时,此方法为现有集合添加一些元素提供了一种便捷的方法:
Collections.addAll(flavors, "Peaches 'n Plutonium", "Rocky Racoon");
- copy
public static <T> void copy(List<? super T> dest,
List<? extends T> src)
将所有元素从一个列表复制到另一个列表中。 操作完成后,目标列表中每个复制元素的索引将与源列表中的其索引相同。 目标列表必须至少与源列表一样长。 如果它更长,则目标列表中的剩余元素不受影响。
- emptyList
public static final <T> List<T> emptyList()
返回空列表(immutable)。 此列表是可序列化的。
此示例说明了获取空列表的类型安全方式:
List s = Collections.emptyList();
- emptyMap
public static final <K,V> Map<K,V> emptyMap()
返回空的Map集合(不可变)。 这张地图是可序列化的。
此示例说明了获取空地图的类型安全方式:
Map<String, Date> s = Collections.emptyMap();
参数类型
K - 键的类型
V - 值的类型
- emptySet
public static final <T> Set<T> emptySet()
返回一个空集(immutable)。 这个集是可序列化的。 与类似命名的字段不同,该方法被参数化。
此示例说明了获取空集合的类型安全方式:
Set s = Collections.emptySet();
该方法的实现不需要为每个调用创建单独的Set对象。 使用此方法可能具有与使用相似名称的字段相当的成本。 (与此方法不同,该字段不提供类型安全性。)
参数类型
T - 集合中的对象的类
- fill
public static <T> void fill(List<? super T> list,T obj)
用指定的元素代替指定列表的所有元素。
该方法运行在线性时间。参数类型
T - 列表中对象的类
参数
list - 要填充指定元素的列表。
obj - 用于填充指定列表的元素。
- list
public static <T> ArrayList<T> list(Enumeration<T> e)
用于返回一个数组列表,该列表包含给定Enumeration返回的所有元素,以及将这些元素按枚举返回的顺序存储在ArrayList中的方式。
参数类型
T - T返回的对象的类
参数
e - 枚举为返回的数组列表提供元素
结果
包含指定枚举返回的元素的数组列表。
- max
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)
根据其元素的自然顺序返回给定集合的最大元素。 集合中的所有元素必须实现Comparable接口。 此外,集合中的所有元素必须相互可比较 (即e1.compareTo(e2)不得为集合中的任何元素e1和e2投掷ClassCastException)
参数类型
T - 集合中对象的类
参数
coll - 要确定其最大元素的集合。
结果
根据其元素的 自然排序 ,给定集合的最大元素。
public static <T> T max(Collection<? extends T> coll,
Comparator<? super T> comp)
根据指定的比较器引发的顺序返回给定集合的最大元素。 集合中的所有元素必须由指定的比较器相互比较 (即, comp.compare(e1, e2)不得为集合中的任何元素e1和e2投放ClassCastException)。
参数类型
T - 集合中对象的类
参数
coll - 要确定其最大元素的集合。
comp - 用于确定最大元素的比较器。 null值表示应该使用元素的自然排序 。
结果
给定集合的最大元素,根据指定的比较器。
9. min
public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll)
根据其元素的自然顺序返回给定集合的最小元素。 集合中的所有元素必须实现Comparable接口。 此外,集合中的所有元素必须相互可比较 (即, e1.compareTo(e2)不得为集合中的任何元素e1和e2提供ClassCastException)。
参数类型
T - 集合中对象的类
参数
coll - 要确定其最小元素的集合。
结果
根据其元素的 自然顺序 ,给定集合的最小元素。
public static <T> T min(Collection<? extends T> coll,
Comparator<? super T> comp)
根据指定的比较器引发的顺序返回给定集合的最小元素。 ==集合中的所有元素必须由指定的比较器相互比较 ==(即, comp.compare(e1, e2)不得为集合中的任何元素e1和e2引发ClassCastException)。
参数类型
T - 集合中对象的类
参数
coll - 要确定其最小元素的集合。
comp - 用于确定最小元素的比较器。 甲null值表示元素的自然顺序应该被使用。
结果
根据指定的比较器,给定集合的最小元素。
- 未完,待续。。。
3.List、Set、Map 之间的区别是什么?
List | set | Map | |
---|---|---|---|
继承接口 | Collection | Collection | |
常见实现类 | AbstractList(LinkedList,ArrayList,Vector) | AbstractSet(HashSet,TreeSet,LinkedHashSet) | HashMap,Hashtable |
常见方法 | add(),remove(),clear(),get(),contains(),size() | add(),remove(),clear(),get(),contains(),size() | put(),get(),remove(),clear(),containsKey(),containsValue(),keyset(),value(),size() |
元素是否可重复 | 可重复 | 不可重复 | 键不可重复,值可以重复 |
元素是否有序 | 有序 | 无序(由HashCode决定)LinkedHashSet按照插入排序,SortedSet可排序,HashSet无序 | 无序 |
线程安全 | voctor线程安全 | Hashtable线程安全 |
4.HashMap 和 Hashtable 有什么区别?
- 线程安全性不同
Hashtable是线程安全的,它的每个方法中都加入了Synchronize方法。在多线程并发的环境下,可以直接使用Hashtable,不需要自己为它的方法实现同步
HashMap不是线程安全的,在多线程并发的环境下,可能会产生死锁等问题。
虽然HashMap不是线程安全的,但是它的效率会比Hashtable要好很多。在我们的日常使用当中,大部分时间是单线程操作的。
- 继承的父类不同
HashTable是继承自Dictionary类,而HashMap是继承自AbstractMap类。不过它们都实现了同时实现了map、Cloneable(可复制)、Serializable(可序列化)这三个接口。
- 是否可以储存null
HashTable不允许储存null值(key和value都不可以),HashMap允许使用null值(key和value)都可以
- 遍历方法不同
HashTable使用Enumeration遍历,HashMap使用Iterator进行遍历。
- 初始化和扩容方式不同
Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。
创建时,如果给定了容量初始值,那么Hashtable会直接使用你给定的大小,而HashMap会将其扩充为2的幂次方大小。也就是说Hashtable会尽量使用素数、奇数。而HashMap则总是使用2的幂作为哈希表的大小。
5.如何决定使用 HashMap 还是 TreeMap?
TreeMap<K,V>的Key值是要求实现java.lang.Comparable,所以迭代的时候TreeMap默认是按照Key值升序排序的;TreeMap的实现是基于红黑树结构。适用于按自然顺序或自定义顺序遍历键(key)。
HashMap<K,V>的Key值实现散列hashCode(),分布是散列的、均匀的,不支持排序;数据结构主要是桶(数组),链表或红黑树。适用于在Map中插入、删除和定位元素。
如果你需要得到一个有序的结果时就应该使用TreeMap(因为HashMap中元素的排列顺序是不固定的)。除此之外,由于HashMap有更好的性能,所以大多不需要排序的时候我们会使用HashMap。
6.HashMap 的实现原理
从上图我们可以发现哈希表是由数组+链表组成的,一个长度为16的数组中,每个元素存储的是一个链表的头结点。那么这些元素是按照什么样的规则存储到数组中呢。一般情况是通过hash(key)%len获得,也就是元素的key的哈希值对数组长度取模得到。比如上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12。所以12、28、108以及140都存储在数组下标为12的位置。
HashMap其实也是一个线性的数组实现的,所以可以理解为其存储数据的容器就是一个线性数组。这可能让我们很不解,一个线性的数组怎么实现按键值对来存取数据呢?这里HashMap有做一些处理。
首先HashMap里面实现一个静态内部类Entry,其重要的属性有 key , value, next,从属性key,value我们就能很明显的看出来Entry就是HashMap键值对实现的一个基础bean,我们上面说到HashMap的基础就是一个线性数组,这个数组就是Entry[],Map里面的内容都保存在Entry[]里面。
HashMap的存取实现
// 存储时:
int hash = key.hashCode(); // 这个hashCode方法这里不详述,只要理解每个key的hash是一个固定的int值
int index = hash % Entry[].length;
Entry[index] = value;
// 取值时:
int hash = key.hashCode();
int index = hash % Entry[].length;
return Entry[index];
疑问:如果两个key通过hash%Entry[].length得到的index相同,会不会有覆盖的危险?
这里HashMap里面用到链式数据结构的一个概念。上面我们提到过Entry类里面有一个next属性,作用是指向下一个Entry。打个比方, 第一个键值对A进来,通过计算其key的hash得到的index=0,记做:Entry[0] = A。一会后又进来一个键值对B,通过计算其index也等于0,现在怎么办?HashMap会这样做:B.next = A,Entry[0] = B,如果又进来C,index也等于0,那么C.next = B,Entry[0] = C;这样我们发现index=0的地方其实存取了A,B,C三个键值对,他们通过next这个属性链接在一起。所以疑问不用担心。也就是说数组中存储的是最后插入的元素。
7. HashSet 的实现原理
HashSet底层由HashMap实现
HashSet的值存放于HashMap的key上
HashMap的value统一为PRESENT
8.ArrayList 和 LinkedList 的区别
1.ArrayList是实现了基于动态数组
的数据结构,而LinkedList是基于链表
的数据结构;
2.对于随机访问get和set,ArrayList要优于LinkedList,因为LinkedList要移动指针;
3.当插入的数据量很小时,两者区别不太大,当插入的数据量大时,大约在容量的1/10之前,LinkedList会优于ArrayList,在其后就劣与ArrayList,且越靠近后面越差。一般首选用ArrayList,由于LinkedList可以实现栈、队列以及双端队列等数据结构,所以当特定需要时候,使用LinkedList,当然咯,数据量小的时候,两者差不多,视具体情况去选择使用;当数据量大的时候,如果只需要在靠前的部分插入或删除数据,那也可以选用LinkedList,反之选择ArrayList反而效率更高。
9.实现数组和 List 之间的转换
数组转换成为List:
方式一:
String[] str=new String[] {"hello","world"};
List<String> list3 = Arrays.asList(str);
使用了asList()方法返回的列表的大小是固定的,事实上,返回的列表不是java.util.ArrayList类,而是定义在java.util.Arrays中一个私有静态类java.util.Arrays.ArrayList,ArrayList的实现本质上是一个数组,而asList()返回的列表是由原始数组支持的固定大小的列表,这种情况下,如果添加或删除列表中的元素,程序会抛出异常UnsupportedOperationException
方式二:
使用Collections.addAll()
String[] str=new String[] {"hello","world"};
List<String> list4=new ArrayList<String>(str.length);
Collections.addAll(list4, str);
方式三:
使用Stream中的Collector收集器
String[] str=new String[] {"hello","world"};
List<String> list5=Stream.of(str).collect(Collectors.toList());
List转换成为数组
使用toArray()方法
List<String> list=new ArrayList<String>();
list.add("hello");
list.add("world");
String[] str2=list.toArray(new String[list.size()]);
10.ArrayList 和 Vector 的区别
- ArrayList 是线程不安全的,Vector 是线程安全的
Vector 类的所有方法都是同步
的。可以有两个线程安全的访问一个 Vector 对象,但是一个线程访问 Vector 的话会在同步操作上耗费大量的时间。
ArrayList 是线程不安全
的,所以当我们不需要保证线程安全性的时候推荐使用 ArrayList,如果想要在多线程中使用 ArrayList 可以通过Collections.synchronizedList(new ArrayList())
或new CopyOnWriteArrayList
的方式创建一个线程安全的 ArrayList 集合 - ArrayList 使用默认构造器创建对象时是在调用 add() 方法时对 ArrayList 的默认容量进行初始化的,Vector 在调用构造器时就对容量进行了初始化
- ArrayList 存储数据的 Object 数组使用了 transient 关键字,Vector 的 Object 数组没有
Java 语言的关键字,变量修饰符,如果用 transient 声明一个实例变量,当对象存储时,它的值不需要维持。这里的对象存储是指,Java 的 serialization 提供的一种持久化对象实例的机制。当一个对象被序列化的时候,transient 型变量的值不包括在序列化的表示中,然而非 transient 型的变量是被包括进去的。使用情况是:当持久化对象时,可能有一个特殊的对象数据成员,我们不想用 serialization 机制来保存它。为了在一个特定对象的一个域上关闭 serialization,可以在这个域前加上关键字 transient。
- ArrayList 和 Vector 的扩容机制不同
11.Array 和 ArrayList 的区别
ArrayList可以理解为一种“会自动扩增容量的Array
存储内容比较: Array可以包含基本类型和对象类型,ArrayList只能包含对象类型。
空间大小比较: array 是数组,arraylist 是集合,集合可以根据自身变化扩大,而数组创建后不可以变化。
方法上的比较: ArrayList提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等。
初始化的比较:Array类型的变量在声明的同时必须进行实例化(至少得初始化数组的大小),而ArrayList可以只是先声明。
12.Queue 中几个方法的比较
- offer()和add()的区别
add()和offer()都是向队列中添加一个元素。但是如果想在一个满的队列中加入一个新元素,调用 add() 方法就会抛出一个 unchecked 异常,而调用 offer() 方法会返回 false。可以据此在程序中进行有效的判断! - peek()和element()的区别
peek()和element()都将在不移除
的情况下返回队头,但是peek()方法在队列为空时返回null,调用element()方法会抛出NoSuchElementException异常。 - poll()和remove()的区别
poll()和remove()都将移除
并且返回对头,但是在poll()在队列为空时返回null,而remove()会抛出NoSuchElementException异常。
13.迭代器 Iterator
迭代器模式,它是 Java 中常用的设计模式之一。用于顺序访问集合对象的元素,无需知道集合对象的底层实现。
Iterator 是可以遍历集合的对象,为各种容器提供了公共的操作接口,隔离对容器的遍历操作和底层实现,从而解耦。
缺点:增加新的集合类需要对应增加新的迭代器类,迭代器类与集合类成对增加。
Iterator的使用
- 使用方法iterator()要求容器返回一个Iterator。
注意:iterator()方法是在java.lang.Iterable接口,被Collection继承。 - 使用next()获得序列中的下一个元素。
- 使用hasNext()检查序列中是否还有元素。
- 使用remove()将迭代器新返回的元素删除。
public class TestIterator {
static List<String> list = new ArrayList<String>();
static {
list.add("111");
list.add("222");
list.add("333");
}
public static void main(String[] args) {
testIteratorNext();
System.out.println();
testForEachRemaining();
System.out.println();
testIteratorRemove();
}
//使用 hasNext 和 next遍历
public static void testIteratorNext() {
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String str = iterator.next();
System.out.println(str);
}
}
//使用 Iterator 删除元素
public static void testIteratorRemove() {
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String str = iterator.next();
if ("222".equals(str)) {
iterator.remove();
}
}
System.out.println(list);
}
//使用 forEachRemaining 遍历
public static void testForEachRemaining() {
final Iterator<String> iterator = list.iterator();
iterator.forEachRemaining(new Consumer<String>() {
public void accept(String t) {
System.out.println(t);
}
});
}
}
注意:
- 在迭代过程中调用集合的 remove(Object o) 可能会报java.util.ConcurrentModificationException 异常
- forEachRemaining 方法中 调用Iterator 的 remove 方法会报 java.lang.IllegalStateException 异常
//使用迭代器遍历元素过程中,调用集合的 remove(Object obj) 方法可能会报 java.util.ConcurrentModificationException 异常
public static void testListRevome() {
ArrayList<String> aList = new ArrayList<String>();
aList.add("111");
aList.add("333");
aList.add("222");
System.out.println("移除前:"+aList);
Iterator<String> iterator = aList.iterator();
while(iterator.hasNext())
{
if("222".equals(iterator.next()))
{
aList.remove("222");
}
}
System.out.println("移除后:"+aList);
}
//JDK 1.8 Iterator forEachRemaining 方法中 调用Iterator 的 remove 方法会报 java.lang.IllegalStateException 异常
public static void testForEachRemainingIteRemove () {
final Iterator<String> iterator = list.iterator();
iterator.forEachRemaining(new Consumer<String>() {
public void accept(String t) {
if ("222".equals(t)) {
iterator.remove();
}
}
});
}
14.Iterator 和 ListIterator 的区别
- Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List。
- ListIterator有add()方法,可以向List中添加对象,而Iterator不能。
- ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历。但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator就不可以。也就是说Iterator是单向的,而ListIterator是双向的。ListIterator继承自Iterator。
- ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现。Iterator 没有此功能。
- 都可实现删除对象,但是ListIterator可以实现对象的修改,set()方法可以实现。Iterator仅能遍历,不能修改。因为ListIterator的这些功能,可以实现对LinkedList等List数据结构的操作。