题目:
1.三种集合差别,集合类都是什么,数据结构是什么,都什么时候用
2.Map集合的主要接口:Map 、Map.Entry 、 AbstractMmap、SortedMap
3.hashset为什么要重写hashcode和equals方法?
4.treeset两种实现方式,在哪用到过,
5.红黑树原理
6.ArrayList和LinkList各自什么时候用
7.Map两种解析方式,并举例框架中的源码使用的源代码段,至少两段
8.迭代器模式,iterator在ArrayList和hashset中实现的源码段,说出设计模式及模式使用的场合
9.Collections和Collection的差别
写出Collections五个常用方法和demo
数组和List相互转换的方法及demo
10.说明在遍历数据库结果集时如果类似写法会出现什么问题,简单画出内存图
ArrayList<user> al = new ArrayList<user>();
User user =new User();
for()
{
user.setName();
.....
......
al.add(user);
}
11.线程安全的集合有哪些,线程安全的list的特点
12.泛型如何提供安全监测机制(编译期),什么是类型擦除(删除)
1.三种集合差别,集合类都是什么,数据结构是什么,都什么时候用
1. List
List接口表示一个有序的集合,允许重复元素。每个元素都有一个索引,可以根据索引来访问元素。
常见实现类:ArrayList,LinkedList,Vector
底层数据结构:ArrayList:动态数组。
LinkedList:双向链表。
Vector:动态数组(线程安全)。
使用场景:
ArrayList:需要快速随机访问元素,插入和删除操作不频繁的场景。
List<String> arrayList = new ArrayList<>();
arrayList.add("A");
arrayList.add("B");
System.out.println(arrayList.get(0)); // 输出 "A"
LinkedList:需要频繁插入和删除操作,且不需要快速随机访问的场景。
List<String> linkedList = new LinkedList<>();
linkedList.add("A");
linkedList.add("B");
linkedList.remove(0);
Vector:与ArrayList相似,但需要线程安全的场景(不推荐使用,通常使用Collections.synchronizedList来替代)。
List<String> vector = new Vector<>();
vector.add("A");
vector.add("B");
2. Set
Set接口表示一个无序的集合,不允许重复元素。
常见实现类:HashSet,LinkedHashSet,TreeSet
底层数据结构:HashSet:哈希表。
LinkedHashSet:哈希表和链表(维持插入顺序)。
TreeSet:红黑树(有序集合)。
使用场景:
HashSet:需要快速判断元素是否存在,不关心元素顺序的场景。
Set<String> hashSet = new HashSet<>();
hashSet.add("A");
hashSet.add("B");
LinkedHashSet:需要快速判断元素是否存在,同时需要维持插入顺序的场景。
Set<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("A");
linkedHashSet.add("B");
TreeSet:需要有序的集合,按自然顺序或自定义比较器排序的场景。
Set<String> treeSet = new TreeSet<>();
treeSet.add("A");
treeSet.add("B");
3. Map
Map接口表示一个键值对的集合,每个键映射到一个值。键不能重复,但值可以重复。
常见实现类:HashMap,LinkedHashMap,TreeMap,Hashtable
底层数据结构:HashMap:哈希表。
LinkedHashMap:哈希表和链表(维持插入顺序)。
TreeMap:红黑树(按键的自然顺序或自定义比较器排序)。
Hashtable:哈希表(线程安全)。
使用场景:
HashMap:需要根据键快速查找值,不关心键的顺序的场景。
Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("A", 1);
hashMap.put("B", 2);
LinkedHashMap:需要根据键快速查找值,同时需要维持键的插入顺序的场景。
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("A", 1);
linkedHashMap.put("B", 2);
TreeMap:需要根据键快速查找值,同时需要键按自然顺序或自定义比较器排序的场景。
Map<String, Integer> treeMap = new TreeMap<>();
treeMap.put("A", 1);
treeMap.put("B", 2);
Hashtable:与HashMap相似,但需要线程安全的场景(不推荐使用,通常使用Collections.synchronizedMap来替代)。
Map<String, Integer> hashtable = new Hashtable<>();
hashtable.put("A", 1);
hashtable.put("B", 2);
2.Map集合的主要接口:Map 、Map.Entry 、 AbstractMmap、SortedMap
1. Map
Map接口表示一个键值对的集合,每个键映射到一个值。键不能重复,但值可以重复。
2. Map.Entry
Map.Entry是一个内部接口,用于表示Map中的一个键值对。Map的entrySet方法返回一个包含所有映射关系的集合,每个映射关系都是一个Map.Entry对象。
常用方法:
getKey()
: 返回与此条目对应的键。getValue()
: 返回与此条目对应的值。setValue(V value)
: 用指定值替换与此条目对应的值。
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
}
3. AbstractMap
AbstractMap是一个实现了Map接口的大部分功能的抽象类,提供了Map接口的骨干实现,以减少实现此接口所需的工作量。具体的Map实现类可以继承AbstractMap并实现其抽象方法。
public class CustomMap<K, V> extends AbstractMap<K, V> {
private final Map<K, V> map = new HashMap<>();
@Override
public Set<Entry<K, V>> entrySet() {
return map.entrySet();
}
@Override
public V put(K key, V value) {
return map.put(key, value);
}
}
4. SortedMap
SortedMap接口继承自Map接口,表示一个按键进行排序的Map。SortedMap中的所有键都必须实现Comparable接口,或者必须提供一个Comparator。键按其自然顺序排序,或者按映射创建时提供的Comparator排序。
常用方法:
comparator()
: 返回对此映射中的键进行排序的比较器,如果此映射使用其键的自然顺序,则返回null
。firstKey()
: 返回此映射中当前第一个(最低)键。lastKey()
: 返回此映射中当前最后一个(最高)键。headMap(K toKey)
: 返回此映射的部分视图,其键严格小于toKey
。tailMap(K fromKey)
: 返回此映射的部分视图,其键大于等于fromKey
。subMap(K fromKey, K toKey)
: 返回此映射的部分视图,其键范围从fromKey
(含)到toKey
(不含)。
SortedMap<String, Integer> sortedMap = new TreeMap<>();
sortedMap.put("C", 3);
sortedMap.put("A", 1);
sortedMap.put("B", 2);
System.out.println(sortedMap.firstKey()); // 输出 "A"
System.out.println(sortedMap.lastKey()); // 输出 "C"
System.out.println(sortedMap.subMap("A", "C")); // 输出 {A=1, B=2}
3.hashset为什么要重写hashcode和equals方法?
在Java中,HashSet的工作原理依赖于哈希表的实现,而哈希表的运作需要依靠对象的hashCode和equals方法。为了正确地存储和检索对象,必须重写这些方法。这是因为HashSet需要保证集合中的元素唯一性,并高效地进行插入、查找和删除操作。
hashCode方法
hashCode方法返回一个整数,该整数用于在哈希表中确定对象的存储位置。HashSet使用哈希值来快速查找对象。因此:如果两个对象被认为是相等的(通过equals方法),它们必须具有相同的哈希码。这是为了确保在比较对象时,如果它们是相等的,就会落在同一个“桶”(bucket)中,从而可以找到和检索相同的对象。
如果对象的hashCode实现不一致(即相等的对象有不同的哈希码),那么在插入和查找时会出现问题,可能导致HashSet无法正确存储和检索对象。
equals方法
equals方法用于确定两个对象是否相等。在HashSet中,这用于确定是否已经存在与要插入的新对象相等的对象。因此:当插入一个新对象时,HashSet首先使用hashCode方法确定对象应该存储的位置。如果在该位置上已经存在其他对象,HashSet会使用equals方法逐一比较这些对象,以确定是否已经存在一个与新对象相等的对象。
如果没有重写equals方法,HashSet将使用Object类的默认实现,这通常是基于对象的引用(即对象的内存地址)。这意味着除非两个引用指向同一个对象,否则equals方法将返回false,即使这两个对象的内容是相同的。
4.treeset两种实现方式,在哪用到过
1. 自然排序(Natural Ordering)
自然排序是通过元素自身的Comparable
接口实现的排序。集合中的元素必须实现Comparable
接口,并重写compareTo
方法,以定义元素的自然顺序。
package code711;
import java.util.TreeSet;
public class code12 {
public static void main(String[] args) {
TreeSet<Integer> treeSet = new TreeSet<>();
treeSet.add(5);
treeSet.add(1);
treeSet.add(3);
treeSet.add(2);
treeSet.add(4);
System.out.println("TreeSet with natural ordering: " + treeSet);
}
}
在这个例子中,TreeSet
使用了Integer
类的自然顺序(从小到大)来排序元素。
如果我们有一个自定义类,例如Person
,该类需要实现Comparable
接口:
package code711;
import java.util.TreeSet;
public class code13 {
public static void main(String[] args) {
TreeSet<Person> treeSet = new TreeSet<>();
treeSet.add(new Person("Alice", 30));
treeSet.add(new Person("Bob", 25));
treeSet.add(new Person("Charlie", 35));
System.out.println("TreeSet with natural ordering: " + treeSet);
}
}
class Person implements Comparable<Person> {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person other) {
return this.age - other.age; // 根据年龄进行比较
}
@Override
public String toString() {
return name + " (" + age + ")";
}
}
2. 自定义排序(Custom Ordering)
自定义排序是通过传递一个实现了Comparator
接口的比较器对象来实现的。Comparator
接口提供了一个compare
方法,用于定义两个对象的比较逻辑。
package code711;
import java.util.TreeSet;
import java.util.Comparator;
public class code14 {
public static void main(String[] args) {
TreeSet<String> treeSet = new TreeSet<>(new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s2.compareTo(s1); // 按照字符串的降序排列
}
});
treeSet.add("Apple");
treeSet.add("Banana");
treeSet.add("Cherry");
treeSet.add("Date");
treeSet.add("Elderberry");
System.out.println("TreeSet with custom ordering: " + treeSet);
}
}
在这个例子中,我们使用一个自定义的Comparator
来按照字符串的降序排列TreeSet
中的元素。
对于自定义类,例如Person
,可以定义一个自定义的比较器来按姓名排序:
package code711;
import java.util.Comparator;
import java.util.TreeSet;
public class code15 {
public static void main(String[] args) {
TreeSet<Person> treeSet = new TreeSet<>(new Comparator<Person>() {
@Override
public int compare(Person p1, Person p2) {
return p1.name.compareTo(p2.name); // 根据姓名进行比较
}
});
treeSet.add(new Person("Alice", 30));
treeSet.add(new Person("Bob", 25));
treeSet.add(new Person("Charlie", 35));
System.out.println("TreeSet with custom ordering: " + treeSet);
}
}
TreeSet
的这两种实现方式广泛应用于需要排序的场景中,包括但不限于以下场景:
- 数据处理和分析:例如在处理数据时,需要对数据进行排序,以便于查找、过滤和统计。
- 报表生成:生成有序的报表或日志,确保数据按特定顺序排列。
- 搜索和查找:在实现自定义搜索算法时,可以利用
TreeSet
的排序特性进行高效查找。 - 排队系统:在需要维护有序队列的系统中,例如优先级队列、事件调度系统等。
通过自然排序和自定义排序,TreeSet
提供了灵活的方式来管理和处理有序数据。
5.红黑树原理
这个实在内容太多,已经理解,有空会写一个代码和博客。
6.ArrayList和LinkList各自什么时候用
-
ArrayList:
- 内部实现:基于动态数组实现。
- 优势:
- 随机访问速度快,时间复杂度为 O(1)。
- 适合对元素的访问操作比较频繁的场景。
- 劣势:
- 在插入和删除操作时,如果需要频繁地在中间或开头插入或删除元素,需要移动大量元素,效率较低(时间复杂度为 O(n))。
适用场景:
- 当需要快速访问元素,而对插入和删除操作的效率要求不是特别高时,可以选择使用
ArrayList
。
-
LinkedList:
- 内部实现:基于双向链表实现。
- 优势:
- 在插入和删除操作时,不需要像
ArrayList
那样移动大量元素,时间复杂度为 O(1)。 - 对于频繁的插入和删除操作,
LinkedList
的效率要高于ArrayList
。
- 在插入和删除操作时,不需要像
- 劣势:
- 随机访问元素时效率较低,需要 O(n) 的时间复杂度来获取第 n 个元素。
- 消耗的空间相对
ArrayList
较大,因为需要存储额外的链表节点。
适用场景:
- 当需要频繁进行插入和删除操作,而对访问操作要求不是特别高时,可以选择使用
LinkedList
。
7.Map两种解析方式,并举例框架中的源码使用的源代码段,至少两段
1. 通过键集(keySet)解析
通过键集解析Map的方式是首先获取Map中所有键的集合,然后遍历这个集合,对于集合中的每个键,使用Map的get方法获取对应的值。
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapExample {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("one", 1);
map.put("two", 2);
map.put("three", 3);
// 通过键集解析
Set<String> keys = map.keySet();
for (String key : keys) {
Integer value = map.get(key);
System.out.println("Key = " + key + ", Value = " + value);
}
}
}
2. 通过键值对集(entrySet)解析
通过键值对集解析Map的方式是首先获取Map中所有键值对的集合(entrySet),然后遍历这个集合,对于集合中的每个键值对对象,可以直接获取键和值。
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
public class MapExampleEntrySet {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("one", 1);
map.put("two", 2);
map.put("three", 3);
// 通过键值对集解析
Set<Entry<String, Integer>> entries = map.entrySet();
for (Entry<String, Integer> entry : entries) {
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println("Key = " + key + ", Value = " + value);
}
}
}
总结
- keySet解析:适用于仅需要键的场景,或者当你想对键进行进一步操作时使用。
- entrySet解析:适用于同时需要键和值的场景,因为它直接提供了键值对对象。
8.迭代器模式,iterator在ArrayList和hashset中实现的源码段,说出设计模式及模式使用的场合
迭代器模式(Iterator Pattern)是一种行为型设计模式,它提供了一种方法顺序访问一个聚合对象(如列表、集合等)中的各个元素,而又不暴露该对象的内部表示。迭代器模式使得在不暴露对象内部结构的情况下,可以遍历集合对象中的元素。
设计模式
迭代器模式 允许用户通过一种统一的方式来遍历访问集合中的元素,而无需关心集合的内部表示,或者集合是如何被构建的。这种方式简化了集合的遍历过程,并且使得集合类的接口和迭代器类的接口之间解耦。
使用场合
迭代器模式通常用于以下场合:
- 遍历集合:当需要遍历访问一个集合对象中的元素时,但又不想暴露该对象的内部表示时。
- 统一接口:当需要为不同的集合提供统一的遍历方式时。
- 多态性:当需要实现多态的集合操作时,比如对不同的集合类(如列表、队列、栈等)进行遍历。
9.Collections和Collection的差别
写出Collections五个常用方法和demo
数组和List相互转换的方法及demo
Collections和Collection的差别
-
Collection 是一个接口,它是Java集合框架的一部分,定义了集合的基本操作,如添加、删除、清空集合等。它不能直接被实例化,只能被其实现类(如
ArrayList
、LinkedList
、HashSet
等)实例化。 -
Collections 是一个工具类,提供了操作或返回集合的静态方法,如排序、搜索、替换、反转等。这个类不能被实例化,因为它所有的方法都是静态的。
package code711;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class code16 {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(5, 1, 4, 2, 8);
Collections.sort(numbers);
System.out.println(numbers); // 输出: [1, 2, 4, 5, 8]
List<String> words = Arrays.asList("apple", "banana", "cherry");
Collections.reverse(words);
System.out.println(words); // 输出: [cherry, banana, apple]
Collections.shuffle(numbers);
System.out.println(numbers); // 输出可能会不同,例如: [4, 5, 2, 1, 3]
Integer max = Collections.max(numbers);
System.out.println(max); // 输出: 8
List<String> list = new ArrayList<>();
Collections.addAll(list, "apple", "banana", "cherry");
System.out.println(list); // 输出: [apple, banana, cherry]
}
}
10.说明在遍历数据库结果集时如果类似写法会出现什么问题,简单画出内存图
ArrayList<user> al = new ArrayList<user>();
User user =new User();
for()
{
user.setName();
.....
......
al.add(user);
}
只会有一个user对象在ArraysList中,然后循环一次就在堆区中改数据。
11.线程安全的集合有哪些,线程安全的list的特点
在Java中,有多种线程安全的集合类,它们通过内部同步机制确保在多线程环境下的数据一致性。以下是一些常见的线程安全集合:
Vector
:这是最早的动态数组实现,其中的方法大多是同步的。但是,由于其性能较低(每次方法调用都进行同步),现在较少使用。Hashtable
:类似于HashMap
,但它是线程安全的。同样,由于其性能问题,现在推荐使用ConcurrentHashMap
。Collections.synchronizedList(List<T> list)
:可以将任何List
转换为线程安全的列表。这个方法通过包装原始列表来提供同步访问。CopyOnWriteArrayList
:这是一个线程安全的ArrayList
变体,它通过在每次修改时复制底层数组来避免并发修改异常。它适用于读多写少的场景。ConcurrentHashMap
:虽然不是List
,但它是Map
接口的一个线程安全且高效的实现,通常用于替代Hashtable
。ConcurrentSkipListMap
、ConcurrentSkipListSet
:这些是线程安全的可排序映射和集合,基于跳表实现。
线程安全的List的特点
- 同步性:线程安全的List通过内部同步机制(如锁)来确保在多线程环境下对列表的访问是互斥的,从而避免数据不一致。
- 性能考虑:虽然线程安全的List提供了线程安全性,但它们往往比非线程安全的集合(如
ArrayList
)在性能上有所牺牲,特别是在高并发环境下。 - 适用场景:线程安全的List适用于需要保证数据一致性的多线程环境。但是,在选择时需要考虑性能和适用场景,比如
CopyOnWriteArrayList
适用于读多写少的场景。
12.泛型如何提供安全监测机制(编译期),什么是类型擦除(删除)
泛型提供的安全监测机制(编译期)
Java泛型在编译期提供了类型安全监测机制,这意味着编译器会检查代码中对泛型的使用是否符合类型约束。例如,如果你有一个List<String>
,编译器会确保你只能向这个列表中添加String
类型的对象,而不能添加其他类型的对象。这种检查是在编译时进行的,有助于在代码运行之前发现并修正类型错误。
类型擦除(删除)
Java的泛型是通过类型擦除来实现的。类型擦除指的是在编译期间,Java编译器会将泛型代码中的类型参数替换为它们的限定类型(通常是Object),然后生成普通的字节码。这个过程称为类型擦除。例如,List<String>
在编译后会被擦除为List<Object>
(实际上是List
,因为原始类型在Java中没有泛型信息)。但是,请注意,这并不意味着你可以向List<String>
中添加任意类型的对象,因为Java虚拟机(JVM)在运行时会通过强制类型转换和类型检查来维护泛型约束的安全性。
类型擦除的主要目的是为了保持与Java 1.5之前版本的兼容性,因为JVM本身在引入泛型之前并不支持泛型。类型擦除也意呀着泛型信息不会保留在运行时,因此你不能在运行时检查一个集合是否包含特定类型的元素(除了通过反射和类型转换时的异常)。但是,Java提供了Class<T>
和其他泛型类型信息(如TypeToken
在Google Guava库中)来帮助处理与泛型相关的运行时问题。