01.Java数据结构和多线程

Wesley13
• 阅读 714

##数据结构

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

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

####常用的数据结构 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
3年前
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
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
java基础(二):谈谈Java基本数据结构
数据结构是计算机存储,组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。通常情况下,精心选择的数据结构可以带来更高的运行或存储效率。数据结构往往同高效的检索算法和索引技术有关java中常见的几种数据结构(也是初级工程师常见面试题)主要是一些常见的容器,它们主要来自于Collection和Map这2个集合;以下是2个集合的总体框架
Wesley13 Wesley13
3年前
MySQL使用on duplicate key update时导致主键不连续自增
使用onduplicatekeyupdate语法有时是很方便,但是会有一个影响:默认情况下,每次更新都会更新该表的自增主键ID,如果更新频率很快,会导致主键ID自增的很快,过段时间就超过数字类型的的范围了解决这个问题,有两种方式:(实际我目前使用的方式是把自增主键ID设置为bigint,也有一部分操作先查询再选择插入OR更新)方法一:拆分成两个
Stella981 Stella981
3年前
Collection 的两个子集Set 和 List
CollectionList(列表)特点:1,有序(存储元素的顺序和取出元素的顺序一致)2,该集合中的元素都有索引,所以可以通过索引(角标)来访问元素。3,它可以存储重复元素。常见子类对象:记住:具体的子类对象,我们要学习应该是该对象的特有的数据结构,以及相关的特点。Vector:jdk1.0
菜园前端 菜园前端
1年前
什么是集合?
原文链接:什么是集合?集合是一种无序且唯一的数据结构,其中的唯一是指集合中的元素。在ES6中新增了一种数据结构Set就是集合。实现功能new()实例化一个集合add()添加元素delete()删除元素has()判断是否存在元素size()获取集合大小应用场