Java集合专题

1.集合的理解(好处)

(1)可以动态保存任意多个对象,使用比较方便

(2)提供了一系列方便操作对象的方法:add,remove,set,get等

(3)使用集合添加,删减等等代码,简洁

2.集合的框架体系

         

1. 集合主要是两组(单列集合 , 双列集合)

2. Collection 接口有两个重要的子接口 List Set , 他们的实现子类都是单列集合

3. Map 接口的实现子类 是双列集合,存放的 K-V

ArrayList arrayList = new ArrayList();
arrayList.add("jack");
arrayList.add("tom");


HashMap hashMap = new HashMap();
hashMap.put("NO1", "北京");
hashMap.put("NO2", "上海");

3.Collection接口和常用方法

3.1collection接口的概念

1.collection实现的子类中可以存放多个元素,每个元素可以是object

2.有些collection实现类,可以存放重复的元素(list集合),有些不可以(set集合)

3.有些collection实现类,可以有序(list),也可以无序(set)

4.collection接口没有直接实现子类,而是通过子接口set和list来实现

3.2collection常用方法

List list = new ArrayList()

(1)add:  添加单个元素    list.add()    //可添加任何对象

(2)remove:  删除指定元素     list.remove(int index)   //index下标

(3)contains:  查找元素是否存在  

(4)size:   获取元素个数

(5)isEmpty:  判断是否为空

(6)clear:清空     list.clear();

(7)addAll:  添加多个元素        //也可添加某个list集合

(8)containsAll:  查找多个元素是否都存在

4.List接口和常用方法

4.1 List接口基本介绍

4.2 List接口常用方法

public static void main(String[] args) {
List list = new ArrayList();
list.add("张三丰");
list.add("贾宝玉");

//1.void add(int index, Object ele):在 index 位置插入 ele 元素
//在 index = 1 的位置插入一个对象

list.add(1, "cc");
System.out.println("list=" + list);

//2.boolean addAll(int index, Collection eles):从 index 位置开始将 eles 中的所有元素添加进来

List list2 = new ArrayList();
list2.add("jack");
list2.add("tom");
list.addAll(1, list2);
System.out.println("list=" + list);

//3.Object get(int index):获取指定 index 位置的元素

//4.int indexOf(Object obj):返回 obj 在集合中首次出现的位置

System.out.println(list.indexOf("tom"));

//5.int lastIndexOf(Object obj):返回 obj 在当前集合中末次出现的位置

list.add("cc");
System.out.println("list=" + list);
System.out.println(list.lastIndexOf("cc"));

//6.Object remove(int index):移除指定 index 位置的元素,并返回此元素
list.remove(0);
System.out.println("list=" + list);

//7.Object set(int index, Object ele):设置指定 index 位置的元素为 ele , 相当于是替换. list.set(1, "玛丽");
System.out.println("list=" + list);

//8.List subList(int fromIndex, int toIndex):返回从 fromIndex 到 toIndex 位置的子集合
// 注意返回的子集合 fromIndex <= subList < toIndex

List returnlist = list.subList(0, 2);
System.out.println("returnlist=" + returnlist);
}

4.3 List的子接口—ArrayList底层结构与源码分析

   1.注意事项:

(1)可以加人多个null,因为(list数据可以重复)

(2)底层是用数组来实现数据存储

(3)基本等同于Vector ,但ArrayList线程不安全,因此执行效率高,在多线程情况下,不建议用

   2.底层操作机制

ArrayList list = new ArrayList();    //使用的无参构造,那初始的elementData容量为10!!!
for(int i= 1;i<=10;i++){
    list.add(i);
}

1.debug —ArrayList list = new ArrayList();后会出现:  elementData ——  一个空数组

2.执行 list.add(i)   

(1)这里首先会进行一个装箱操作,将int 转为 Integer 型

(2)并不会马上就要执行element[size++] = e  ,(size初始化为0)需要先验证一下该数组是否有空间来添加数据 ,所以就要转去执行 ensureCapacityInternal

minCapacity = size+1 (0+1=1)

 3.执行ensureCapacityInternal()

传一个空数组和minCapacity给上面这个函数,让其返回一个minCapacity

先比较该数组是否为空,是的话,就调用Math.max  比较DEFAULT_CAPACITY(10)与minCapacity 返回大的

由于第一次“扩容”,所以返回DEFAULT_CAPACITY(10)

 4.执行ensureExplicitCapcity()  上面已经拿到了最小需要的容量

modCount (记录该数组被修改了多少次)

minCapacity-element.length > 0   用当前加入元素了的数组容量((至少容纳已加入元素的空间,比如我已经有一个元素在数组里面,然后我要添加第二个,那说明此时我至少需要两个空间才可以容纳两个数据) - 当前(实际拥有)的数组容量   来判断需不需扩容(grow)真正的扩容操作 

小于0 代表当前实际拥有的容量够用,就不需要扩容!!!

 

 5. 执行 grow()   真正扩容操作

oldCapacity+(oldCapacity>>1)扩容机制(向右移动一位相当于变成原来的1/2) 例如12扩容 则 12+12/6=18

然后考虑两种情况:

(1)新扩容的长度-至少需要的数组长度<0  说明我扩容的数组长度不够数据的添加 所以就需要将至少需要的数组长度(minCapacity)作为要扩容的长度 ;若结果为大于,则表明新扩容的长度比至少需要的数组长度(minCapacity)大,就不需修改,直接用新扩容的长度就行。

(2)考虑如果扩容的长度大于  最大的数组长度(很大) 跳过 

两种情况都不满足就进入 最后一条语句(数组的复制copyOf)

Arrays.copyOf ( arr,n)  【在arr数组的后面加上n个空间(长度)】,这样不会因为我复制数组而导致前面已有的值被丢弃!!!

   做到这步的时候,一步步返回到add的位置

若使用有参构造(则一开始的数组就是8,直接在8的基础上扩容) 其余过程跟无参大致一样

8+4=12

 

5.Vector底层结果和源码分析

5.1 Vector类概念:

(1)底层也是一个对象数组,与ArraysList大致一样。

(2)是线程同步(线程安全)因为带有synchronized(开发时需要线程同步安全 考虑Vector),但效率不高

5.2 扩容机制

(1)使用无参构造时,默认一开始就是10 ,然后按两倍扩容

1. Vector vector = new Vector() 

  new Vector()   底层

 public Vector() { this(10); }   调用另一个构造器

2.public synchronized boolean add(E e) {

          modCount++;

          ensureCapacityHelper(elementCount + 1);

          elementData[elementCount++] = e;

          return true;

}

3.    //确定是否需要扩容 条件 : minCapacity - elementData.length>0

private void ensureCapacityHelper(int minCapacity) {

          if (minCapacity - elementData.length > 0)

          grow(minCapacity);

4.如果 需要的数组大小 不够用,就扩容 , 扩容的算法

capacityIncrement:默认是0,可以自己修改数值,来决定要扩容多少

newCapacity = oldCapacity + ((capacityIncrement > 0) ?  capacityIncrement : oldCapacity);      //就是扩容两倍.

private void grow(int minCapacity) { 

           int oldCapacity = elementData.length;

           int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);

           if (newCapacity - minCapacity < 0) newCapacity = minCapacity;

           if (newCapacity - MAX_ARRAY_SIZE > 0)

          newCapacity = hugeCapacity(minCapacity);

          elementData = Arrays.copyOf(elementData, newCapacity);

}

 (2)使用有参时,直接按照给的参数两倍或任意数值扩容

例如:最初数值容量为10,设置了capacityIncrement 为5 【根据自己需求来自定义扩容空间】

这说明,我下一次扩容时,是按照10+5=15来扩容  15+5=20 每次扩容5个空间 

 6.LinkedList分析

6.1  LinkedList概念

(1)底层使用了双向链表和双端队列的特点

(2)可添加任意元素(可重复)

(3)线程不安全,没有同步

6.2 源码分析

添加(其余的看源码)

 1. LinkedList linkedList = new LinkedList();

         public LinkedList() {}

2. 这时 linkeList 的属性       first = null last = null 

3. 执行 添加      public boolean add(E e) { linkLast(e); return true; }

4.将新的结点,加入到双向链表的最后 void linkLast(E e) {

          final Node l = last;

          final Node newNode = new Node<>(l, e, null);

          last = newNode;

          if (l == null)

              first = newNode;

          else l.next = newNode;

          size++; modCount++;

}

 6.3  ArrayList 和 LinkedList比较


底层结构增删效率改查的效率线程安全
ArrayList可变数组较低,数组扩容(1.5倍)较高不安全
LinkedList双向链表较高,通过链表追加

较低

不安全
Vector可变数组较低,数组扩容(2倍)较低安全

(1)若改查的操作多,选择ArrayList或者 Vector 【和通过索引定位】

(2)增删操作多,选 LinkedList

7. Set接口(常用方法与Collection一样)

没有索引,说明不能使用普通for循环。

7.1 hashSet底层分析 (底层是hashMap)

 

1. 执行 HashSet()

          public HashSet() {

            map = new HashMap<>();

 }

2. 执行 add() public boolean add(E e) {

           return map.put(e, PRESENT)==null;     //(static) PRESENT = new Object();

}

3.执行 put() , 该方法会执行 hash(key) 得到 key 对应的 hash 值

算法 h = key.hashCode()) ^ (h >>> 16)  【向左移16位,类似逻辑左移】

  public V put(K key, V value) {       //key = "" value = PRESENT 共享

         return putVal(hash(key), key, value, false, true);

}

 4.  执行 putVal

注:Node[] tab; Node p; int n, i; //定义了辅助变量

table 就是 HashMap 的一个数组,类型是 Node[]

if 语句表示如果当前 table 是 null, 或者 大小=0 //就是第一次扩容,到 16 个空间.

(1)根据 key(计算hash),得到 hash 去计算该 key 应该存放到 table 表的哪个索引位置 //并把这个位置的对象,赋给 p

(2)判断 p 是否为 null

(2.1) 如果 p 为 null, 表示还没有存放元素, 就创建一个 Node (key="???",value=PRESENT)

(2.2) 就放在该位置 tab[i] = newNode(hash, key, value, null)

 如果p 不为null ,这说明该索引的tab位置上已经有了一个数据,那就执行else的内容:

//如果当前索引位置对应的链表的第一个元素和准备添加的 key 的 hash 值一样【tab所放位置一样】

//并且满足 下面两个条件之一:

//(1) 准备加入的 key 和 p 指向的 Node 结点的 key 是同一个对象

//(2) p 指向的 Node 结点的 key 的 equals() 和准备加入的 key 比较后     相同就不能加入     

就让e=p 跳到          if(e!=null)返回一个对象

8.  LinkedHashSet

8.1 LinkedHashSet的说明

(1)是hashSet的子类

(2)底层维护了一个数组+双向链表

(3)添加顺序和输出顺序一样(双向链表的作用) 

9.  Map接口 (晕晕晕)

9.1 HashMap说明

没有实现同步,线程不安全

当有相同的key   就等价于value替换(修改value)

真正的k-v 是放在HashMap$Node 里面 (内部类)  Node实现了Entry接口

Set   Collection 指向  HashMap$Node  【方便遍历】把每一组对象做成一个entry

然后把entry放在entrySet 集合 , 该集合存在的元素类型是Entry

key表面放在Set集合的KeySet里面,values表面放在Collection里面 

KeySet:获取所有的键(封装到Set集合中)

values封装在Collection

(Set)entrySet:获取所有关系k-v

entrySet中,定义的类型是Map.Entry ,但是实际上存放的还是HashMap$Node(方便遍历)【接口的多态】

存放原因(HashMap$Node 实现了 Map.Entry ) 方便原因:Map.Entry 提供重要方法 getKey()、getValue()

注:为了从HashMap$Node 取出k-v  (HashMap$Node转为Entry 然后Entry放在EntrySet

先做向下转型

Map.Entry entry = (Map.Entry) obj  

values:获取所有的值

 9.2 遍历

//第一组: 先取出 所有的 Key , 通过 Key 取出对应的 Value
Set keyset = map.keySet();
//(1) 增强 for
System.out.println("-----第一种方式-------");
for (Object key : keyset) {
System.out.println(key + "-" + map.get(key));
}
//(2) 迭代器
System.out.println("----第二种方式--------");
Iterator iterator = keyset.iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
System.out.println(key + "-" + map.get(key));
}


//第二组: 把所有的 values 取出
Collection values = map.values();
//这里可以使用所有的 Collections 使用的遍历方法
//(1) 增强 for
System.out.println("---取出所有的 value 增强 for----");
for (Object value : values) {
System.out.println(value);
}
//(2) 迭代器
System.out.println("---取出所有的 value 迭代器----");
Iterator iterator2 = values.iterator();
while (iterator2.hasNext()) {
Object value = iterator2.next();
System.out.println(value);
}
//第三组: 通过 EntrySet 来获取 k-v
Set entrySet = map.entrySet();// EntrySet<Map.Entry<K,V>>
//(1) 增强 for
System.out.println("----使用 EntrySet 的 for 增强(第 3 种)----");
for (Object entry : entrySet) {
//将 entry 转成 Map.Entry
Map.Entry m = (Map.Entry) entry; (向下转型)
System.out.println(m.getKey() + "-" + m.getValue());
}
//(2) 迭代器
System.out.println("----使用 EntrySet 的 迭代器(第 4 种)----");
Iterator iterator3 = entrySet.iterator();
while (iterator3.hasNext()) {
Object entry = iterator3.next();
//System.out.println(next.getClass());//HashMap$Node -实现-> Map.Entry (getKey,getValue)
//向下转型 Map.Entry
Map.Entry m = (Map.Entry) entry;
System.out.println(m.getKey() + "-" + m.getValue());
}

 例题:

10.  Hashtable

10.1  Properties 【继承了Hashtable】

 11.  Collections 工具类

 常见方法:

sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
//我们希望按照 字符串的长度大小排序
Collections.sort(list, new Comparator() {   传入一个新定义的接口  (匿名内部类)
@Override
public int compare(Object o1, Object o2) {
//可以加入校验代码. return ((String) o2).length() - ((String) o1).length();
}
});
System.out.println("字符串长度大小排序=" + list);

//Object max(Collection):根据元素的自然顺序(字典顺序),返回给定集合中的最大元素
System.out.println("自然顺序最大元素=" + Collections.max(list));
//Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
//比如,我们要返回长度最大的元素
Object maxObject = Collections.max(list, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return ((String)o1).length() - ((String)o2).length();
}
});
System.out.println("长度最大的元素=" + maxObject);
//void copy(List dest,List src):将 src 中的内容复制到 dest 中
ArrayList dest = new ArrayList();
//为了完成一个完整拷贝,我们需要先给 dest 赋值,大小和 list.size()一样 否则会抛异常
for(int i = 0; i < list.size(); i++) {
dest.add("");
}
//拷贝
Collections.copy(dest, list);
System.out.println("dest=" + des

 

 12. 总结!!!

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值