集合框架学习笔记

集合框架由来
  Java2之前,Java是没有完整的集合框架的.它只有一些简单的可以自扩容的容器类,比如 Vector,Stack,Hashtable等
  为什么存在容器(可以存储多个数据)
数组的弊端:
  1,长度是不可变的,一旦数组初始化之后,长度是固定的
  2,在N个地方需要存储多个数据,都得专门写数组的操作方法,代码和功能重复
  3,数组定义方法参差不齐
集合框架:是为表示和操作集合而规定的一种统一的标准的体系结构.任何集合框架都包含三大块内容:对外的接口,接口的实现和对集合运算的算法
为什么需要集合框架:
  1):提供功能的复用
  2):专注于业务开发,而不是数据结构和算法

**************** Vector 类 ********************
底层是存储数据的,我们通过Vector对象的add方法来存储数据,其实底层依然是存储到 Object 数组中
默认初始容量是10
支持同步 synchronized ,线程安全
(集合只能存储对象,不能存储基本数据类型)(Java5之前,必须对基本数据手动装箱,Java5之后,自动装箱,其实底层依然是手动装箱(Java源文件))
*****集合中存储的对象,都存储的是对象的引用,而不是对象本身******

Vector v = new Vector(5);
v.addElement(123);
v.addElement(123);
StringBuilder sb = new StringBuilder("ABC");
v.addElement(sb);
System.out.println(v);    //[123, 123, ABC]
sb.append("SeeMyGo");
System.out.println(v);    //[123, 123, ABCSeeMyGo]
------->(String改变的不是本身,不会变化)

****************************************************************
Vector 操作方法
===>增加操作
boolean add(E e)  将指定元素添加到此向量的末尾。
void add(int index, E element)  在此向量的指定位置插入指定的元素。
boolean addAll(Collection<? extends E> c)  将指定 Collection 中的所有元素添加到此向量的末尾,按照指定 collection 的迭代器所返回的顺序添加这些元素。
add 和 addAll 比较

Vector v = new Vector(5);
v.addElement(123);
v.addElement(123);

Vector v2 = new Vector();
v2.add(1);
v2.add(2);
v2.add(3);

v.add(v2);
System.out.println(v); //[123, 123, [1, 2, 3]]

v.addAll(v2);
System.out.println(v); //[123, 123, 1, 2, 3]

===>删除操作
Object remove(int index) 移除此向量中指定位置的元素。
boolean remove(Object o)  移除此向量中指定元素的第一个匹配项,如果向量不包含该元素,则元素保持不变。
boolean removeAll(Collection<?> c)  从此向量中移除包含在指定 Collection 中的所有元素。
boolean retainAll(Collection<?> c)  在此向量中仅保留包含在指定 Collection 中的元素。
--->即求两个集合的交集
===>修改操作
Object set(int index, E element)  用指定的元素替换此向量中指定位置处的元素。
===>查询
Object get(int index)  返回向量中指定位置的元素。
Object[] toArray()  返回一个数组,包含此向量中以恰当顺序存放的所有元素。
---->返回的是数组的副本

源代码:

public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
  protected Object[] elementData;
  public Vector() {
    this(10);
  }
  public synchronized void addElement(E obj) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = obj;
  }
====>jdk1.0开始就有
  public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
  }
=======>jdk2.0开始归属集合框架
}

**************** 栈Stack ********************
存储特点:Last In First Out
底层可以使用 数组 或 链表 实现
----->>>>>>>源码: class Stack<E> extends Vector<E> 继承Vector类,数组方式
把数组的最后一个元素位置作为栈顶
方法:
Object peek() 查看堆栈顶部的对象,但不从堆栈中移除它。
Object pop() 移除堆栈顶部的对象,并作为此函数的值返回该对象。
Object push(E item) 把项压入堆栈顶部。
boolean empty() 测试堆栈是否为空。
int search(Object o) 返回对象在堆栈中的位置,以 1 为基数。
>>>>>>Deque 接口及其实现提供了 LIFO 堆栈操作的更完整和更一致的 set,应该优先使用此 set,而非此类。例如:
Deque<Integer> stack = new ArrayDeque<Integer>();

Stack s1 = new Stack();
s1.push("c");
s1.push("a");
s1.push("b");
System.out.println(s1); //[c, a, b]
System.out.println(s1.peek()); //b
ArrayDeque s2 = new ArrayDeque();
s2.push("c");
s2.push("a");
s2.push("b");
System.out.println(s2); //[b, a, c]
System.out.println(s2.peek()); //b

**************** ArrayList类 ********************
集合框架出现后用来替代 Vector的
同:底层原理都是基于数组的算法,一模一样
区别:
Vector : 所有方法都使用了 synchronized 修饰符, 线程安全,但是性能较低,适用于多线程环境
ArrayList : 所有的方法都没有使用 synchronized 修饰符,线程不安全,但是性能较高
---->即使以后在多线程环境下,我们也不使用Vector类

ArrayList list = Collections.synchronizedList(new ArrayList(...));
...
synchronized(list) {
  Iterator i = list.iterator(); // Must be in synchronized block
  while (i.hasNext())
  foo(i.next());
}

若方法需要返回一个 ArrayList 对象
但是该方法未找到对象,不会返回 null,会返回一个空集合
  如 public Arraylist getAll(){
      //return Collections.emptyList(); //最好的方式,返回空的列表
    return new ArrayList();
  }
在Java7之前,即使使用new ArrayList 创建对象,一个元素都不存储,但是在堆空间依然初始化了长度为10的Object数组
在Java7后,优化这个设计,new ArrayList 底层创建的是一个空数组
Object[] elementData = new Object[]{};
在第一次调用add()方法的时候,才会重新去初始化数组

**************** LinkedList类 ********************
LinkedList类是双向链表,单向队列,双向队列,栈的实现类
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable

LinkedList 线程不安全,多线程情况下:
List list = Collections.synchronizedList(new LinkedList(...));

在LinkedList中存在 Object get(int index) 根据索引位置获取对应的元素
链表本没有索引的概念,本不应该有索引,但是从Java2开始,存在了集合框架类作为List接口的实现类,
List中提供了根据索引查询元素的方法,LinkedList内部类提供了一个变量来当做索引
该方法少用,因为LinkedList不擅长查询操作,擅长保存和删除
另: LinkedList 要用 LinkedList来接收,否则其特有的方法不能使用
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Vector类 , ArrayList类,LinkedList类
面向接口编程
接口类型 变量 = new 实现类
List list = new ArrayList

共同特点:
  1):允许元素重复(有序)
  2):记录元素的先后添加顺序
区别:
  Vector类:底层采用数组结构,方法都使用了 synchronized 修饰符,线程安全,但是性能相对于ArrayList低
  ArrayList类:底层采用数组结构,方法没有使用 synchronized 修饰符,线程不安全,性能相对于Vector高
  ==>为保证ArrayList的线程安全,可使用 ArrayList list = Collections.synchronizedList(new ArrayList(...));
  LinkedList类:底层采用双向链表结构,方法没有使用synchronized,线程不安全.
  --->数组结构:插入和删除操作速度低,查询和更改快
       链表结构:插入和删除操作速度快,查询和更改慢
  使用选择:
    Vector不用,优先使用ArrayList

集合元素的迭代遍历操作

Iterator:迭代器推荐使用 for 循环,性能更好一点

System.out.println("使用for循环操作iterator");
List list1 = new ArrayList();
list1.add("a");
list1.add("b");
list1.add("c");
for (Iterator it = list1.iterator();it.hasNext();){
System.out.println(it.next());
}

ListIterator :Iterator的子接口,可以双向迭代,但是注意指针开始在第一个位置,所以只能先往下迭代,然后再往上
Enumeration : 已被Iterator取代 ,方法同Iterator
for (Enumeration<E> e = v.elements(); e.hasMoreElements();)
System.out.println(e.nextElement());

深入分析for-each和迭代器
1):foreach操作数组,底层依然采用for循环+索引来获取元素
2):foreach操作Iterable的实例,底层其实采用Iterator
====>推荐使用foreach迭代数组和集合元素
例外情况:当需要边迭代边删除指定的元素时,此时只能使用迭代器的删除方法

迭代器循环时,在当前线程(main线程)A中,会单独创建一个新的线程B
A线程负责继续迭代,B线程负责去删除
B线程每次都会去检查和A线程中的元素是否个数相同,如果不是报 并发修改异常
解决方法:
不要使用集合对象的删除方法
在Collection接口中存在删除指定元素的方法 boolean remove(Object ele);
该方法只能从集合中删除元素,不能把迭代器中指定的元素删除
==>所以要使用 Iterator中的remove方法,
该方法会从两个线程中同时移除被删除的元素,保证了两个线程的同步

Iterator it = list.iterator();
while (it.hasNext()){
  Object ele = it.next();
  if ("b".equals(ele)){
    it.remove();
  }
}

********泛型*******
底层依然使用的是 强转

泛型通配符: ? 不知道使用什么类型来接收的时候,用?表示未知. 此时只能接收数据,不能存数据
泛型的上限和下限:用来限定元素的类型必须是X类的子类,或父类,或相同

//泛型的上限:此时的泛型?,必须是Number类型或Number类的子类
  private static void doWork1(List<? extends Number> list){
}
//泛型的下限:此时的泛型?,必须是Number类型或Number类的父类
  private static void doWork2(List<? super Number> list){
}

泛型的擦除
1):泛型编译之后就消失了(泛型自动解除)
2):当把带有泛型的集合赋给了不带泛型的集合,此时泛型被解除(手动擦除)
  //带有Integer类型的泛型
  List<Integer> list = new ArrayList();
  list.add(234);
  //不带泛型的集合
  List list2 = null;
  list2 = list;//此时泛型被擦除
  list2.add("abc");
  //带有String类型的泛型
  List<String> list3 = null;
  list3 = list2;
  String num = list3.get(0);
  System.out.println(num);
  //java.lang.Integer cannot be cast to java.lang.String
  //等价于 String num = 123;报错

堆污染:
当一个方法即使用泛型的时候也使用可变参数,此时容易导致堆污染.
如:在Arrays类中的asList方法:public static<T> asList(T...a)

**************************** Set 接口 *********************************
Set只包含了从Collection继承的方法,不过Set无法记住添加的顺序,不允许包含重复的元素.
当试图添加两个相同元素进Set集合,添加操作失败,add()方法返回false
Set判断两个对象是否相等用equals,而不是用==.即两个对象equals比较返回true,Set集合是
不会接受这两个对象的

HashSet():底层采用哈希算法,实际上也是一个数组,提高查询速度
构造方法
HashSet()  构造一个新的空 set,其底层 HashMap 实例的默认初始容量是 16,加载因子是 0.75。
HashSet(Collection<? extends E> c)  构造一个包含指定 collection 中的元素的新 set。
HashSet(int initialCapacity)  构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和默认的加载因子(0.75)。
HashSet(int initialCapacity, float loadFactor)  构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和指定的加载因子。

HashSet判断两个元素是否相等标准,二者缺一不可
  1):两个对象的equals比较相等
  2):两个对象的hashCode方法返回值相等

存储在哈希表中的对象,都应该覆盖equals方法和hashCode方法,并且保证equals相等的时候,hashCode也应该相等
当向HashSet集合中存入一个新的元素时,HashSet会先调用该对象的hashCode方法来得到该对象的hashCode值,然后决定
该对象在hashSet中的存储位置

如果需要把自定义的对象存储到哈希表中,该对象应该覆盖equals和hashcode方法

LinkedHashSet:具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现
(List和Set综合) HashSet子类

SortedSet 可排序的集合
NavigableSet 可范围查询的集合
===>实现类:TreeSet 红黑树查询
TreeSet集合底层采用红黑树算法,会对存储的元素默认使用自然排序(从小到大)
-->必须保证TreeSet存储的元素数据类型一致,否则运行 java.lang.ClassCastException 异常
构造方法:
TreeSet() 构造一个新的空 set,该 set 根据其元素的自然顺序进行排序。
TreeSet(Collection<? extends E> c) 构造一个包含指定 collection 元素的新 TreeSet,它按照其元素的自然顺序进行排序。
TreeSet(Comparator<? super E> comparator) 构造一个新的空 TreeSet,它根据指定比较器进行排序。
TreeSet(SortedSet<E> s) 构造一个与指定有序 set 具有相同映射关系和相同排序的新 TreeSet。

TreeSet的排序规则
自然排序
TreeSet调用集合元素的compareTo方法来比较元素的大小关系,然后将集合元素按照升序排列,要求TreeSet集合中元素得实现Java.util.Comparable接口
数据类型 排序规则
整形,浮点型 按数字大小顺序
Character 按字符集Unicode的值得数字大小排序
String 按字符串中字符的Unicode值排序
定制排序
在TreeSet构造器中传递Java.lang.Comparator对象,并覆盖public int Compare(Object o1, Object o2)再编写比较规则
1,定义一个比较器类 implements Comparator<>方法,编写比较规则

class PersonNameComparator implements Comparator<Person>{

  //按名字长度排序
  @Override
  public int compare(Person o1, Person o2) {
    if (o1.getName().length()>o2.getName().length()){
      return 1;
    } else if (o1.getName().length()<o2.getName().length()){
      return -1;
    } else {
      return 0;
  }

}

2,Set<?> set = new TreeSet<>(new 比较器类());
Set<Person> set1 = new TreeSet<Person>(new PersonNameComparator());

====>比较两个对象是否相等
自然排序:compareTo方法返回0
定制排序:compare方法返回0
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Set接口的实现类
共同的特点:
  1):都不允许元素重复
  2):都不是线程安全的类
    解决方案:Set = Collections.synchronizedSet(Set对象);
区别:
  HashSet:底层采用哈希表算法,查询效率较高
判断两个对象是否相等的规则:
1):equals比较相等
2):hashCode相同
要求:元素都要覆盖equals和hashCode方法
LinkedHashSet:
  HashSet的子类,底层也采用哈希算法,但是也使用了链表算法来维持元素的先后添加顺序
判断两个对象是否相等的规则和HashSet相同
因为需要多一个链表来记录元素的顺序,所以性能相对于HashSet较低
一般少用,如果要求一个集合既要保证元素不重复,也要记录添加先后顺序,才选择使用LinkedHashSet
TreeSet:不保证元素的先后添加顺序,但是会对集合中的元素做排序操作
底层采用红黑树算法(树结构,比较擅长做范围查询)
TreeSet要么自然排序,要么定制排序
.......HashSet 等值查询效率高, TreeSet范围查询效率高(多用于数据做索引)

**************************** Map 接口 *********************************
严格上说,Map并不是集合,而是两个集合之间的映射关系(Map接口并没有继承Collection接口),然而Map可以存储
数据,所以还是习惯称之为集合

key集合(不允许重复--Set集合)
value集合(允许重复--List集合)
Entry: key-value(键值对)
Entry是多个键值对的集合
-->Set<Entry> --- Map

Set<Map.Entry<String, Object>> entry = map.entrySet();
for (Map.Entry<String, Object> entrys:entry){
  String key = entrys.getKey();
  Object value = entrys.getValue();
  System.out.println(key+"--"+value);
}

Set Map 算法
------------------------------------------
HashSet HashMap 哈希表
TreeSet TreeMap 红黑树
LinkedHashSet LinkedHashMap 哈希表/链表

-----------------------
Set底层是Map
把Set集合对象作为Map的key,再使用一个Object常量为value

Map的常用实现类:
HashMap:采用哈希表算法,此时Map中的key不会保证添加的先后顺序,key也不允许重复
key判断重复的标准是:key1和key2是否equals为TRUE,并且hashCode相等
TreeMap:采用红黑树算法,此时Map中的key会按照自然顺序或定制排序进行排序,key也不允许重复
key判断重复的标准是:compareTo/compare的返回值是否为0
LinkedHashMap:采用链表和哈希表算法,此时Map中的key会保证先后添加顺序,key不允许重复.
key判断重复的标准是:key1和key2是否equals为TRUE,并且hashCode相等
Hashtable:采用哈希表算法,是HashMap的前身
集合框架之前,表示映射关系就使用Hashtable
线程安全,但是性能较低
Properties:Hashtable的子类,此时要求key和value都是String类型
用来加载资源文件(properties文件)

---一般的,定义Map,key值用不可变的String类型
HashMap,TreeMap和LinkedHashMap都是线程不安全,但是性能较高
解决方案:
Map m = Collections.synchronizedMap(new HashMap(...));
Hashtable类线程安全,但是性能较低

List和Set及Map相互转换

List<Stng> list = new ArrayList();
把list转换为Set:
Set<String> set = new HashSet<>(list);//此时会消除重复的元素
把Set转换为List:
List<String> list2 = new ArrayList(set);
Map不能直接转换为List或Set
可以将Set,List以对象形式存入Map

*********************************************
集合操作工具类
1,Arrays类
  在Collection接口中有一个方法 toArray 把集合转换为Object数组,
  把集合转换为数组:Object[] arr = 集合对象.toArray();
  数组也可以转换为集合(List集合)
  //把数组转化为List对象
  List<String> list = Arrays.asList("a","b","c");
  >>通过Arrays.asList方法得到的List对象长度是固定的,不能增,也不能减
  因为此ArraysList是 Arrays内部重写的内部类对象,而不是Java.util.ArrayList
  >list.remove(0);
  >此时报错:UnsupportedOperationException
2,Collections类
  EMPTY_LIST 空的列表(不可变的)。
  EMPTY_MAP 空的映射(不可变的)。
  EMPTY_SET 空的 set(不可变的)。
  获取空集合对象(没有元素的集合,注意集合不为null)

  >返回同步安全的方法,当需要使用迭代的时候使用
  List list = Collections.synchronizedList(new ArrayList());
  ...
  synchronized(list) {
  Iterator i = list.iterator(); // Must be in synchronized block
    while (i.hasNext())
    foo(i.next());
  }

时间: 2024-10-21 13:45:31

集合框架学习笔记的相关文章

JavaSE中Collection集合框架学习笔记(2)——拒绝重复内容的Set和支持队列操作的Queue

前言:俗话说“金三银四铜五”,不知道我要在这段时间找工作会不会很艰难.不管了,工作三年之后就当给自己放个暑假. 面试当中Collection(集合)是基础重点.我在网上看了几篇讲Collection的文章,大多都是以罗列记忆点的形式书写的,没有谈论实现细节和逻辑原理.作为个人笔记无可厚非,但是并不利于他人学习.希望能通过这种比较“费劲”的讲解,帮助我自己.也帮助读者们更好地学习Java.掌握Java. 无论你跟我一样需要应聘,还是说在校学生学习Java基础,都对入门和进一步启发学习有所帮助.(关

JavaSE中Collection集合框架学习笔记(3)——遍历对象的Iterator和收集对象后的排序

前言:暑期应该开始了,因为小区对面的小学这两天早上都没有像以往那样一到七八点钟就人声喧闹.车水马龙. 前两篇文章介绍了Collection框架的主要接口和常用类,例如List.Set.Queue,和ArrayList.HashSet.LinkedList等等.根据核心框架图,相信我们都已经对Collection这个JavaSE中最常用API之一有一个较为全面的认识. 这个学习过程,还可以推及到其他常用开源框架和公司项目的学习和熟悉上面.借助开发工具或说明文档,先是对项目整体有一个宏观的认识,再根

Java集合框架学习笔记之集合与Collection API

一.CollectionAPI 集合是一系列对象的聚集(Collection).集合在程序设计中是一种重要的数据接口.Java中提供了有关集合的类库称为CollectionAPI. 集合实际上是用一个对象代表一组对象,在集合中的每个对象称为一个元素.在集合中的各个元素的具体类型可以不同,但一般说来,它们都是由相同的类派生出来的(而这一点并不难做到,因为Java中的所有类都是Object的子类).在从集合中检索出各个元素是,常常要根据其具体类型不同而进行相应的强制类型转换. Collection

Java集合框架学习笔记

本文为学习笔记,学习课程为慕课网Java入门第三季中的集合框架部分,若需要研究文中的代码,可前往下载.http://www.imooc.com/learn/110 1. List(Collection子接口) 1.1 实例化 List list = new ArrayList(); ??List是一个接口,不可直接实例化,通常情况下ArrayList实现类进行实例化. 1.2 增 1.2.1 add(obj) ??直接将obj对象加入List末位. 1.2.2 add(i, obj) ??将ob

JavaSE中Collection集合框架学习笔记(1)——具有索引的List

前言:因为最近要重新找工作,Collection(集合)是面试中出现频率非常高的基础考察点,所以好好恶补了一番. 复习过程中深感之前的学习不系统,而且不能再像刚毕业那样死背面试题,例如:String是固定长度的,StringBuffer和StringBuilder的长度是可以变化的.如果一旦问得深入一点,问为什么有这样的区别就傻眼了,只能一脸呆萌地看着面试官. 因此想要通过写文章的形式,系统地总结学习的内容,例如Collection架构是怎样的.有哪些相关的继承和接口实现,这样才能了解什么时候应

JavaSE中线程与并行API框架学习笔记——线程为什么会不安全?

前言:休整一个多月之后,终于开始投简历了.这段时间休息了一阵子,又病了几天,真正用来复习准备的时间其实并不多.说实话,心里不是非常有底气. 这可能是学生时代遗留的思维惯性--总想着做好万全准备才去做事.当然,在学校里考试之前当然要把所有内容学一遍和复习一遍.但是,到了社会里做事,很多时候都是边做边学.应聘如此,工作如此,很多的挑战都是如此.没办法,硬着头皮上吧. 3.5 线程的分组管理 在实际的开发过程当中,可能会有多个线程同时存在,这对批量处理有了需求.这就有点像用迅雷下载电视剧,假设你在同时

JavaSE中线程与并行API框架学习笔记1——线程是什么?

前言:虽然工作了三年,但是几乎没有使用到多线程之类的内容.这其实是工作与学习的矛盾.我们在公司上班,很多时候都只是在处理业务代码,很少接触底层技术. 可是你不可能一辈子都写业务代码,而且跳槽之后新单位很可能有更高的技术要求.除了干巴巴地翻书,我们可以通过两个方式来解决这个问题:一是做业余项目,例如在github上传自己的demo,可以实际使用:二是把自己的学习心得写成博客,跟同行们互相交流. 3.1 线程的初窥门径 我们在之前的文章里提到的程序其实都是单线程程序,也就说启动的程序从main()程

j2ee开发之Spring2.5框架学习笔记

Spring 2.5框架学习笔记 1.是一个开源的控制反转IOC和面向切面AOP的容器框架 2.IOC控制反转 public class PersonServiceBean { private PersonDao personDao = new PersonDao(); publiv void save(Person person){ personDao.save(person); } } 控制反转:应用本身不负责依赖对象personDao的创建以及维护,依赖对象的创建以及维护是由外部容器负责的

windows下scrapy框架学习笔记—&#39;scrapy&#39; 不是内部或外部命令

最近几天在深入的学习scrapy框架,但是装完各种需要的基础包之后却发现scrapy命令在别的路径下都用不了,我一开始是把python安装在F:\Python路径下的,安装了scrapy后它默认都会安装在这个路径下,scrapy在路径F:\Python\Scripts路径下,我的scrapy命令只能在此路径下用,因此创建什么工程也都只能在此文件下. 想了一下它的工作原理:它在F:\Python\Scripts路径下,就会在Scripts文件下存在一个scrapy批处理文件,那么在DOS下想要命令