容器
- 容器
- 1.Collection和Collections的区别?
- 2.List、Set、Map之间的区别是什么?
- 3.HashMap和HashTable的区别?
- 4.如何决定使用 HashMap 还是 TreeMap?
- 5.谈谈HashMap的实现原理?
- 6.说下HashSet的实现原理?
- 7.ArrayList和LinkedList的区别是什么?
- 8.如何实现数组和List之间的转换?
- 9.ArrayList和Vector的区别是什么?
- 10.Array和ArrayList的区别?
- 11.在 Queue 中 poll()和 remove()有什么区别?
- 12.哪些集合类是线程安全的?
- 13.迭代器Iterator是什么?
- 14.Iterator怎么使用?有什么特点?
- 15.Iterator和ListIterator的区别?
- 16.ArrayList循环遍历并删除元素,怎么保证去重?
- 17.HashSet去重原理?如何将自定义类对象存入HashSet并进行去重呢?
容器
常见的容器图录:
1.Collection和Collections的区别?
- Collection: java.util.Collection是集合类的一个顶级接口,为各种具体的集合提供了最大化的统一操作方式。
- Collections: 是集合类的一个工具类,它提供了一系列静态法,用于对集合中元素进行排序,搜索等各种操作。
2.List、Set、Map之间的区别是什么?
3.HashMap和HashTable的区别?
- 方法上: HashMap去掉了HashTable的contains方法,但是加上了containsValue() 和containsKey() 方法。
- 性能上: HashTable 是线程安全的,而HashMap是非线程安全的,所以在单线程的环境下HashMap性能上要好一些。
- 空键值: HashMap允许空键值(null键null值),而HashTable不允许。
- 遍历方法: HashTable使用Enumeration遍历,HashMap使用Iterator进行遍历。
4.如何决定使用 HashMap 还是 TreeMap?
- 对于在Map中插入、删除和定位元素这类操作,HashMap是更佳的选择。
- 如果你需要对一个有序的Key集合进行遍历,TreeMap是更好的选择。
5.谈谈HashMap的实现原理?
- 概述: HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null键和null值。此类不保证映射的顺序,特别是它不保证该顺序永恒不变。
- 数据结构: 在JAVA编程语言中,最基本的结构就是两种:数组和模拟指针(引用)。HashMap也不例外。HashMap实际上就是一个“链表散列”的数据结构,即链表和数组的结合体。
- 往HashMap中put元素时,首先会根据key的hashcode重新计算hash值,根据hash值得到这个元素在数组中的位置(下标),如果该数组在该位置上已经存放了其他元素,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,原来的放在链尾。如果数组中该位置没有元素,就直接将该元素存放到数组该位置上。
- 需要注意Jdk 1.8中对HashMap的实现做了优化,当链表中的节点数据超过八个之后,该链表会转为红黑树来提高查询效率,从原来的O(n)到O(logn)。
6.说下HashSet的实现原理?
- HashSet底层由HashMap实现。
- HashSet的值存放在HashMap的key上,而value中的值都是统一的一个private static final Object PRESENT = new Object();。
- HashSet跟HashMap一样,都是一个存放链表的数组。
7.ArrayList和LinkedList的区别是什么?
- 数据结构: ArrayList的底层数据结构是数组(支持随机访问),而LinkedList的底层数据结构是双向循环链表(不支持随机访问)。
- 时间复杂度: ArrayList的时间复杂度是O(1),LinkedList的时间复杂度是O(n)。
8.如何实现数组和List之间的转换?
- List转换为数组:ArrayList的toArray方法。
- 数组转换为List:Arrays的asList方法。
9.ArrayList和Vector的区别是什么?
- 线程安全: Vector是同步的,所以是线程安全的。ArrayList是不同步的,所以是非线程安全的。
- 速度: 单线程环境下,ArrayList的速度要更快一些。
- 扩容: 当容量超过初始值时,ArrayList扩容原有初始容量的50%,而Vector扩容原有初始容量的100%,他们的初始容量都是10。
10.Array和ArrayList的区别?
- 容纳类型: 数组可以容纳基本数据类型和对象,而ArrayList只能容纳对象。
- 大小 : 数组是指定大小的。而ArrayList大小默认是10,且支持自动扩容。
- 功能: ArrayList的方法要多于数组。
11.在 Queue 中 poll()和 remove()有什么区别?
- poll() 和 remove() 都是从队列中取出一个元素,但是 poll() 在获取元素失败的时候会返回空,但是 remove() 失败的时候会抛出异常。
12.哪些集合类是线程安全的?
- vector:是同步的。
- statck:堆栈类,先进后出。
- hashtable:是同步的。
- enumeration:枚举,相当于迭代器。
13.迭代器Iterator是什么?
- 迭代器是一种设计模式,是一个对象,用于遍历和选择集合中的元素。迭代器通常被称为“轻量级对象”,因为创建它的代价很小。
14.Iterator怎么使用?有什么特点?
- 特点: Iterator功能比较简单,只能单向移动,是Java迭代器最简单的实现
- 1.调用iterator()方法返回一个Iterator
- 2.调用next() 方法获取集合中下一个元素。(第一次调用时,返回的是集合的第一个元素)。
- 3.调用hasNext() 方法检查集合中是否还有元素。
- 4.调用remove() 将迭代器新返回的元素删除。
15.Iterator和ListIterator的区别?
- 遍历对象: Iterator可以用来遍历Set和List集合,而ListIterator只能遍历List。
- 遍历方向: Iterator对集合只能是前向遍历,而ListIterator既可以前向也可以后向。
- 功能: ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。
16.ArrayList循环遍历并删除元素,怎么保证去重?
- 1.倒序删除的方式避免删除元素后带来的集合元素移动的问题。
- 2.使用迭代器且使用迭代器的remove方法。
- 3.正序删除且每删掉一个元素,遍历的下标减1,往回移动一个位置
class T1{
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"a","b","c","d","b","b");
System.out.println("去重前list = " + list);
remove(list);//和remove3(list)的结果一样
/*输出: 去重前list = [a, b, c, d, b, b]
去重后list = [a, b, c, d] */
//remove2(list);
/*输出:去重前list = [a, b, c, d, b, b]
去重后list = [a, c, d, b] */
System.out.println("去重后list = " + list);
}
//1.迭代器删除
public static void remove(ArrayList<String> list)
{
Iterator<String> it = list.iterator();
int count = 0;//统计遇见"b"的次数
while (it.hasNext())
{
String s = it.next();
if (s.equals("b"))
{
if (count!=0){
it.remove();
}
count++;//进来一次,计数一次
}
}
}
//2.倒序删除
public static void remove2(ArrayList<String> list)
{
int count=0;//统计遇见"b"的次数
for(int i=list.size()-1;i>=0;i--)
{
String s = list.get(i);
if(s.equals("b"))
{
if(count!=0){
list.remove(i);
}
count++;//进来一次,计数一次
}
}
}
//3.每删掉一个,遍历的下标减1.往回移动一个位置
public static void remove3(ArrayList<String> list){
int count = 0;
for (int i = 0; i <=list.size()-1 ; i++) {
String s = list.get(i);
if ("b".equals(s)){
if (count!=0) {
list.remove(i);
i--;
}
count++;
}
}
}
}
17.HashSet去重原理?如何将自定义类对象存入HashSet并进行去重呢?
- 原理:依赖hashCode()方法和equals()方法。
- HashSet的add()方法实际上调用的是HashMap的put方法,把要添加进HashSet的元素当做key存入,而value则一个固定值:一个Object类对象。
- 先用hashCode方法获得传入元素的哈希值,在集合中查找是否包含相同哈希值的元素,如果哈希值相同,则继续执行equals方法,返回true不添加,返回false就添加。哈希值不同直接添加。
- 自定义类对象添加HashSet进行去重:
- 1.类中重写hashCode方法和equals方法
- 2.equals方法中比较所有属性值
- 3.hashCode()方法要保证属性相同的对象返回值相同:对象的成员变量值相同即为同一个元素。
class Student {
private String name;
private int age;
public Student(String name,int age){
this.name = name;
this.age = age;
}
public static void main(String[] args) {
HashSet set = new HashSet();
/* Student s1 =new Student("zs",12);
Student s2 =new Student("ww",12);
Class<? extends Student> aClass = s1.getClass();
Class<? extends Student> aClass1 = s2.getClass();
System.out.println(aClass==aClass1);//true */
System.out.println("---------------");
set.add(new Student("zs",12));
set.add(new Student("ls",13));
set.add(new Student("ww",14));
set.add(new Student("zs",12));
set.add(null);
Iterator it = set.iterator();
while (it.hasNext()){
System.out.println(it.next());
/*输出结果:---------------
null
Student{name='zs', age=12}
Student{name='ls', age=13}
Student{name='ww', age=14} */
}
}
@Override
public boolean equals(Object o) {
//如果内存地址值相同直接返回true
if (this == o) return true;
//如果传入对象为null或者类不相同直接返回false
if (o == null || getClass() != o.getClass()) return false;
//进行到这一步,则证明是同一个类,直接向下转型为Student
Student student = (Student) o;
//并且比较两个对象的属性值是否相等,相等的话就返回true,实现去重
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}