对JAVA SE 集合类的个人总结

JAVA类集(JDK1.2 ,java.util)

JAVA类集本质:动态对象数组

复习Collection接口及其子接口List与Set,熟悉常见子类比如ArrayList与HashSet用法。ArrayList与Vector的区别等

1.Collection 接口 :

                  针对单个对象的处理,是单个集合保存的最大父接口。定义如下:

public interface Collection<E> extends Iterable<E>

public interface Iterable<T>:实现了此接口,可以使用for-each循环

Iterator<T> interator()  [JDK1.5之前直接在Collection接口中定义];

Iterator :集合遍历类。迭代器模式      Scanner 类是 Iterator的集合类

Collection 有两个子接口 list   和   Set

List extends Collection:  允许数据重复

Set extends Collection:不允许数据重复

2.List接口(80%):

 E get(int index):根据索引取得保存数据

E  set(int index,E element): 修改指定索引元素的内容,返回修改前内容

List 子接口与Collection接口相比最大的特点在于其有一个get()方法,可以根据索引取得内容,由于List本身还是接口,要想取得接口的实例对象,就必须有子类,在List接口下有三个常用的子类:ArrayList    Vector     LinkedList

2.1    ArrayList :底层为数组

       List允许重复数据

List 基本处理:

public class Test {
	public static void main(String[] args) {
		List<String> list=new ArrayList<>();
		list.add("hello");
		list.add("hello");
		System.out.println(list);
	}
}

//结果: [hello,hello]
//List的其他操作
public class Test {
	public static void main(String[] args) {
		List<String> list=new ArrayList<>();
		System.out.println(list.size()+"、"+list.isEmpty());
		list.add("hello");
		list.add("hello");
		System.out.println(list.size()+"、"+list.isEmpty());
		System.out.println(list);
		System.out.println(list.remove("hello"));
		System.out.println(list.contains("ABC"));
		System.out.println(list.contains("hello"));
		System.out.println(list);
	}
}

List本身有一个好的支持:存在get()方法,可以利用get()方法结合索引取得数据。

//List的get()方法
public class Test {
	public static void main(String[] args) {
		List<String> list=new ArrayList<>();
		list.add("hello");
		list.add("hello");
		for(int i=0;i<list.size();i++) {
			System.out.println(list.get(i));
		}
	}
}

get()方法是List子接口提供的,如果现在操作的是Collection接口,那么对于此时数据的取出只能将集合变为对象数组操作

//Collection 接口,对象的取出只能变成对象数组的方法
public class Test {
	public static void main(String[] args) {
		Collection<String> list=new ArrayList<>();
		list.add("hello");
		list.add("hello");
		Object[] result=list.toArray();
		System.out.println(Arrays.toString(result));
	}
}

2.2    Vector:  底层为数组

  旧的子类Vector使用的较少,Vector是从JDK1.0提出的,而ArrayList是从JDK1.2提出的

//使用Vector
public class Test {
	public static void main(String[] args) {
		List<String> list=new Vector<>();
		list.add("lisifan");
		list.add("lisifan");
		System.out.println(list);
		list.remove("lisifan");
		System.out.println(list);
	}
}

面试题:

ArrayList 于 Vector 区别:

1.Vector JDK1.0  ,ArrayList JDK1.2

2.Vector 采用同步处理,线程安全,效率低

   ArrayList采用异步处理,线程不安全,效率高

3. ArrayList支持 Iteratro,ListIterator,foreach  输出

   Vactor 支持 Iterator, ListIterator, foreach,Enumeration输出

 

2.3    LinkedList:底层为双链表

//使用LinkedList,这个子类向父接口转型的话,使用形式与之前没有任何区别
public class Test {
	public static void main(String[] args) {
		List<String> list=new LinkedList<>();
		list.add("lisifan");
		list.add("lisifan");
		System.out.println(list);
		list.remove("lisifan");
		System.out.println(list);
	}
}

面试题:

ArrayList 与 LinkedList 区别:

1.ArrayList采用数组实现,LinkedList采用链表实现

2.ArrayList适用于频繁查找元素的场景,LinkedList适用于频繁修改元素的场景

ArrayList 封装的是数组;LinkedList封装的是链表

ArrayList时间复杂度为O(1),LinkedList时间复杂度为O(N)

2.4   List与简单JAVA类

        remove()、contains()等方法需要equals()方法的支持(需要覆写equals方法比较属性值)

3.Set接口:

           (本质为没有Value值得Map,先有的Map集合,才有得Set接口)

Set集合接口与List接口再大的不同在于:Set接口中的内容是不允许重复的。

Set接口并没有对Collection接口进行扩充,在Set接口中没有get()方法。

Set集合有两个常用子类:HashSet(无序存储)、TreeSet(有序存储)

3.1   HashSet(无序存储,实际上就是HashMap):底层为数组

        1.允许为空,且不能重复,元素乱序存储

        2.判断重复的依据 hashCode()+equals()

        3.两个对象(第三方的类)相等判断:覆写 hashCode()  equals() 方法消除重复

          必须hashCode()与equals()均返回true才认为相等

public class Test {
	public static void main(String[] args) {
		Set<String> set=new HashSet<>();
		set.add("ly");
		set.add("ying1");
		set.add("ly");
		set.add(null);
		System.out.println(set);
	}
}
// 结果为:[null, ying1, ly]

3.2  TreeSet(有序存储):底层为红黑树(平衡二叉树的一种)

                  1.TreeSet不允许为null,有序存储,并且使用 升序 队列

                  2.TreeSet判断重复元素由CompareTo()来实现

                  3.自定义类要是想使用TreeSet,必须覆写Comparable接口,无需再覆写equals、hashCode

                  4.public int compareTo(T o);

                     a.返回 >0,当前对象大于目标对象

                     b.返回 <0,当前对象小于目标对象

                     c.返回 =0,当前对象等于目标对象

      面试题:

      如果两个对象的hashCode()相同,equals()不同结果是什么?   答:不能消除重复

      如果两个对象的hashCode()不同,equals()相同结果是什么?   答:不能消除重复

个人建议:

     1.保存自定义对象的时候使用 list 接口

     2.保存系统信息的时候使用 Set  接口(避免重复)

4. 集合输出

   四种:Iterator(推荐使用)、ListIterator、Enumeration、for-each

4.1 迭代器输出 Iterator   (List接口 、Set接口 都有这个方法,且只能从前往后输出)

      a. hasNext(): 判断是否有下一个元素

      b.next() :       取得当前元素

      c. remove():   删除当前元素

4.2 双向迭代器  ListIterator (List接口独有)

            除了 Iterator 提供的方法外:

            hasPrevious(): 判断是否有上个元素

            previous();     取得上个元素

            要想从后向前输出,必须先从前向后走一遍,否则无法进行从后向前

4.3  Enumeration 枚举输出(只有Vector类才有)

4.4 for-each  输出

5.Map集合:

              一次性保存两个对象,且两个对象关系为 key = value (键值对对象)

   Map特点:可以通过 key 找到对象 value       public interface Map<K,V>

   常用方法:

   put(K key,V value): 向Map中追加数据

   get(K key): 根据key取得对应value,没找到返回  null

   keySet():返回所有key信息,保存在Set集合中(key值不能重复)

   values():取得所有value信息,保存在Collection中 (value 值可以重复)

   entrySet(): Set<Map,Entry<K,V>> 将Map集合变成Set集合

   interface Entry<K,V> (方便Map 集合输出以及元素保存):Map集合内部接口,具体保存键值对对象

   Map集合内部实际上是一个一个的Map.Entry对象

  getKey():取得key值

   getValue():取得Value值

   ConcurrentHashMap:线程安全的HashMap

5.1 HashMap子类():   HashMap允许 key,value 为null,value 允许多个null

public class Test {
	public static void main(String[] args) {
		Map<Integer, String> map= new HashMap<>();
		map.put(1, "hello");
		map.put(2, "world");
		map.put(1, null);
		map.put(null, null);
	    Set<Integer> set=map.keySet();
	    Iterator<Integer> iterator=set.iterator();
	    while(iterator.hasNext()) {
	    	System.out.println(iterator.next());
	    }
	}
}
// 结果为:null  1  2

5.2  HashMap  源码解析:

5.2.1  HashMap 内部实现 :数组+链表

         数组被分为一个个桶(bucket),通过hash决定键值对在数组中的寻址;hash相同的键值对,以链表形式存储。

         当链表大小超过阈值(TREEIFY_THRESHOLD=8),将其树化成(红黑树)。 

         

         构造方法:

         //初始化默认负载因子 0.75 

        this.loadFactor = DEFAULT_LOAD_FACTOR;

        HashMap采用lazy_load,在初次使用时对table初始化

        put方法实际调用 putVal(int hash, K key,V value,bollean onlulfAbsent,boolean evict)

        putval()参数解析:

                                    hash:表示键值对hash码

                                    onlyIfAbsent:表示当key值重复时是否允许修改,默认为false

                                    evict:当前HashMap状态,false表示正在初始化阶段

       putVal():

       a.如果表格为null,resize方法会初始化它(只在使用时初始化  lazy_load)

       b.resize方法负责创建初始化存储表格或者在容量不满足需求时扩容

       c.键值对在哈希表中的位置由(并非key的hash码),i=(n-1)&hash   ,避免哈希碰撞

       resize(): 初始化表格

       static final int DEFAULT_INITIAL_CAPACITY=1<<4;

       初始化时桶大小为16;

       a.门限值=负载因子*容量     (什么时候需要调整容量)

       b.门限值通常以倍数调整(newThr = oldThr <<1),当元素个数超过门限值大小时,调整Map大小

       c.扩容后将老数组拷贝到新数组

       容量、负载因子:

               容量:

                public HashMap(int initialCapacity):设置容量

                预设容量的大小需要满足 > "预估元素容量/负载因子" 且同时是2的幂次方

                public HashMap(int initialCapacity,float loadFactor)

                负载因子

                a.不推荐改变,默认负载因子满足通用场景需求

                b.不推荐设置负载因子超过0.75的值,会显著增加Hash冲突,降低HashMap性能;

                   同时也不能设置太小的负载因子值,会导致更加频繁的扩容,影响访问性能

       树化:

               JDK1.8为什么引进树化?

                答:安全问题。当对象键值对哈希冲突时,会放在同一个桶中以链表形式存储,如果链表过长,会严重影响

                    存取的性能。

               当一个桶中元素个数 > 8 个会调用treeifyBin() 尝试树化

               如果容量小于 MIN_TREEIFY_CAPACITY,只会再次调用resize() 扩容

               如果容量大于 MIN_TREEIFY_CAPACITY,才会树化改造(只把容量超过8的链表变成红黑树)

               当链表变为红黑树后,元素删除n次后,如果红黑树的节点个数  < UNTREEIFY_THRESHOLD(默认为6)

               在下一次调用resize()后,又会将红黑树变为链表

       5.2.2 Hashtable子类(JDK1.0,第一个存储二元偶对象的值)

                Hashtable  中,key,value 均不为空

                线程安全:在增删改查等方法上加锁(synchronized,同Vector)

       面试题:

       HashMap 与 Hashtable

       Hashtable 是早期 Java类提供的一个哈希表实现,本身是同步的,不支持 null 键和 null 值,

                        由于同步导致的性能开销,所以很少被推荐使用

       HashMap JDK1.2 主要区别在于 HashMap 不是同步的,支持 null 键和 null 值。通常情况下。

                        HashMap进行 put 或  get 操作,可以达到常数时间的性能,所以它是绝大部分利用

                        键值对存储场景的首选。eg:实现一个用户ID和用户信息对应的运行时存储结构。

       NO                  区别·                  HashMap                                         HashTable

         1                   性能                 异步处理,效率高                            同步处理,效率低

         2                   线程安全           线程不安全                                        线程安全

         3                 null操作           允许键和值为null(只允许一对)          不允许键和值为空

         4                  推出版本             JDK1.2                                             JDK1.0

 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

       所有与并发操作相关的集合:都在Java.util.concurrent 包

       //将ArrayList包装为线程安全的List            List<Integer> list=collection.synchronizedList(new ArrayList<>());

       5.3   ConcurrentHashMap子类

       5.3.1  为什么会有ConcurrentHashMap?

        因为Collection集合提供的线程安全包装方法本质上与HashTable、Vector实现线程安全的核心思想一致,线程安全包装、

        方法在各种修改方法(add,remove,set等方法)使用内建锁实现同步,没有真正意义上的改进,效率较低。而ConcurrentHashMap

        的特点 = HashTable的安全性+HashMap的高性能,它既可以保证多个线程更新数据的同步,又可以保证很高的查询速度,并且

        ConcurrentHashMap不允许数据为null.

       2. ConcurrentHashMap   JDK1.7的实现

         答:a.分离锁。将内部结构分段(分成16个:segment),内部存放的HashEntry数组,hash相同的条目也按照链表存储,并发操作的时候只锁条目

                    对应的Segment段,有效避免类似Hashtable整体同步的问题,大大提高性能。

                b.HashEntry内部使用volatile的value字段来保证可见性。

                    构造时,Segment数量由concurrentcylevel决定,默认为16,必须为2的幂数值。

 ####################################################################################################################

      快速失败策略有关问题?

      什么是并发修改?

      答:当一个或多个线程正在遍历一个集合Collection,此时另一个线程修改了这个集合的内容(增、删、改),这就是并发修改

      产生ConcurrentModeficationException的类集?

      答: ArrayList、HashSet()、HashMap()

      

如何产生ConcurrentModification Exception?

//(modeCount)描述当前集合被修改的次数

protected transient int modeCount=0;

expectedModCount 在调用iterator时取得迭代器等于当前的modCount

//遍历时就赋值:

int expectedModeCount=modCount;

checkForComodification();

ConcurrentModifcationException产生于迭代器内部

expectedModCount!=modCount

          如何解决ConcurrentModificationException的类集?

          答:a.并发遍历集合时,不要修改集合的内容!

                 b.使用fail-safe类集(CopyOnWriteArrayList、ConcurrentHashMap)

          

什么是fail-fast 机制?(为了数据安全性)

fail-fast机制在遍历一个集合时,当集合结构被修改,会抛出ConcurrentModification Exception

 

fail-safe机制集合均为线程安全集合

CopyOnWriteArrayList使用Lock锁实现线程安全(JDK1.7之前的ConcurrentHashMap也采用Lock锁)

 

faile-safe任何对集合结构的修改都会在一个复制的集合上进行修改,因此不会抛出ConcurrentModificationException,因为原来集合根本就没变!

 

fail-safe机制有两个问题

(1)需要复制集合,产生大量的无效对象,开销大

(2)无法保证读取的数据是目前原始数据结构中的数据

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值