《编写高质量代码(改善Java程序的151个建议)》读书笔记(二)

举报
山河已无恙 发表于 2022/03/11 10:13:53 2022/03/11
【摘要】 第四章,字符串 52. 推存使用String直接量赋值,Java为了避免在一个系统中产生大量的String对象,设计了一个字符串池(也称字符串常量池,String pool 或 String Constant Pool,存在于JVM常量池(Constant Pool)中),在字符串池中容纳的都是String对象,当创建一个字符串时,首先检查池中是否有字面值相等的字符串,有不在创建,直接返回...

第四章,字符串

52. 推存使用String直接量赋值,

Java为了避免在一个系统中产生大量的String对象,设计了一个字符串池(也称字符串常量池,String pool 或 String Constant Pool,存在于JVM常量池(Constant Pool)中),在字符串池中容纳的都是String对象,当创建一个字符串时,首先检查池中是否有字面值相等的字符串,有不在创建,直接返回池中该对象的引用,没有则创建,然后放到池中,返回新建对象的引用,所以当用“==”判断相等。当使用new String("") 时不会检查池,也不会放入池。不相等,intern方法(会检查池里,有返回)处理相等。String类是一个不可变(Immutable)的类,final不可继承,在String提供的方法中,如果返回字符串一般为新建的String对象,不对原对象进行修改。

53. 注意方法中传递的参数要求,replaceAll传递的是第一个参数为正则表达式。
54. 正确使用String ,StringBuffer , StringBuilder :

CharSequence接口有三个实现类:String , StringBuffer , StringBuilder ,

  • String为不可改变量,修改要么创建新的字符串对象,要么返回自己(str.substring(0)),
  • StringBuffer是一个可变字符序列,他与String一样,在内存中的都是一个有序的字符序列,不同点是值可以改变,
  • StringBuilder 与 StringBuffer在性能上基本相同,都为可变字符序列。不同点为StringBuffer为线程安全的,而StringBuilder为线程不安全。

使用场景

  • String:常量的声明,少量的变量运算。
  • StringBuffer:频繁的字符串运算,多线程(xml,Http解析)。
  • StringBuilder:频繁的字符串运算,单线程(SQL语句拼接)。
55. 注意字符串的位置:在+号处理中,String字符串具有最高的优先级。
56. 自由选择字符串拼接方式:
  • 1,“+”号拼接,时间最长,等价于srt = StringBuilder(srt).append(“c”).toString();
  • 2,concat方法拼接,时间中等,每次都会新建一个String对象;
  • 3,append方法拼接字符串,时间最快,只生成一个String对象。
57. 推存在复杂字符串操作中使用正则表达式来完成复杂处理。查找单词数,\b\w+\b
58. 强烈建议使用UTF编码。
59. 对字符串排序持一种宽容的心态,如果排序不是关键算法,用Collator类即可。

第五章,数组和集合

60. 性能考虑,数组是首选,集合类的底层都是通过数组实现的,基本类型在栈内存中操作的(速度快,容量小),而对象则是在堆内存中操作的(速度慢,容量大)。性能要求较高的时候用数组代替集合。
61. 若有必要,使用变长数组,

数组扩容:

public class demo {
	public demo() {
		// TODO Auto-generated constructor stub
	}
    public static <T> T[] expandpacity(T[] datas, int newLen) {
			newLen = newLen<0?0:newLen;
			return  Arrays.copyOf(datas, newLen);		
		}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		demo []de= new demo[30];
		de = expandpacity(de,60);						
	}

}

集合的长度自动维护功能的原理与此类似。

62. 警惕数组的浅拷贝,数组的copyOf方法产生的数组为一个浅拷贝,与序列化的浅拷贝相同,基本类型拷贝值, 其他拷贝地址,集合的clone方法也为浅拷贝,
  • 浅拷贝(浅复制、浅克隆):被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。 换言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象。
  • 深拷贝(深复制、深克隆):被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深拷贝把要复制的对象所引用的对象都复制了一遍。继承自java.lang.Object类的clone()方法是浅复制。
63. 在明确的场景下,为集合指定初始容量,ArrayList中java实现长度的动态管理。如果不设置初始容量,系统就按照1.5倍的规则扩充,每一次扩充都是一次数组拷贝。
public boolean add(E e) {
    	ensureCapacity(size + 1);//扩展长度。
    	elementData[size++] = e;//追加元素
    	return true;
    }
public void ensureCapacity(int minCapacity){
			modCount++;//修改计数器
			int oldCapacity = elementData.length;//上次数组长度
			if(minCapcity>oldCapacity) {
				Object oldDate[] = elementDate;
				//计算新数组长度。
				int newCapacity = (oldCapacity*3)/2+1;
				if(newCapacity<minCapacity) 
					newcapacity =  minCapacity;
					//数组拷贝,生成新数组
					elementDate = Arrays.copyof(elementDate,newCapacity);
				}
				
			}
	//并不是增加一个元素elementDate的长度就加1,而是在达到elementDate长度的临界点,才将elementDate元素扩充1.5倍。
//无参构造函数
		public ArrayList() {
			this(10);
		}
		//指定长度的有参构造函数
		public ArrayList(int initialCapacity) {
			super();
			if(initialCapacity<0)
				this.elementDate = new Object[initialCapacity];
	}

vector的处理方式与ArrayList的长度处理相似,不同的地方是提供递增步长(capacityIncrement变量)。不设置容量翻倍。HashMap是按照倍数增加的。

64. 多种最值算法,适时选择,使用集合最简单,使用数组性能最优。
public static int max(int [] data) {
			int max = data[0];
			for(int i:data) {
				max = max >i?max:i;
			}
			return max;
		}//自行实现,速度最快
public static int max(int [] data) {
		Arrays.sort(data.clone());
		return data[data.length-1];
	}//先排序后取值
	//查找不重复的仅次于最大值的。
	public static int getSecond(Integer[] data) {
		List<Integer> dataList = Arrays.asList(data);//转换为列表
		TreeSet<Intger> ts = new TreeSet<Integer>(dataList);//删除重复元素并升序排序,由TreeSet类实现。
		return ts.lower(ts.last());//用lower获取小于最大值的值。
	}
65. 避开基本类型数组转化为列表陷阱,原始数据类型数组不能做为asList的输入参数,否则会引起程序逻辑混乱。
public static void main(String[] args ){
	int [] data = {1,2,3,4,5};
	List<Integer> dataList= Arrays.asList(data);
	System.out.println("列表中的元素个数为:"+list.size());
	//asList方法输入的为泛型变长参数,在java中数组为一个对象,它是可以泛型化的,即把int型数组当做T的类型,所以转换后长度为1,调用getclass方法返回:class [I,jvm不能输出Arrays类型,因为Arrays是属于java.lang.reflect包的,它是通过反射访问数组元素的工具类,在java中任何一个数组的类都是“[I”数组类并没有定义,是在编译器编译时生成的,是一个特殊的类,
66. asList方法产生的List对象不可更给,

调用add方法会抛不支持的操作的异常,基于Arrays的ArrayList是一个静态私有内部类,除了Arrays能访问以外,其他类都不能访问,且add方法为ArrayList的父类提供,但是没有具体的实现,Arrays的内部类没有覆写add方法。
ArrayList静态内部类实现了5 个方法。s
ize(元素数量),toArray(转化为数组),get(获取指定元素),set(重置某一元素),container(是否包含某个元素),方法 ,没有实现add和remove方法,
即asList返回的为一个长度不可变的列表,数组为多长转换为列表为多长,即不在保持列表动态变长的特性。List<String>names = Arrays.asList("","","");不可取,列表长度不可修改;

package java_151;

import java.io.Serializable;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.RandomAccess;

public class asList_Demo {
/*
 * 报不支持 的操作异常,	java.lang.UnsupportedOperationException
 * List的
 */
	enum Week {Sun,Mon,Tue,Wed,Thu,Fri,Sat}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Week[] workDays = {Week.Mon,Week.Tue,Week.Wed,Week.Thu,Week.Fri};
		List<Week> list = Arrays.asList(workDays);
		list.add(Week.Sat);
	}
	/*
	 * asList method 源码,直接new一个ArrayList对象并返回,
	 * 此ArrayList非java.util.ArrayList,而不是Arrays的工具类。
	 */
	public static <T> List<T>asList(T...a){
		return new ArrayList<T>();
	}
	/*
	 * ArrayList类为Arrays的内置类,构造函数:为一个静态私有的内部类,没有提供add方法,即只能是
	 * AbstractList提供,但父类提供的add方法没有提供具体的实现,需要覆写。
	 * 
	 */
	private static class ArrayList<E> extends AbstractList<E> implements 
	                          RandomAccess, Serializable{
		private final E[];
		ArrayList(E[] array){
			if(array ==null) {
				throw new NullPointerException();
			}
			a = array;
		}
	}
}

67.不同的列表选择不同的遍历方法,

对于 ArrayList数组for循环下标方式要比foreach遍历快,
对于LinkedList类讲,foreach方法要比fo循环方法快,
ArrayList数组为随机存取列表,LinkedList为有序存取列表(实现了双向链表,每个数据节点有三个数据项前节点引用(Previons),本节点元素(Node element),后继节点的引用(Next Node ))。ArrayList数组实现了RandomAccess接口(随机存取接口),即为一个随机存取的列表,数据元素之间没有关联,没有依赖和索引关系,
Java中,RandomAccess(随机存取)和Cloneble(可以拷贝)。Serializable(可以序列化)接口一样,为标志性接口,不需要任何实现,只是用来表明实现类具有某种性质,
java中foreach语句为iterator(迭代器)的变形使用,即迭代器为23种设计模式的一种,提供一种方法访问一个容器中对象的各个元素,同时又无需暴露内部细节,对于ArrayList来讲,创建一个迭代器,然后屏蔽内部遍历细节,提供hasnext,next等方法,

package java_151;

import java.io.Serializable;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.RandomAccess;

public class asList_Demo {

	public static void main(String[] args) {
		int stuNum = 80*10000;
		//实例化List集合,并指定初始容量。
		List<Integer> scores = new ArrayList<Integer>(stuNum);
		for(int i =  0; i <stuNum ;i++) {
//返回一个伪随机数,它是取自此随机数生成器序列的、在 0(包括)和指定值(不包括)之间均匀分布的 int 值。
			scores.add(new Random().nextInt(150));
		}
		long start = System.currentTimeMillis();
		System.out.println("平均分是:"+average(scores));
		System.out.println("执行时间:"+(System.currentTimeMillis()-start)+"ms");
		
	}
	//foreach语句遍历
	public static int average(List<Integer> list) {
		int sum  = 0;
		for(int i:list) {
			sum = sum+i;
		}
		return sum/list.size();
	}
	//下标遍历
	public static int average(List<Integer> list) {
		int sum  = 0;
		for(int i=0,size=list.size();i<size;i++) {
			sum += list.get(i); 
		}
		return sum/list.size();
		
		/*
		 * foreach 原理
		 */
		for(Iterator<Integer>i = list.iterator();i.hasNext();) {
			
		}
	}
}
//适用用于有序存取列表和随机存取列表。
public static int average(List<Integer> list) {
		int sum  =0;
		if(list instanceof RandomAccess) {
			for(int i = 0,size = list.size();i<size;i++) {
				sum += list.get(i);
			}
		}else {
			for(int i:list) {
				sum +=i; 
			}
		}
		return sum/list.size();
	}
	}
	//LinkList 下标遍历法;
	public E get(int index) {
		return entry(index).element;
	}
	private Entry(E) entry( int index){
		Entry<E> e = heder;
		if(index < (size>>1)) {
			for(int i =0 ; i<= index; i++)
				e = e.next;
		}else {
			for(int i = size; i>index; i++) {
				e = e.previous;
			}
			return e;
		}

68.频繁插入和删除时使用LinkedList,

LinkedList的插入效率要比ArrayList快 50倍,删除要比ArrayList快40倍,修改要慢ArrayList许多,ArrayList在写操作时要慢Lnkedlist;

69.列表相等只需关心元素数据,

在java中,列表只是一个容器,只要是同一种类型的容器(List),不会关心容器的细微差(ArrayList和Kinkedlist两者都实现了LIst接口继承了AbstractList抽象类,equals方法在抽象类中定义。),只要确定所有元素数据,个数相等即可,Set,Map与此相同。判断集合是否相等,只需要判断元素是否相等。

public boolean equals(Object o) {
		//是否为自身
        if (o == this)
            return true;
        //是否为List的实现类,即是否为List列表
        if (!(o instanceof List))
            return false;
        //返回列表元素的迭代器,访问所有的元素进行比较。
        ListIterator<E> e1 = listIterator();
        ListIterator<?> e2 = ((List<?>) o).listIterator();
        while (e1.hasNext() && e2.hasNext()) {
            E o1 = e1.next();
            Object o2 = e2.next();
            //当都为空不执行,一个为空false,都不为空equals比较。
            if (!(o1==null ? o2==null : o1.equals(o2)))
                return false;
        }
        //判断长度是否相等。
        return !(e1.hasNext() || e2.hasNext());
    }
	
70.子列表只是原列表的一个视图,

List提供一个subList方法,与String的subString有点类似,subList方法是由AbstractList实现的,它会根据是不是随机存储提供不同的实现方法,SubList返回的类也是AbstractList的子类,其所有的方法(get,add,set,remove等)都是在原始列表上操作的,它自身并没有生成一个数组或是链表,也就是子列表只是原列表的一个视图(View),所有的修改都反映在原列表上。

List <String> c = new ArrayList<String>();
		c.add("A");
		c.add("B");
		List<String> c1 = new ArrayList<String>(c);
		List<String> c2 = c.subList(0, c.size());
		c2.add("C");
		System.out.println("c==c1"+c.equals(c1));
		System.out.println("c==c2"+c.equals(c2));
71.推存使用subList处理局部列表,
List<Integer> initate = Collections.nCopies(100, 0);
		//public static <T> List<T> nCopies(int n, T o)
		//返回由指定对象的 n 个副本组成的不可变列表。
		List<Integer> list = new ArrayList<Integer>(initate);
		//转换为可变列表。
		//list.subList(20, 30).clear();
	    for(int i = 0,size=list.size();i<size;i++) {
	    	if(i>=20&&i<30) {
	    		list.remove(i);
	    	}
	    }
72.生成子列表后不要在操作原列表

,操作抛出java.util.ConcurrentModificationException并发修改异常,因为subLis取出的列表只是原列表的一个视图,原数据集修改了,但是subList取出的子列表不会重新生成一个新列表,后面的对子列表继续操作时,就会检测到修改计数器与预期的不相同,会抛出并发修改异常,subList的其他方法也会检测修改计数器,例如Set,get,add方法,如果生成子列表在操作原列表,必然会导致视图不稳定,有效的办法是通过Collections.unmodifiableList方法设置列表为只读状态当有多个字列表时,任何一个子列表就都不能修改啦。生成子列表后,原列表保持只读状态。防御式编程。

List<String> list = new ArrayList<>();
	    list.add("A");
	    list.add("B");
	    list.add("C");
	    List<String>subList = list.subList(0, 2);
	    list.add("D");
	    System.out.println("原列表的长度:"+list.size());
	    System.out.println("子列表偶的长度:"+subList.size());
	    抛出异常:Exception in thread "main" java.util.ConcurrentModificationException//设置列表为只读状态。	    
List<String>list  = new ArrayList<String>();
		List<String> subList = list.subList(0, 2);
		//unmodifiableList(List<?y extends T> list) 
        //返回指定列表的不可修改视图。
		list = Collections.unmodifiableList(list);  
73.使用Comparator进行排序,

在java中,要想给数据进行排序,有两种事项方式,一种为实现Comparable接口,一种是实现Comparator接口,

public interface Comparable<T>

此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 compareTo 方法被称为它的自然比较方法。实现此接口的对象列表(和数组)可以通过 Collections.sort(和 Arrays.sort)进行自动排序。实现此接口的对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。
int compareTo(T o)比较此对象与指定对象的顺序。如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。

@FunctionalInterface
public interface Comparator<T>

比较功能,对一些对象的集合施加了一个整体排序 。 可以将比较器传递给排序方法(如Collections.sort或Arrays.sort ),以便对排序顺序进行精确控制。 比较器还可以用来控制某些数据结构(如顺序sorted sets或sorted maps ),或对于不具有对象的集合提供的排序natural ordering ,与Comparable不同,比较器可以可选地允许比较空参数,同时保持对等价关系的要求。
int compare(T o1, T o2) 比较其两个参数的顺序。

package com.liruilong;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * @Description : 集合排序整理
 * @Author: Liruilong
 * @Date: 2019/7/31 22:21
 */
public class Compara implements  Comparable<Compara>{

    private Integer data;

    public Integer getData() {
        return data;
    }

    public void setData(Integer data) {
        this.data = data;
    }

    /**
     * @Author Liruilong
     * @Description public interface Comparable<T>
     * 此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,
     * 类的 compareTo 方法被称为它的自然比较方法。
     * 实现此接口的对象列表(和数组)可以通过 Collections.sort(和 Arrays.sort)进行自动排序。
     * 实现此接口的对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。
     * int compareTo(T o)比较此对象与指定对象的顺序。
     * 如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。
     * @Date 22:54 2019/7/31
     * @Param [o]
     * @return int
     **/
    @Override
    public int compareTo(Compara o) {
       // (x == 1) ? -1 : ((x == 2) ? 0 : ((x == 3) ? 1:((x == 4) ? 3 : ))) 代替 if -else if。
       return  (this.data < o.data) ? -1 : ((this.data == o.data) ? 0: 1);
    }

    public interface Comparator<T> {
        int compare(T o1, T o2);
    }

    public static void main(String[] args) {

        List<Compara> comparaList = new ArrayList<>();
        //方法一 集合元素实现了Comparable 接口,直接用工具类排序
        Collections.sort(comparaList);
        // 方法二 在排序时将比较器传入
        Collections.sort(comparaList, (Compara o1, Compara o2) ->o1.compareTo(o2) );
        //方法三,通过Comparator传入一个比较器
        comparaList.sort((Compara o1,Compara o2) ->o1.compareTo(o2));
        comparaList.sort(new java.util.Comparator<Compara>() {
            /**
             * @Author Liruilong 
             * @Description 针对一些本身没有比较能力的对象(数组)为它们实现比较的功能,
             * 所以它叫做比较器,是一个外部的东西,通过它定义比较的方式,
             * 再传到Collection.sort()和Arrays.sort()中对目标排序,
             * 而且通过自身的方法compare()定义比较的内容和结果的升降序;
             * @Date 23:16 2019/7/31
             * @Param [o1, o2] 
             * @return int 
             **/
            @Override
            public int compare(Compara o1, Compara o2) {
                if (o1.getData()== o2.getData()){
                    return 0;
                }else if(o1.getData() < o2.getData()) {
                    return -1;
                }else {
                    return 1;
                }
            }
        });

    }



}

74.不推存使用binarySearch对队列进行检索。一般使用indexOf方法进行检索,binarySearch使用二分搜索法搜索指定列表,以获得指定的对象,二分法查询,数据要已经实现了升序排序。但是binarySearch在性能上要比indexOf好。
75.集合中的元素必须 做到compareTo和equals同步:实现了Comparable接口的元素就可以排序,compareTo方法是Comparable接口要求必须实现的。当返回0时,表示进行比较的两个元素是相等的,indexOf检索方法是通过equals方法判断的,binarySearch则依赖compare方法查找,不懂?????
76.集合运算时使用更优雅的方式,并集,交集,差集。
List<String> list1 = new ArrayList<>();
		list1.add("A");
		list1.add("B");
		List<String> list2 = new ArrayList<>();
		list2.add("C");
		list2.add("D");
		//并集
		list1.addAll(list2);
		//交集
		list1.retainAll(list2);
		list1.removeAll(list2);
		//无重复的并集
		list2.removeAll(list1);
		list1.addAll(list2);
77.使用shuffle打乱列表,
int tagCloudNum = 10;
	List<String> tagClouds = new ArrayList<String>(tagCloudNum);
	Random random = new Random();
	for(int i = 0;i <tagCloudNum;i++) {
		//获取随机位置;
		int randPotion  = random.nextInt(tagCloudNum);
		//当前元素与随机位置元素交换。
		String temp = tagClouds.get(i);
		tagClouds.set(i, tagClouds.get(randPotion));
		tagClouds.set(randPotion, temp);
	}
		
		int tagCloudNum = 10;
		List<String> tagClouds = new ArrayList<String>(tagCloudNum);
		Random random = new Random();
		for(int i = 0;i <tagCloudNum;i++) {
			int randPotion  = random.nextInt(tagCloudNum);
			//当前元素与随机位置元素交换。
			Collections.swap(tagClouds, i, randPotion);
	}
		int tagCloudNum = 10;
		List<String> tagClouds = new ArrayList<String>(tagCloudNum);
		Collections.shuffle(tagClouds);
78.减少HashMap中的元素的数量

HashMap在底层也是以数组的方式保存元素的,每一个键值对就是一个元素,HashMap把键值对封装为Entry对象,然后把Entry放到数组中,HashMap的底层数组变量为table,是Entry类型的数组,保存一个一个的键值对,HashMap也可以动态的增加,大于等于阈值,数组增大一倍,阈值为当前长度与加载因子的乘机,默认加载因子为 0.75,即HashMap的size大于等于数组长度的0.75倍,就开始扩容。

79.集合中的哈希码不要重复

随机存取的列表是遍历查找,顺序存储列表是链表查找,或者Collections的二分法查找,HashMap等set集合要快于List集合,HashMap每次增加元素都会先计算器哈希码,然后使用hash方法再次对hashCode进行抽取和统计,同时兼顾哈希码的高位和低位的信息产生唯一值,之后通过indexFor方法与数组长度做一次与运算,计算数组位置,hash的方法和iindexFor方法就是把哈希码转化为数组,


public boolean containsKey(Object key) {
        return getNode(hash(key), key) != null;
    }

final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }
public static void main(String[] args) {
		// TODO Auto-generated method stub
		int size = 10000;
		List<String> list = new ArrayList<String>(size);
		for(int i = 0;i < size; i++) {
			list.add("value"+i);
		}
		long statc = System.nanoTime();
		//开始查找
		list.contains("value"+(size-1));
		long end = System.nanoTime();
		System.out.println("List时间为:"+(end-statc)+"ns");
		//Map 的查找时间
		Map  map  =  new HashMap<String, String>(size);
		for(int i = 0;i < size; i++) {
			map.put("key"+i, "value"+i);
		}
		 statc = System.nanoTime();
		//开始查找
		map.containsKey("key"+(size-1));
		 end = System.nanoTime();
		System.out.println("Map 时间为:"+(end-statc)+"ns");	
		
		}
80.多线程使用Vector或HashTable,

Vector是ArrayList的多线程版本,HashTable是HashMap的多线程版本,线程安全和同步修改异常是两个概念,基本上所有集合类都有一个叫做快速失败的校验机制(Ffail-Fast),当一个集合在被多个线程修改并访问时,就可能会出现ConcurrentModificationException 异常,这是为了确保集合方法一致设置对的保护措施,实现原理为modCount修改统计器,当读取列表是发生变化(其他线程也在操作),则会抛出异常,也与 线程同步不同,线程同步是为了保护数据不被脏写,脏读而设置的,Vector的每个方法都加上了synchronized,两个线程进行同样的操作才可以讨论线程同步,一个线程删除一个线程增加,不属于多线程范畴。

package java_151;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Vector {
	
	public static void main(String[] args) {
		
		//火车票列表
		final List<String>tickets = new ArrayList<String>();
		//线程安全:final Vector<String>tickets = new Vector<String>();
		//初始化数据池,
		for(int i = 0; i < 10000 ;i++) {
			tickets.add("火车票"+i);
		}
		//退票
		Thread returnThread  = new Thread() {
			public void rnu() {
				while(true) {
					tickets.add("车票"+new Random().nextInt());
				}
			}
		};
		//售票
		Thread saleThread = new Thread() {
			public void run() {
				for(String ticket:tickets)
					tickets.remove(ticket);
			}
		};
		//启动退票线程
		returnThread.start();
		saleThread.start();
	}

}
Exception in thread "Thread-1" java.util.ConcurrentModificationException
	at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:937)
	at java.base/java.util.ArrayList$Itr.next(ArrayList.java:891)
	at java_151/java_151.Vector$2.run(Vector.java:28)
//10个窗口售票
		for(int i=0; i <10 ; i++) {
			new Thread() {
				public void run() {
					while(true) {
						System.out.println(Thread.currentThread().getId()+"---"+tickets.remove(0));
					}
				};
			}.start();
		}
//为线程同步问题

81.非稳定排序推:

存使用List,Set与List的最大区别为Set中的元素不可以重复(基于equals的返回值),Set的实现类有一个比较常用的类TreeSet,该类实现了类默认排序为升序Set集合,插入一个元素,默认按照升序排序,SortedSet(TreeSet实现了该接口)接口只是定义了在给集合加入元素时将其进行排序,不能保证修改后的排序结果。所以TreeSet适合不变量的集合数据排序。Sstring或Integer等

82.由点几面,集合大家族:
  • `List:实现List的集合主要有ArrayList(动态数组),LinkedList(双向链表),Vector(线程安全的动态数组),Stack(对象栈,先进后出)。

  • Set: 不包含重复元素的集合,其主要的实现类有:EnumSet(枚举类型专用,所有为枚举类型),HashSet(以哈希码决定元素位置,与HashMap相似,提供快速插入和查找),TreeSet(自动排序Set,实现了SortedSet接口)

  • Map:分为排序Map和非排序Map,排序Map主要是TreeMap类,根据Key值进行自动排序,非排序Map主要包括:HashMap,HsahTable,Properties,EnumMap等,其中Properties是HashTable的子类,它的主要用途从property文件中加载数据,并提供方便的读写操作:EnumMap则要求Key必须为某一个枚举类型。Map中还有一个weakHashMap类,采用弱键方式实现的Map类,WeakHashMap对象的存在并不会阻止垃圾回收器对键值对的回收,即不用担心内存溢出问题。

  • Query:队列,分为阻塞式队列和非阻塞式队列,阻塞式队列主要包括:ArrayBlockingQuery(以数组方式实现的有借阻塞数组),PrinonityBoockingQuery(依照优先级组建的队列),LinkedBlockingQuery(通过链表实现的阻塞队列),非阻塞式队列,PrinonityQuery类.

  • 数组:数组与集合的最大区别就是数组能够容纳基本类型,而集合不行,且数组为非动态,集合的底层都是数组。

  • 工具类:数组的工具类时java.util.Arrays和java.lang.reflect.Array,集合的工具类是java.util.Collections.

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。