LinkedList是一个实现了List接口和Deque接口的双端链表。 LinkedList底层的链表结构使它支持高效的插入和删除操作,另外它实现了Deque接口,使得LinkedList类也具有队列的特性; LinkedList不是线程安全的,如果想使LinkedList变成线程安全的,可以调用静态类Collections类中的synchronizedList方法:
List list=Collections.synchronizedList(new LinkedList(...));
import java.util.Iterator;
import java.util.LinkedList;
public class test2 {
public static void main(String[] args) {
LinkedList<Integer>linkedList=new LinkedList<Integer>();
linkedList.addFirst(0);
linkedList.add(1);
linkedList.add(2,2);
linkedList.addLast(5);
/************************** Search操作 ************************/
System.out.println("直接输出:"+linkedList);
System.out.println("getFirst()获得第一个元素:"+ linkedList.getFirst());
System.out.println("removeFirst()删除第一个元素并返回: " + linkedList.removeFirst()); // 移除并返回此列表的第一个元素
System.out.println("After remove:" + linkedList);
System.out.println("contains判断是否存在"+linkedList.contains(1));
System.out.println("该linkedlist的大小"+linkedList.size());
System.out.println("-----------------");
linkedList.set(1,3);
System.out.println("After set(1,3):"+linkedList);
System.out.println("get(1)要获得指定位置的元素"+linkedList.get(1));
System.out.println("-------------------");
linkedList.add(3);
System.out.println("indexof(3):"+linkedList.indexOf(3));
//返回第一个3的索引
System.out.println("lastindexof(3)"+linkedList.lastIndexOf(3));
/************************** Queue操作 ************************/
System.out.println("-----------------------------------------");
System.out.println("获取但不移除列表的头:"+linkedList.peek());
System.out.println("element():"+linkedList.element());
linkedList.poll(); // 获取并移除此列表的头
System.out.println("After poll():" + linkedList);
linkedList.remove();
System.out.println("After remove():" + linkedList); // 获取并移除此列表的头
linkedList.offer(4);
System.out.println("After offer(4):" + linkedList); // 将指定元素添加到此列表的末尾
/************************** Deque操作 ************************/
System.out.println("-----------------------------------------");
linkedList.offerFirst(2); // 在此列表的开头插入指定的元素
System.out.println("After offerFirst(2):" + linkedList);
linkedList.offerLast(5); // 在此列表末尾插入指定的元素
System.out.println("After offerLast(5):" + linkedList);
System.out.println("peekFirst(): " + linkedList.peekFirst()); // 获取但不移除此列表的第一个元素
System.out.println("peekLast(): " + linkedList.peekLast()); // 获取但不移除此列表的第一个元素
linkedList.pollFirst(); // 获取并移除此列表的第一个元素
System.out.println("After pollFirst():" + linkedList);
linkedList.pollLast(); // 获取并移除此列表的最后一个元素
System.out.println("After pollLast():" + linkedList);
linkedList.push(2); // 将元素推入此列表所表示的堆栈(插入到列表的头)
System.out.println("After push(2):" + linkedList);
linkedList.pop(); // 从此列表所表示的堆栈处弹出一个元素(获取并移除列表第一个元素)
System.out.println("After pop():" + linkedList);
linkedList.add(3);
linkedList.removeFirstOccurrence(3); // 从此列表中移除第一次出现的指定元素(从头部到尾部遍历列表)
System.out.println("After removeFirstOccurrence(3):" + linkedList);
linkedList.removeLastOccurrence(3); // 从此列表中移除最后一次出现的指定元素(从头部到尾部遍历列表)
System.out.println("After removeFirstOccurrence(3):" + linkedList);
/************************** 遍历操作 ************************/
System.out.println("-----------------------------------------");
linkedList.clear();
System.out.println(linkedList);
for (int i=0;i<100000;i++){
linkedList.add(i);
}
//迭代器遍历
long start = System.currentTimeMillis();
System.out.println(start);
Iterator<Integer>iterator=linkedList.iterator();
while (iterator.hasNext()){
iterator.next();
}
long end=System.currentTimeMillis();
System.out.println("iterator时间差:"+(end-start)+"ms");
//顺序遍历(随即遍历)
start=System.currentTimeMillis();
for (int i=0;i<linkedList.size();i++){
linkedList.get(i);
}
end=System.currentTimeMillis();
System.out.println("for循环时间差:"+(end-start)+"ms");
// 另一种for循环遍历
start = System.currentTimeMillis();
for (Integer i : linkedList)
;
end = System.currentTimeMillis();
System.out.println("for2:" + (end - start) + " ms");
//通过pollFirst或者pollLast来遍历LinkedList
LinkedList<Integer>temp1=new LinkedList<>();
temp1.addAll(linkedList);
start=System.currentTimeMillis();
while (temp1.size()!=0){
temp1.pollFirst();
}
end = System.currentTimeMillis();
System.out.println("pollFirst()或pollLast():" + (end - start) + " ms");
// 通过removeFirst()或removeLast()来遍历LinkedList
LinkedList<Integer> temp2 = new LinkedList<>();
temp2.addAll(linkedList);
start = System.currentTimeMillis();
while (temp2.size() != 0) {
temp2.removeFirst();
}
end = System.currentTimeMillis();
System.out.println("removeFirst()或removeLast():" + (end - start) + " ms");
}
}
java面经
Java 集合类
类的属性
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
// 序列号
private static final long serialVersionUID = 362498820763181265L;
// 默认的初始容量是16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
// 最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
// 默认的填充因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
// 当桶(bucket)上的结点数大于这个值时会转成红黑树
static final int TREEIFY_THRESHOLD = 8;
// 当桶(bucket)上的结点数小于这个值时树转链表
static final int UNTREEIFY_THRESHOLD = 6;
// 桶中结构转化为红黑树对应的table的最小大小
static final int MIN_TREEIFY_CAPACITY = 64;
// 存储元素的数组,总是2的幂次倍
transient Node<k,v>[] table;
// 存放具体元素的集
transient Set<map.entry<k,v>> entrySet;
// 存放元素的个数,注意这个不等于数组的长度。
transient int size;
// 每次扩容和更改map结构的计数器
transient int modCount;
// 临界值 当实际大小(容量*填充因子)超过临界值时,会进行扩容
int threshold;
// 加载因子
final float loadFactor;
}
介绍下 HashMap 的原理,源码,扩容机制,加载因子等内容
loadFactor加载因子
loadFactor加载因子是控制数组存放数据的疏密程度,loadFactor越趋近于1,那么 数组中存放的数据(entry)也就越多,也就越密,也就是会让链表的长度增加,loadFactor越小,也就是趋近于0,数组中存放的数据(entry)也就越少,也就越稀疏。
loadFactor太大导致查找元素效率低,太小导致数组的利用率低,存放的数据会很分散。loadFactor的默认值为0.75f是官方给出的一个比较好的临界值。
给定的默认容量为 16,负载因子为 0.75。Map 在使用过程中不断的往里面存放数据,当数量达到了 16 * 0.75 = 12 就需要将当前 16 的容量进行扩容,而扩容这个过程涉及到 rehash、复制数据等操作,所以非常消耗性能。
介绍下 ConcurrentHashMap 的分段锁
解释下哈希碰撞,当两个元素哈希值相等时,它是怎么一个操作原理和过程呢?
Java 多线程
了解过线程(Thread)吗?
说一下实现线程有哪几种方法?
在实际场景中有使用过 Callable 吗?
平时一般使用哪种类型的线程池?
假设你的 CPU 是 2 核或者是 4 核的,如果采用固定大小的线程池,那你会固定多少个线程?
说一下对 synchronized 关键字的了解
平时在实际场景中是如何使用这个关键字的?
一般是将这个关键字加在方法上还是代码体上?
平时在项目中是有没有使用过 synchronized,是如何是用的?
说一下对 volatile 关键字的了解
volatile 关键字会禁止指令重排序吗?
平时有使用过 volatile 关键字吗?
有了解过 volatile 的底层原理吗?比如说在操作系统中,在内存中的过程
AQS 了解吗?简单讲一下
JVM
说一下你对 JVM 的了解
了解 JVM 的内存分配吗?
比如 private String a = “abc” 语句,定义了一个字符串常量,它是存储在哪里?
说一下堆细分成哪几部分?
比如 new String(“字符串”) 这种方式,常量的值是放到哪一区域?新生代还是老年代?
如果是大对象的话是优先放到老年代,对吧?
说一下一个对象从 Eden 区到 From Survivor ,再到 To Survivor 区,再到老年代的过程
如何判断一个对象死亡?
说一下四种引用类型
像我们日常当中是使用哪一个引用?
日常中使用强引用的时候会导致 gc 无法回收,虚拟机内存从而会抛出一个什么异常或错误?
Error 和 Exception 的区别?
如何判断一个类是无用的类?
数据库
说一下你对索引的理解,优缺点等
平时有使用过联合索引吗?
比如说有 a,b,c,d,e,f,g 这些字段,把 b,c,d 三个字段做一个索引,有没有这样使用过?或者说当我这样去使用的时候,为了使索引生效,我应该怎样去查询?比如当我有 b,c,d 三个字段的值的时候可以生效,如果只有 b,c 或 c,d 等字段的值时可以让联合索引生效吗?
如果使用模糊查询,那这个索引还能生效吗?
如果是前后都有 % ,它能生效吗?
除了 % 能让索引失效,还有哪些能让索引失效?
平时在设计数据库表的一些基础字段时是如何设计的?
说一下事务的隔离级别
框架
有看过 Spring 的 IOC 和 AOP 的源码吗?
在日常使用中有没有了解过 xml 配置文件,使用注解之类的?
Node节点类源码:
// 继承自 Map.Entry<K,V>
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;// 哈希值,存放元素到hashmap中时用来与其他元素hash值比较
final K key;//键
V value;//值
// 指向下一个节点
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
public final K getKey() { return key; }
public final V getValue() { return value; }
public final String toString() { return key + "=" + value; }
// 重写hashCode()方法
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
// 重写 equals() 方法
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
public class test3 {
public static void main(String[] args) {
HashMap<String,String> map=new HashMap<>();
// 键不能重复,值可以重复
map.put("san", "张三");
map.put("si", "李四");
map.put("wu", "王五");
map.put("wang", "老王");
map.put("wang", "老王2");// 老王被覆盖
map.put("lao", "老王");
System.out.println("-------直接输出hashmap:-------");
System.out.println(map);
/*
遍历hashmap
*/
System.out.println("-----foreach获取Map中所有的键--------");
Set<String> keys=map.keySet();
for (String key:keys){
System.out.print(key+" ");
}
System.out.println();
// 2.获取Map中所有值
System.out.println("-------foreach获取Map中所有的值:------");
Collection<String> values = map.values();
for (String value : values) {
System.out.print(value+" ");
}
System.out.println("--------得到key的同时也得到值----------");
Set<String> keys2=map.keySet();
for (String key:keys2){
System.out.print(key+":"+map.get(key)+" ");
}
Set ent=map.entrySet();
Iterator it=map.keySet().iterator();
while (it.hasNext()){
String key=(String) it.next();
}
System.out.println("----------map的其他用法-------------");
System.out.println("after map.size():"+map.size());
System.out.println("after map.isEmpty():"+map.isEmpty());
System.out.println(map.remove("san"));
System.out.println("after map.remove():"+map);
System.out.println("after map.get(si):"+map.get("si"));
System.out.println("after map.containsKey(si):"+map.containsKey("si"));
System.out.println("after containsValue(李四):"+map.containsValue("李四"));
System.out.println(map.replace("si", "李四2"));
System.out.println("after map.replace(si, 李四2):"+map);
}
}