• 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);
        }
      }
      
    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)
    " , 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(); ;
        }
      }
    }
    

  • 相关阅读:
    SmartDb代码修改
    windows下Nginx+RTMP部署
    嵌入式linux下获取flash分区大小
    (转)Qt添加windows开机自启动
    (转)交叉编译lrzsz
    关于海思SDK在Ubuntu下安装错误问题
    电总协议串口调试助手
    使用git将本地仓库上传到远程仓库(转)
    c++中包含string成员的结构体拷贝导致的double free问题
    59. 可变参数
  • 原文地址:https://www.cnblogs.com/SteveDZC/p/9726371.html
Copyright © 2020-2023  润新知