01.Java数据结构和多线程
数据结构
数据结构是计算机存储、组织数据的方式。
数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。不同的数据结构的操作性能是不同的:(有的查询性能很快,有的插入速度很快,有的是插入头和尾速度很快,有的做等值判断很快,有的做范围查找很快,有的允许元素重复,有的不允许重复等等),在开发中如何选择,要根据具体的需求来选择.
常用的数据结构
1.数组(Array)
Array的作用:此类用来包含用来操作数组(比如排序和搜索)的各种方法.此类还包含一个运行将数组作为列表来查看的静态工程
语法:
ArrayList list = new ArrayList();//创建对象的方式 list.add(10);//此处发生的是自动装箱int --> Integer 1.5 list.add(true);//Boolean list.add(3.14);//Double list.add('a');//Character for(Object object : list) { System.out.println(object); } list.add(Integer.valueOf(10)); list.add(Boolean.valueOf(true)); list.add(Double.valueOf(3.1400000000000001D)); list.add(Character.valueOf('a'));
语法:
ArrayList<Integer> list = new ArrayList<>();
list.add(10);
list.add(12);
list.add(14);
list.add(17);
list.add(20);
int index = Collections.binarySearch(list, 10);
System.out.println(index);
Integer min = Collections.min(list);
System.out.println(min);
System.out.println(Collections.max(list));
ArrayList<Integer> list = new ArrayList<>();
List<Integer> sList = Collections.synchronizedList(list);
System.out.println(list == sList);
2.链表(Linked List)
内部通过链表实现,通过指针实现,检索的对象时从两头检索,看索引位于哪个半段范围中。内部存放了首尾两个节点,元素通过node连接在一起。Node由item 、 prev、 next构成,检索最坏情况不会超过半数。
语法:
HashMap<String, Integer> map = new HashMap<String, Integer>();
//如果是第一次添加,返回值为null,否则返回的是key之前对应的value的值.
Integer r1 = map.put("001", new Integer(1));
Integer r2 = map.put("001", new Integer(100));
Integer r3 = map.put("008", new Integer(2));
map.put("003", new Integer(3));
map.put("004", new Integer(4));
map.put("005", new Integer(3));
map.put("A05", new Integer(3));
map.put("105", new Integer(3));
map遍历的第两种方式
// 1.先取得key的集合,然后根据key去找对应的value
Set<String> keySet = map.keySet();
for (String key : keySet) {
// 根据key找value
// Integer value = map.get(key);
// System.out.println(key +"--"+ value);
System.out.println(key + "--" + map.get(key));
}
// 2.获取entry的集合,然后找其中的key和value
Set<Entry<String, Integer>> entrySet = map.entrySet();
for (Entry<String, Integer> entry : entrySet) {
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key +"--"+ value);
}
//获取所有值的集合
Collection<Integer> values = map.values();
for (Integer i : values) {
System.out.println(i);
}
3.哈希表(Hash)
线程不安全,存取速度快,它不保证元素的迭代顺序;也不保证该顺序恒久不变。当HashSet中的元素超过一定数量时,会发生元素的属性重新分配。
HashSet如何保证元素唯一?
1.先调用obj的hashCode方法,计算哈希值(槽位值)
2.根据哈希值确定存放的位置
3.若位置上没有元素,则这个元素就是第一个元素,直接添加
4.若此位置上已经有元素,说明还有元素的hashCode方法返回值与它相同,则调用它的equals方法与已经存在的元素进行比较
5.若返回值为true,表明两个元素是“相同”的元素,不能添加
6.若返回值为false,表明两个元素是“不同”的元素,新元素将以链表的形式添加到集合中
HashMap<String,String>
HashMap<String, String> map = new HashMap<String,String>();
map.put("001", "zhangsan");
map.put("002", "lisi");
map.put("003", "wangwu");
//1.
Set<String> keySet = map.keySet();
for (String key : keySet) {
System.out.println(key +"--"+ map.get(key));
}
System.out.println("---------------");
//2.
Set<Entry<String, String>> entrySet = map.entrySet();
for (Entry<String, String> en : entrySet) {
System.out.println(en.getKey()+"--"+en.getValue());
}
HashMap<Integer,String>
HashMap<Integer, String> map = new HashMap<Integer,String>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
Set<Integer> keySet = map.keySet();
for (Integer key : keySet) {
System.out.println(key +"--"+ map.get(key));
}
Set<Entry<Integer, String>> entrySet = map.entrySet();
for (Entry<Integer, String> en : entrySet) {
}
for(Iterator<Entry<Integer, String>> it = entrySet.iterator();it.hasNext();){
Entry<Integer, String> en = it.next();
System.out.println(en.getKey()+"----"+en.getValue());
}
Iterator<Entry<InCv()){
Entry<Integer, String> en = it.next();
System.out.println(en.getKey() +"--"+ en.getValue());
}
HashMap<String,Student>
HashMap<String, Student> map = new HashMap<String,Student>();
Student s1 = new Student("tom1", 10);
Student s2 = new Student("tom2", 20);
Student s3 = new Student("tom3", 30);
map.put("001",s1);
map.put("002",s2);
map.put("003",s3);
//
Set<String> keySet = map.keySet();
for (String key : keySet) {
Student s = map.get(key);
System.out.println(key +"--"+ s.getName() +"--"+ s.getAge());
}
System.out.println("---------------");
//
Set<Entry<String, Student>> entrys = map.entrySet();
for (Entry<String, Student> en : entrys) {
System.out.println(en.getKey() +"--"+ en.getValue().getName() +"--"+ en.getValue().getAge());
}
HashMap<Student,String>
HashMap<Student, String> map = new HashMap<Student,String>();
Student s1 = new Student("tyson", 10);
Student s2 = new Student("tyson", 10);
Student s3 = new Student("张三", 20);
Student s4 = new Student("张三", 30);
map.put(s1, "001");
map.put(s2, "888");
map.put(s3, "003");
map.put(s4, "004");
System.out.println(map.containsKey(s1));
System.out.println(map.containsValue("001"));
Set<Student> keySet = map.keySet();
for (Student key : keySet) {
System.out.println(key.getName() +"--"+ key.getAge() +"--"+ map.get(key));
}
LinkedHashMap<Student,String>
LinkedHashMap<Student, String> map = new LinkedHashMap<Student,String>();
Student s1 = new Student("tyson", 10);
Student s2 = new Student("tyson", 10);
Student s3 = new Student("张三", 20);
Student s4 = new Student("张三", 30);
map.put(s1, "001");
map.put(s2, "888");
map.put(s3, "003");
map.put(s4, "004");
Set<Student> keySet = map.keySet();
for (Student key : keySet) {
System.out.println(key.getName() +"--"+ key.getAge() +"--"+ map.get(key));
}
Hashtable<String, String> ht = new Hashtable<String,String>();
ht.put("001", "one");
ht.put("002", "two");
ht.put("003", "three");
//通过key找value
Enumeration<String> keys = ht.keys();
while(keys.hasMoreElements()){
String key = keys.nextElement();
System.out.println(key);
String value = ht.get(key);
System.out.println(key +"--"+ value);
}
System.out.println("-----");
Enumeration<String> elements = ht.elements();
while(elements.hasMoreElements()){
String value = elements.nextElement();
System.out.println(value);
}
Hashmap put流程为(源码解析)
int newhash = 获取key的新hash;
通过新hash定位桶的坐标;
if(坐标位为空?){
在该位置创建节点 ;
}
else{
if(新hash相同?){
if(key是同一对象?){
key相同,覆盖value的值。
}
else{
if(key的equals是否相同?){
key相同,覆盖value
}
else{
继续寻找下一个Node;
}
}
}
else{
继续找下一个Node;
}
}
新hash计算方法
旧hash码的高低16位做异或运算,实现计算结果的更加分散,高位右移是想想让更多的各种值参预进来。
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
4.树(Tree)
基于树的Map,它的键唯一,并可以自动排序
根据使用的构造方法决定是使用元素本身可比性,还是使用集合的可比性
TreeMap<String,String>
//创建一个TreeMap集合,让元素具有比较性(自然排序)
语法:
/*
TreeMap<String, String> map = new TreeMap<String,String>();
map.put("001", "001");
map.put("001", "888");
map.put("1", "001");
map.put("01", "001");
map.put("01", "999");
Set<String> keySet = map.keySet();
for (String key : keySet) {
System.out.println(key +"--"+ map.get(key));
}
*/
//创建TreeMap集合,让集合具有比较性
TreeMap<Student,String> map = new TreeMap<Student,String>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
//先比年龄
int r1 = o1.getAge() - o2.getAge();
//比名字
int r2 = (r1==0)?o1.getName().compareTo(o2.getName()):r1;
return r2;
}
});
Student s1 = new Student("tom1", 10);
Student s2 = new Student("Atom1", 10);
Student s3 = new Student("tom1", 9);
Student s4 = new Student("Atom1", 9);
Student s5 = new Student("Atom1", 9);
map.put(s1, "001");
map.put(s2, "002");
map.put(s3, "003");
map.put(s4, "004");
map.put(s5, "005");
Set<Entry<Student, String>> ens = map.entrySet();
for (Entry<Student, String> en : ens) {
System.out.println(en.getKey().getName()+"--"+en.getKey().getAge() +"--"+ en.getValue());
}
5.栈:Stack
6.队列:Queue
Collection
//添加操作:接口多态
Collection c = new ArrayList();
c.add("hello");
c.add(10);//自动装箱
c.add(new Integer(10));
System.out.println(c.size());
Collection c2 = new ArrayList();
c2.addAll(c);
c2.add("world");
System.out.println(c2.size());
//创建新集合
Collection c3 = new ArrayList();
c3.add("hello");
c3.add(10);
c3.add(10);
//删除
c2.removeAll(c);
c2.remove("world");
c2.removeAll(c3);//删除的是二者的交集元素
System.out.println(c2.size());
//判断功能
/*
System.out.println(c2.isEmpty());
c2.clear();
System.out.println(c2.isEmpty());
*/
//判断是否包含元素
System.out.println(c2.contains("java"));
System.out.println(c2.contains("hello"));
System.out.println(c2.containsAll(c3));
List接口:可存放重复元素,元素存取是”有序“的
迭代器返回值类型Object,想调用元素类型本身的方法,必须强转.迭代器返回值类型Object,想调用元素类型本 身的方法,必须强转.
List list = new ArrayList();
list.add("hello"); list.add("world"); list.add(10); Iterator it = list.iterator(); while(it.hasNext()){ Object obj = it.next(); //强转 String s = (String)obj; System.out.println(s.substring(1)); }
迭代器:
Collection接口种的方法:
Iterator iterator()
Collection c = new ArrayList();//List接口都是有序的.
c.add("hello2");
c.add("hello");
c.add("hello");
//使用迭代器遍历集合
Iterator it = c.iterator();//具体是哪个子类对象呢??
//循环改进
while(it.hasNext()){
Object obj = it.next();
System.out.println(obj);//实际上调用的是obj的toString方法
}
//增强for循环:迭代器的一种简化写法:
for(Object obj : c){
System.out.println(obj);
}
//用增强for遍历数组
int[] arr = {1,2,3,5};
for(int a : arr){
System.out.println(a);
}
反编译 后的结果是:
* for (Iterator it = c.iterator(); it.hasNext(); System.out.println(obj))
obj = it.next();
Object obj;
for (Iterator iterator = c.iterator(); iterator.hasNext(); System.out.println(obj))
obj = iterator.next();
*/
/*
if(it.hasNext()){
System.out.println(it.next());
}
if(it.hasNext()){
System.out.println(it.next());
}
if(it.hasNext()){
System.out.println(it.next());
}
if(it.hasNext()){
System.out.println(it.next());
}
*/
/*
//快速生成带元素的集合
Collection c2 = Arrays.asList("hello","world","abc");
System.out.println(c2.size());
*/
Set接口:不可以存放重复元素,通常元素有一些实现类元素是”有序“的存取是”无序“的
List和Set,Map的特点
List //有序 , 可重复
Set //无序 ,不重复
Map //key-value,key有set特点
java
NullPointerException
java里面每一指针操作 :
int *p = &a ;取值
p = ;
List
ArrayList //数组,[] ,擅长读 , 容量。,扩容法则 : cap = cap + (cap >> 1)
LinkedList //链表,擅长写.通过指针引用实现。
first Node E(元素类型)next(下一站) prew
last Node 两个引用
添加一个元素 一个1 把1做成托盘
判断是否存在的标准是判断equals是否相等。同时支持null。
public void testLinkedList(){
List<Integer>list=new LinkedList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.add(6);
list.add(7);
ist.add(8);
}
@Test
public void testBatchIinsert(){
List<Integer>list=new ArrayList<Integer>();
long start=System.currentTimeMillis();
int n==100000000;
for(int i=0;i<n;i++){
list1.add(0,i);
}
System.out.println("arrayList :"+(System.currentTimeMillis()-start));
List<Integer> list2=new LinkedList<Integer>();
start=System.currentTimeMillis();
for(int i=0;i<n;i++){
list2.add(0,i);
}
System.out.println("list :"+(System.currentTimeMillis()-start));
}
Map
hashMap原理 -hashcode-equalsa-新hash判断方式
public void testFind(){
List<Integer>list=new ArrayList<Integer>();
long start=System.currentTimeMillis();
int n==100000000;
for(int i=0;i<n;i++){
list1.add(0,i);
}
long start=System.currentTimeMillis();
list.get(5000);
System.out.println("arrayList :"+(System.currentTimeMillis()-start));
List<Integer> list2=new LinkedList<Integer>();
start=System.currentTimeMillis();
for(int i=0;i<n;i++){
list2.add(0,i);
}
start=System.currentTimeMillis();
list2.get(5000);
System.out.println("arrayList :"+(System.currentTimeMillis()-start));
}
hash :闪列 取决于HashCode是怎么实现的,让更多的特征值参与进来
hasMap判断存不存在有三个条件:
1.新hash值一样不?
2.key是否是同一对象
3.equalsa是否相等
TreeMap判断存不存在
只比较compareTo()只比较这个数字实现compare接口或对比器,只要两个key值相同,比较结果是0就代表它存在了
TreeMap:本身是可以排序的
@Test
public void testNewTree(){
/**
Map<Integer,String> map=new TreeMap();
map.put(1,"toms1");
map.put(2,"toms1");
map.put(3,"toms1");
map.put(4,"toms1");
map.put(5,"toms1");
map.put(6,"toms1");
map.put(7,"toms1");
map.put(8,"toms1");
map.put(9,"toms1");
***/
Map<MyKey,String> map=new TreeMap(new Comparator() {
public int compare(Object o1, Object o2) {
//o1,o2进行强转
MyKey k1=(MyKey)o1;
MyKey k2=(MyKey)o2;
//升序
// return k1.n -k2.n;
//降序
return -(k1.n -k2.n);
//return 0;
}
});
map.put(new MyKey(1),"toms1");
map.put(new MyKey(3),"toms1");
map.put(new MyKey(6),"toms1");
map.put(new MyKey(8),"toms1");
map.put(new MyKey(2),"toms1");
map.put(new MyKey(4),"toms1");
map.put(new MyKey(5),"toms1");
map.put(new MyKey(7),"toms1");
map.put(new MyKey(9),"toms1");
//迭代
treavelMap(map);
}
public static void treavelMap(Map map){
for(Object obj:map.entrySet()){
Map.Entry e=(Map.Entry) obj;
Object key=e.getKey();
Object value=e.getValue();
System.out.println(key+" : "+value);
}
}
public class MyKey {
public int n;
public MyKey(int n){
this.n=n;
}
//重写toString方法
public String toString(){
return "" +n;
}
//private int age=0 ;
/**
public int hashCode(){
return 1;
}
public boolean equals(Object obj){
return false;
}**/
}
ClassCastException:类转换异常
// 构造对比器
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
它是一个接口 interface Comparator
put
将指定的值与此映射中的指定键关联。
如果先前的map包含了密钥的映射,则旧的值被替换
数据结构具体的东西
Entry是一个内部类
static final class Entry<K,V> implements Map.Entry<K,V> {
K key;
V value;
Entry<K,V> left;
Entry<K,V> right;
Entry<K,V> parent;上边的
boolean color = BLACK;
TreeMap源代码解析
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
//
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
//创建了一个新的节点
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
插入完开始修正
fixAfterInsertion对当前的元素进行修正
fixAfterInsert方法处理逻辑如下,x为插入的节点
private void fixAfterInsertion(Entry<K,V> x) {
x标成红色;
x.color = RED; 把当前元素变成红色
当前元素不为空,不是根 x上级元素是红色的才进行循环
while (x != null && x != root && x.parent.color == RED)(x存在 && x不是root && x上级是红色) {
if (parentOf(x) == leftOf(parentOf(parentOf(x))))x上级是左节点? {
y = 取出x上级对应的右节点;
Entry<K,V> y = rightOf(parentOf(parentOf(x)));
if (colorOf(y) == RED)y是红色的? {
setColor(parentOf(x), BLACK);设置x上级为黑色 ;
setColor(y, BLACK);设置y为黑色;
setColor(parentOf(parentOf(x)), RED);x的上上级为红色;
x = parentOf(parentOf(x));x = x的上上级;
} else//y不是红色的 {
if (x == rightOf(parentOf(x)))x本身是右节点? {
x = parentOf(x);x = x上级节点;
rotateLeft(x);对x进行左旋;
}
setColor(parentOf(x), BLACK);x上级标黑;
setColor(parentOf(parentOf(x)), RED);x上上级标红;
rotateRight(parentOf(parentOf(x))); x上上级右旋;
}
} else x上级是右节点{
Entry<K,V> y = leftOf(parentOf(parentOf(x))); y = x上级对应的左节点 ;
if (colorOf(y) == RED)y是红色 {
setColor(parentOf(x), BLACK);x上级标黑;
setColor(y, BLACK);y标黑;
setColor(parentOf(parentOf(x)), RED);x上上级标红;
x = parentOf(parentOf(x)); x = x上上级;
} else {
if (x == leftOf(parentOf(x)))x上级是左节点? {
x = parentOf(x);x = x上级;
rotateRight(x);对x右旋;
}
setColor(parentOf(x), BLACK); x上级标黑;
setColor(parentOf(parentOf(x)), RED); x上上级标红;
rotateLeft(parentOf(parentOf(x))); x上上级左旋;
}
}
}
root.color = BLACK;把树根制成黑色
}
左旋处理
节点的左孩子看成女儿,右孩子看成儿子,节点本身可能是女儿,也可能是儿子。根暂看成儿子(女儿也可以,只有一个根)。
private void rotateLeft(Entry<K,V> p) {
if (p != null)p存在? {
Entry<K,V> r = p.right; //r = p的儿子;
p.right = r.left;//p的孙女成为p的儿子;
if (r.left != null)//孙女存在
r.left.parent = p;// 孙女的长辈是p;
r.parent = p.parent;//r和p同辈份;
if (p.parent == null)//p没有父代
root = r;//儿子变成root;
else if (p.parent.left == p)
p.parent.left = r;
else
p.parent.right = r;//p原来的儿子r顶替自己的位置;
r.left = p;//p成了r的女儿;
p.parent = r;//儿子r成了p的家长;
}
}
右旋理
右旋原理同左旋原理相类似,左旋是找孙女,右旋是找外孙。
private void rotateRight(Entry<K,V> p) {
if (p != null) {
//找女儿
Entry<K,V> l = p.left;
//外孙变女儿
p.left = l.right;
//建立和外孙的关系
if (l.right != null) l.right.parent = p;
//女儿和自己同辈
l.parent = p.parent;
//女儿顶替自己的位置
if (p.parent == null)
root = l;
else if (p.parent.right == p)
p.parent.right = l;
else p.parent.left = l;
//p成女儿的儿子
l.right = p;
//女儿成p的家长
p.parent = l;
}
}
二叉树的遍历
二叉树遍历有四中遍历方式,分别是前序遍历、中序遍历、后续遍历和层序遍历。其中,前中后是指任何一个节点的根在遍历过程中所处的位置。比如前序遍历为根左右,后续遍历为左右根,中序遍历为左根右。由于java的TreeMap没有提供访问根节点、左节点、右节点的方法,需要自行设计方法进行实现。以下代码是分别提取根、key、左节点、右节点的方法,主要是通过反射实现。
getRoot
-
getRoot
/** * 获取map的根节点 */ public static Map.Entry getRoot(TreeMap map) throws Exception { Field f = TreeMap.class.getDeclaredField("root") ; f.setAccessible(true); Object o = f.get(map) ; return (Map.Entry) o; }
-
getLeft
public static Map.Entry getLeft(Map.Entry e) throws Exception { Field f = e.getClass().getDeclaredField("left") ; f.setAccessible(true); Object o = f.get(e) ; return (Map.Entry) o; }
-
getRight
public static Map.Entry getRight(Map.Entry e) throws Exception { Field f = e.getClass().getDeclaredField("right") ; f.setAccessible(true); Object o = f.get(e) ; return (Map.Entry) o; }A
-
getKey
public static Object getKey(Map.Entry e) throws Exception { Field f = e.getClass().getDeclaredField("key") ; f.setAccessible(true); Object o = f.get(e) ; return o; }
-
getColor
-
获得节点的颜色,颜色是boolean值,表示是否是黑色,默认是true,即默认黑色。
public static String getColor(Map.Entry e) throws Exception { Field f = e.getClass().getDeclaredField("color") ; f.setAccessible(true); Object o = f.get(e) ; return o.toString(); } 1前序遍历 /** * 递归实现前序遍历 */ public static void preOrderTravel(Map.Entry e) throws Exception { if(e != null){ System.out.println(getKey(e)); preOrderTravel(getLeft(e)) ; preOrderTravel(getRight(e)) ; } } @Test public void testMidTravel() throws Exception { TreeMap<Integer, String> map = new TreeMap<Integer, String>(); map.put(8 , "tom1"); map.put(4 , "tom1"); map.put(1 , "tom1"); map.put(2 , "tom1"); map.put(3 , "tom1"); map.put(6 , "tom1"); map.put(7 , "tom1"); map.put(5 , "tom1"); preOrderTravel(getRoot(map)); } 2 中序遍历 public static void preOrderTravel(Map.Entry e) throws Exception { if(e != null){ preOrderTravel(getLeft(e)) ; //中间访问 System.out.println(getKey(e)); preOrderTravel(getRight(e)) ; } } 3 后续遍历 public static void preOrderTravel(Map.Entry e) throws Exception { if(e != null){ preOrderTravel(getLeft(e)) ; preOrderTravel(getRight(e)) ; //最后访问 System.out.println(getKey(e)); } } 4 层序遍历 public static void midOrderTravel(int level ,List<Map.Entry> list) throws Exception { List<Map.Entry> sublist = new ArrayList<Map.Entry>() ; if(!list.isEmpty()){ System.out.print(level + " ==> "); for(Map.Entry e : list){ Object key = getKey(e) ; String red = getColor(e) ; System.out.print(String.format("(%d:%s)", key , red)) ; Map.Entry left = getLeft(e); if(left != null) sublist.add(left) ; Map.Entry right = getRight(e); if(right != null) sublist.add(right) ; } System.out.println(); midOrderTravel(level + 1 , sublist); } }
- 根节点是黑的。该规则有时会忽略,因为根总是要从红变成黑色,反之是没有必要,该规则在分析时有些许影响。
- 所有叶子是黑色的
- 如果节点为红,孩子都是黑色的
- 给定节点到所达叶子(NIL)节点的每条路径都含有相同数量的黑色节点
时间复杂度
O(n)
arr[0] = O(n^0) = O(1)
for(...) //O(n)
n = 81
冒泡 : O(n^2) O(n)
hashmap : 最差(O(n) , O(1))
折半查找 : O(log_2{n})
二叉树遍历 : O(n)
并行|并发
并行计算 //集群相关.
并发场景 //通常跟多线程更相关。
应用程序
能够独立运行的软件。
进程
runtime , 运行的应用程序。
多线程
线程的常用方法
1.start();启动
启动线程,调用该方法后,CPU才能够开始调度该线程,但不是不一定马上调度到,还需要看CPU具体的执行情况。
Thread t = new Thread();
t.start();
2.run();需要实现的方法
我们需要实现的方法,在方法中执行具体的业务逻辑代码。应用程序不需要调用该方法,而是CPU在调度该线程执行后,会自动调用该方法。
3.yield();放弃cpu的抢占权
暂时放弃cpu的抢占权,但是瞬间即逝,即该方法执行后又立即开始抢占CPU开始执行。因此是一个瞬间的动作。这通常是一个暗示,不能完全保证其达到目的。
//当前线程放弃CPU抢占权
Thread.yield();
4.join();加入
等待指定的线程执行完成后,当前线程才能继续执行。因此也可以理解为将指定的线程执行过程加入到当前的线程中。
Thread t = new Thread() ;
//等待t执行完之后当前线程继续执行
t.join() ;
5.sleep();休眠
休眠指定的时间片(毫秒值),就是让当前线程休眠一段时间,时间一到,也不一定就会立即继续执行,还需要等待CPU的调度执行时间。
//当前线程休眠1s
Thread.sleep(1000) ;
6.daemon();守护线程
守护线程是通常是为那些非守护线程提供服务的。如果一个进程中剩余的线程都是守护线程,则进程结束。
Thread t = new Thread();
//设置线程t为守护线程
t.setDaemon(true) ;
7.holdsLock();
判断当前线程是否持有指定的对象的锁,该方法是静态方法。
同一应用程序内同时执行的代码段。,runtime.
//创建锁旗标
Object lock = new Object();
//判断当前线程是否持有lock的锁
Thread.holdsLock(lock) ;
线程安全
线程安全问题是多线程编程中必然会遇到的问题,通常是由于多个线程并发访问共享变量,导致变量的内容不一致引发的安全问题。解决方式就是上锁,即使用synchronized关键字。java中任何对象都含有锁旗标,都可以作为锁出现,因此也相当于信号灯方式,同一时刻,只能有一个线程可以对该锁旗标上锁,其他线程会处于blocked状态,即阻塞状态。线程解锁后,其他线程可以进行抢夺,再进行上锁。锁操作过程中,需要确保的是线程是对同一个对象上锁。实现锁方式有两种,同步方法和同步代码块。
//非静态方法是对当前对象上锁,即this对象
public synchronized void m(){
...
}
//静态方法是以当前Class描述符为锁
public static synchronized void m(){
...
}
//
public void m(){
//同步代码块使用指定对象作为锁
synchronized(lock){
...
}
}
/**
* 生产者生产过程,判断池中是否已满
*/
public synchronized void put(Integer x){
while(pool.isFull()){
this.wait() ;
}
pool.put(x) ;
this.notify() ;
}
/**
* 消费者消费过程,判断池中是否已空
*/
public synchronized void put(Integer x){
while(pool.isEmpty()){
this.wait() ;
}
pool.remove() ;
this.notify() ;
}
死锁问题
如果所有线程都进入等待队列,都等着别人发送通知,但是没有人能够发通知的时候,此时程序处于一种死锁状态。如下经过精心设计的程序就会导致死锁。程序最终的结果是生产者和消费者都进入等待队列。
class PCDemo5{
public static void main(String[] args){
//使用java中集合类,List是列表。
Pool pool = new Pool();
Productor p1 = new Productor("生产者1",pool);
p1.setName("p1");
Consumer c1 = new Consumer("消费者",pool);
c1.setName("c1");
Consumer c2 = new Consumer("消费者",pool);
c2.setName("c2");
p1.start();
c1.start();
c2.start();
}
}
//生产者
class Productor extends Thread{
static int i = 0 ;
private String name ;
private Pool pool ;
public Productor(String name ,Pool pool){
this.name = name ;
this.pool = pool ;
}
public void run(){
while(true){
pool.add(i ++);
}
}
}
//消费者
class Consumer extends Thread{
private String name ;
private Pool pool ;
public Consumer(String name ,Pool pool){
this.name = name ;
this.pool = pool;
}
public void run(){
while(true){
pool.remove();
//System.out.println("-: " + i);
}
}
}
class Pool{
private java.util.List<Integer> list = new java.util.ArrayList<Integer>();
//容器最大值
private int MAX = 1 ;
//添加元素
public void add(int n){
synchronized(this){
try{
String name = Thread.currentThread().getName();
while(list.size() == MAX){
System.out.println(name + ".wait()");
this.wait();
}
list.add(n);
System.out.println(name + " + : " + n);
System.out.println(name + ".notify()");
this.notify();
}catch(Exception e){
e.printStackTrace();
}
}
}
//删除元素
public int remove(){
synchronized(this){
try{
String name = Thread.currentThread().getName();
while(list.size() == 0){
System.out.println(name + ".wait()");
this.wait();
}
int i = list.remove(0);
System.out.println(name + " - : " + i);
System.out.println(name + ".notify()");
this.notify();
return i ;
}
catch(Exception e){
e.printStackTrace();
}
return -1 ;
}
}
}
指定jvm的堆的大小:
java -Xms1m -Xmx1m
多线程面试题
熊吃蜂蜜问题
两只熊,100只蜜蜂,蜜蜂每次生产的蜂蜜量是1,罐子的容量是30,熊在罐子的蜂蜜量达到20的时候,一次性将蜂蜜吃光。熊吃蜂蜜问题
/**
* 罐子类,容器
*/
class Box{
//最大量
public static int MAX = 50 ;
//当前蜂蜜量
private int honeyNum = 0 ;
//向罐子追加蜂蜜
public synchronized void add(int n){
while(honeyNum == MAX){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
honeyNum ++ ;
this.notify();
}
/**
* 消费行为
*/
public synchronized int clearAll(){
while(honeyNum < Bear.MIN){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int n = honeyNum ;
honeyNum = 0 ;
notify();
return n ;
}
}
/**
* 熊
*/
class Bear extends Thread {
public static int MIN = 20 ;
private String bearName ;
private Box box ;
public Bear(Box box, String bearName){
this.box = box ;
this.bearName = bearName ;
}
public void run() {
for(;;){
int n = box.clearAll();
System.out.println(bearName + " : " + n);
}
}
}
/**
* 蜜蜂
*/
class Bee extends Thread {
private String bname;
private Box box ;
public Bee(Box box, String bname) {
this.box =box;
this.bname = bname;
}
public void run() {
int index = 1 ;
for(;;){
box.add(index);
System.out.println(bname + " : " + index);
index ++ ;
}
}
}
//测试类
class App{
public static void main(String[] args) {
Box box = new Box() ;
new Bear(box, "xxxxxx1").start();
new Bear(box, "xxxxxx2").start();
for(int i = 0 ; i < 30 ; i ++){
new Bee(box , "B" + i).start();
}
}
}
和尚吃馒头问题
有30个和尚,100个馒头,每个和尚最多吃4馒头,最少一个馒头,一次只能吃一个馒头。满足上述条件下,尽快把馒头吃了。
/**
* 派发馒头的类
*/
class Boss{
//剩余的馒头数
public static int breadNum = 30 ;
//未吃馒头的和尚数
public static int uneatedMonks = 10 ;
//获取馒头
public synchronized int getBread(Monk monk){
//不足最小值
if(monk.eated < Monk.MIN){
//取出最上方的馒头
int tmp = breadNum ;
breadNum -- ;
if(monk.eated == 0){
uneatedMonks -- ;
}
return tmp ;
}
if(monk.eated == Monk.MAX){
return 0 ;
}
//判断是否有多余的馒头
if(breadNum > (uneatedMonks * Monk.MIN)){
int tmp = breadNum ;
breadNum -- ;
return tmp ;
}
return 0 ;
}
}
/**
* 和尚类
*/
class Monk extends Thread{
private String mname ;
public static int MAX = 4 ;
public static int MIN = 2 ;
//已经吃了多少个
private int eated ;
private String breadNumStr = "" ;
private Boss boss ;
public Monk(Boss boss , String mname){
this.boss = boss ;
this.mname = mname ;
}
public void run() {
for(;;){
int breadNo = boss.getBread(this) ;
if(breadNo == 0){
System.out.printf("%s吃了%d:(%s)\r\n" , mname , eated , breadNumStr);
break ;
}
else{
breadNumStr = breadNumStr + "," + breadNo ;
eated ++ ;
}
}
}
}
//测试类
class App{
public static void main(String[] args) {
Boss boss = new Boss();
for(int i = 0 ; i < 10 ; i ++){
new Monk(boss , "tom" + i).start(); ;
}
}
}