01.Java数据结构和多线程

Wesley13
• 阅读 521

##数据结构

数据结构是计算机存储、组织数据的方式。 数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。

不同的数据结构的操作性能是不同的:(有的查询性能很快,有的插入速度很快,有的是插入头和尾速度很快,有的做等值判断很快,有的做范围查找很快,有的允许元素重复,有的不允许重复等等),在开发中如何选择,要根据具体的需求来选择.

####常用的数据结构 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);
      }
    }
    
  1. 根节点是黑的。该规则有时会忽略,因为根总是要从红变成黑色,反之是没有必要,该规则在分析时有些许影响。
  2. 所有叶子是黑色的
  3. 如果节点为红,孩子都是黑色的
  4. 给定节点到所达叶子(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(); ;
    }
  }
}

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
java基础(二):谈谈Java基本数据结构
数据结构是计算机存储,组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。通常情况下,精心选择的数据结构可以带来更高的运行或存储效率。数据结构往往同高效的检索算法和索引技术有关java中常见的几种数据结构(也是初级工程师常见面试题)主要是一些常见的容器,它们主要来自于Collection和Map这2个集合;以下是2个集合的总体框架
Wesley13 Wesley13
2年前
java 数据结构(五):数据结构简述
1.数据结构概述数据结构(DataStructure是一门和计算机硬件与软件都密切相关的学科,它的研究重点是在计算机的程序设计领域中探讨如何在计算机中组织和存储数据并进行高效率的运用,涉及的内容包含:数据的逻辑关系、数据的存储结构、排序算法(Algorithm)、查找(或搜索)等。2.数据结构与算法的理解程序能否快速而高效地完成预定的任务,
Wesley13 Wesley13
2年前
MySQL使用on duplicate key update时导致主键不连续自增
使用onduplicatekeyupdate语法有时是很方便,但是会有一个影响:默认情况下,每次更新都会更新该表的自增主键ID,如果更新频率很快,会导致主键ID自增的很快,过段时间就超过数字类型的的范围了解决这个问题,有两种方式:(实际我目前使用的方式是把自增主键ID设置为bigint,也有一部分操作先查询再选择插入OR更新)方法一:拆分成两个
Stella981 Stella981
2年前
Collection 的两个子集Set 和 List
CollectionList(列表)特点:1,有序(存储元素的顺序和取出元素的顺序一致)2,该集合中的元素都有索引,所以可以通过索引(角标)来访问元素。3,它可以存储重复元素。常见子类对象:记住:具体的子类对象,我们要学习应该是该对象的特有的数据结构,以及相关的特点。Vector:jdk1.0
菜园前端 菜园前端
1年前
什么是集合?
原文链接:什么是集合?集合是一种无序且唯一的数据结构,其中的唯一是指集合中的元素。在ES6中新增了一种数据结构Set就是集合。实现功能new()实例化一个集合add()添加元素delete()删除元素has()判断是否存在元素size()获取集合大小应用场
稚然 稚然
3个月前
物联网入门C语言数据结构与算法(全套课程精讲手把手教会)
//下仔のke:https://yeziit.cn/14679/数据结构是计算机存储、组织数据的方式,指相互之间存在一种或多种特定关系的数据元素的集合。数据结构可以分为线性结构和非线性结构。常见的线性结构包括数组、队列、链表等,常见的非线性结构包括树形结构
秦朗 秦朗
3个月前
【万门大学】数据结构与算法Python进阶班
//下仔のke:https://yeziit.cn/13764/数据结构是计算机存储、组织数据的方式,它关注的是数据的逻辑结构、物理结构以及数据之间关系的存储方式。精心选择的数据结构可以提高计算机程序的运行效率。数据结构可以分为线性结构和非线性结构。常见的