JavaSE第七章.集合

泛型

即参数化类型

就是将类型参数化,在使用/调用时传入具体的类型

public class Demo<T,E> {   //参数化类型
    T name;
    E age;

    public void eat(T person){
        System.out.println(age+"的"+person+"吃");
    }

    public static void main(String[] args) {
        Demo<String,Integer> demo = new Demo<>();  //传入类型(只能是类类型),默认Object
        demo.name = "张三";
        demo.age = 18;
        demo.eat(demo.name);
    }
}
----------------------------------------------
18的张三吃

集合概述

放数据对象引用的容器 , 动态增长长度的容器

集合类存放的都是对象的引用,而非对象本身,出于表达上的便利,我们称集合中的对象就是指集合中对象的引用

Java的集合框架是由很多接口、抽象类、具体类组成的

集合类型主要有3种:set(集)、list(列表)和map(映射)

在这里插入图片描述

在这里插入图片描述

Collection

单列存储

List

  • 有序集合,按照插入元素的顺序排列
  • 可以存储重复元素
  • 有索引
ArrayList
  • 底层是数组
  • 查询快,增删慢
  • 线程不安全,效率高

add()底层实现

  1. 检测容器是否已满
  2. 未满将元素添加到末尾
  3. 满了就扩容
    1. 获取oldCapacity,原容积
    2. newCapacity为原来的1.5倍
    3. 使用Array.copyof()将之前数组元素复制到新数组中
//方法
boolean add(E e) 将指定的元素追加到此列表的末尾。  
void add(int index, E element) 在此列表中的指定位置插入指定的元素。  

E get(int index) 返回此列表中指定位置的元素。  

int indexOf(Object o) 返回此列表中指定元素的第一次出现的索引,如果此列表不包含元素,则返回-1int lastIndexOf(Object o) 返回此列表中指定元素的最后一次出现的索引,如果此列表不包含元素,则返回-1E remove(int index) 删除该列表中指定位置的元素。  

boolean remove(Object o) 从列表中删除指定元素的第一个出现(如果存在)。  

protected void removeRange(int fromIndex, int toIndex) 从这个列表中删除所有索引在 fromIndex (含)和 toIndex之间的元素。  

set(int index, E element) 用指定的元素替换此列表中指定位置的元素。

List<E> subList(int fromIndex, int toIndex) 从原集合中复制一段返回新集合,原集合不变 

条件删除 removeIf

boolean removeIf(Predicate<? super E> filter) 删除满足给定谓词的此集合的所有元素。 

        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("a");
        arrayList.add("b");
        arrayList.add("b");
        arrayList.add("c");

        arrayList.removeIf(Predicate.isEqual("b"));  //删除b元素

        arrayList.removeIf(new Predicate<String>() { //new 的是一个匿名内部类,相当于一个外部类继承了Predicate<E>接口然后重写了方法
            @Override
            public boolean test(String s) {
                return "a".equals(s)||"c".equals(s); //条件满足删除元素
            }
        });
        System.out.println(arrayList);  // []

自定义排序规则

arrayList.sort(String::compareTo);

arrayList.sort(new Comparator<String>() {//匿名内部类
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);  //指定规则
            }
        });
LinkedList
  • 底层是双向链表
  • 查询慢(通过序号从头或者尾查找,没有索引。按照索引是否大于一半来决定从左或者从右开始)
  • 增删快 , 只需改变指针域 , 元素在内存中不发生变化
  • 线程不安全,效率高
Vector
  • 底层是数组
  • 查询快;增删慢
  • 线程安全(添加同步锁Synchronized) 对读写都加锁,效率低
  • 扩容为原来的2倍

Set

不可以重复 , 没有索引

HashSet
  • 底层是哈希表+链表+红黑树
  • 无序
  • 可存储null键

add() 操作原理

首先调用hashCode()方法计算哈希值,如果产生哈希冲突再调用equals()方法比较

我们自己写的Student类没有重写hashcode()和equals(),就会调用Object中的

“s”、String、Integer…这些类重写了hashCode()是根据对象中包含的内容来计算哈希值

/*
	HashSet添加时如何判断值是否重复
		添加时会调用hashCode(),equals()
		 添加时要比较内容是否相等,既要保证效率,又要保证安全
		    * 先调用hashCode()计算出一个哈希值,比较哈希值非常快,但是不安全
		    * 当哈希值相同时,再调用equals()方法比较
*/

int h1 = "通话".hashCode();
int h2 = "重地".hashCode();
System.out.println(h1+"::::"+h2);
System.out.println(h1==h2);

HashSet<String> set1 = new HashSet<>();
set1.add("a");
set1.add("通话");
set1.add("重地");
set1.add("a");
set1.add("c");
set1.add("b");
System.out.println(set1);

------------------------------------------
1179395::::1179395
true
[a, 通话, 重地, b, c]
TreeSet
  • 底层是红黑树
  • 有序:自然排序按照值编码的顺序排序;比较器排序继承Comparable接口,通过比较器返回的值来排序或去重
  • 不允许放入null值
//自然排序
        TreeSet<String> tset = new TreeSet<>();
        tset.add("b");
        tset.add("a");
        System.out.println(tset);   //[a,b]
//自定义排序
     //继承Comparable<T>接口
     //向TreeSet中添加元素时调用进行排序比较,以及去重
    @Override
    public int compareTo(Student o) {
        //return this.age-o.age; // 小于0  ==0    大于0
        return this.name.compareTo(o.name);
    }
Iterator

迭代器在迭代期间可以从集合中移除元素

ArrayList<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    list.add(4);
    list.add(1);

/*方式1:for循环      length (数组) length()(字符串)  size()(集合)
		    循环时允许删除元素,但是我们需要注意索引与集合长度之间关系*/
for (int i = 0; i < list.size(); i++) {
    if (list.get(i) == 1) {
        list.remove(i);
        i--;
    }
}
System.out.println(list);

//方式2:增强for循环,循环时不允许从中删除元素   ConcurrentModificationException
for (Integer item : list) {
    if (item == 1) {
        list.remove(item);
    }
    System.out.println(item);
}

//方式3迭代器遍历
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
    Integer item = it.next();
    if (item == 1) {
        it.remove();
    }
}
System.out.println(list);
/*list.removeIf(item -> item == 1);*/

三种遍历方式

// listIterator() 只能用于List接口的集合
ListIterator<Integer> lit = list.listIterator();
while(lit.hasNext()){
    if (lit.next()==2){  //在2后面加2
        lit.add(2);
    }
    //lit.remove();
}
System.out.println(list);

//从指定的位置开始,  list.size()从后向前遍历
ListIterator<Integer> lit1  =   list.listIterator(list.size());
while(lit1.hasPrevious()){
    System.out.println(lit1.previous());
}

//以流的形式输出遍历
Stream<Integer> s =  list.stream();
s.forEach(new Consumer<Integer>() {
    @Override
    public void accept(Integer t) {
        System.out.println(t);
    }
});
//s.forEach(t->System.out.println(t));

Map

  • Map中的集合,元素是成对存在的。每个元素由键与值两部分组成,通过键可以找对所对应的值
  • 双列集合
  • key(键不能重复) value(可以重复)

常用方法

V put(K key, V value) //插入
V remove(Object key) //删除
V get(Object key) //获取
boolean containsKey(Object key)//包含
boolean containsValue(Object value) 
Collection<V> values() //返回此图中包含的值的Collection视图

遍历

//遍历方式1:	KeySet()
Set<String> keySet = new HashSet<>(map.keySet());
for (String key : keySet) {
    System.out.println("key:" + key + ",\tvalue:" + map.get(key));
}

//遍历方式2     entrySet()将map中底层存储键值的Entry对象,封装到一个Set集合中
Set<Map.Entry<String, String>> entrySet = map.entrySet();
for (Map.Entry<String, String> entry : entrySet) {
    System.out.println(entry.getKey() + "::" + entry.getValue());
}

//方式3	forEach
map.forEach(new BiConsumer<String, String>() {
    @Override
    public void accept(String k, String v) {
        System.out.println(k + ":::" + v);
    }
});

HashMap

  • 底层是哈希表+链表+红黑树
  • 无序:根据哈希值排序
  • 键值都可为null
  • 哈希值的使用要重新计算hash值
put()底层原理
  1. 将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算key的哈希值(数组下标)
  2. 遍历该数组下标下的链表,找到此次插入元素的key的原来位置的元素并返回(put()的返回值)
  3. 判断这个结构是链表还是树;红黑树就是用红黑树的插入方式
  4. (尾插法)一直遍历链表,判断到是最后一个元素,在尾部插入,并统计元素个数,大于8就转成红黑树
  5. 判断扩容
为什么用红黑树
  • 链表:插入效率最高,查询效率最低

  • 完全二叉树:插入效率最低,查询效率最高

  • 红黑树:介于链表和完全二叉树之间,因为hasmap不仅需要增还要查

  • jdk8以后 , 做了优化 , 当链表长度为8时,哈希表长度大于64,自动转为红黑树;红黑树需要左旋、右旋、变色操作来保持平衡,当元素小于8时,链表可保持查询性能,元素多时,红黑树时间复杂度为logn,链表是n,所以用红黑树

为什么负载因子0.75

哈希表默认初始长度为16 (entry类型的数组,entry封装了元素<k,v>), 负载因子 0.75(到75%时扩容);对时间和空间的平衡分布,根据泊松分布,0.75性能最佳

为什么扩容两倍

每次处罚扩容机制时 , 扩容为原来的2倍;可以使用位运算代替取余操作,提高了性能

头插法&尾插法

(头插法)将新插入的元素的后继指向之前第一个位置的对象,将数组上的引用改为新插入元素的引用
(尾插法)一直遍历链表,判断到是最后一个元素,在尾部插入,并统计元素个数,大于8就转成红黑树

resize死循环

在JDK1.7当中,多线程同时put时,如果同时调用了resize操作,可能会导致循环链表产生,进而使得后面get的时候,会出现死循环。

比如有ABC这样一个链表。T1和T2线程同时进行扩容,T1和T2现在都指向A,他们的next都是B,T1开始扩容也就是节点转移,因为使用的是头插法,转移后节点排列就是CBA,也就是B指向A,T1的转移对于T2是不可知的,T2恢复执行后,A还是指向B,就形成了环,死循环就产生了。

JDK1.8使用尾插法就避免了链表的死循环。但链表转换为树或对树进行操作时仍会出现死循环。

解决hash冲突方案
拉链法

对于相同的hash值,使用链表进行连接

  1. 处理简单,处理一次就不会发生冲突
  2. 占用空间小
  3. 增删容易实现
  4. 会出现聚集现象(链表过长),导致查询效率低,所以使用hashMap红黑树,兼顾读写效率
再hash法

提供多个hash函数,如果第一个hash函数计算出的hash值冲突,使用其他的hash函数计算

  1. 不易聚集
  2. 增加了计算时间
建立公共溢出区

将hash表分为 基本表公共溢出区,当和基本表冲突时,放入溢出区

开放定址法

当计算出的hash值发生冲突时,以计算出的hash值为基础,再产生一个hash值,直到不发生冲突;有一下计算方法

  1. 线性探测再散列:顺序找到下一个元素
  2. 平方探测再散列:在表的左右两侧进行跳跃式探测

删除节点麻烦,占用空间较大

TreeMap

  • 底层是红黑树
  • 有序
  • key值所在类必须实现Comparable接口
  • 键是红黑树结构,可以保证键的排序和唯一性

HashTable

  • 底层是哈希表+链表(红黑树)
  • 线程安全:put()方法被synchronized修饰,相当于把整个hash表锁住了,锁的力度较大,效率低,用于低并发可以
  • key不能为null
  • 对于哈希值计算直接使用对象的HashCode()

Collections类

集合类的工具类,与数组的工具类Arrays类似

static <T> boolean addAll(Collection<? super T> c, T... elements) 
将所有指定的元素添加到指定的集合。

static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key) 
使用二叉搜索算法搜索指定对象的指定列表。 

static <T extends Comparable<? super T>> void sort(List<T> list) 
根据其元素的natural ordering对指定的列表进行排序。  

static <T> void sort(List<T> list, Comparator<? super T> c) 
根据指定的比较器引起的顺序对指定的列表进行排序。  

static void swap(List<?> list, int i, int j) 
交换指定列表中指定位置的元素。  

static <T> void copy(List<? super T> dest, List<? extends T> src) 
将所有元素从一个列表复制到另一个列表中。  

static <T> List<T> emptyList() 
返回空列表(immutable)。  

static <T> void fill(List<? super T> list, T obj) 
用指定的元素代替指定列表的所有元素。  

static <T extends Object & Comparable<? super T>>T max(Collection<? extends T> coll) 
根据其元素的 自然顺序返回给定集合的最大元素。  

static <T extends Object & Comparable<? super T>>T min(Collection<? extends T> coll) 
根据其元素的 自然顺序返回给定集合的最小元素。 

static <T> boolean replaceAll(List<T> list, T oldVal, T newVal) 
将列表中一个指定值的所有出现替换为另一个。  

static void reverse(List<?> list) 
反转指定列表中元素的顺序。  

可变长度参数

//int...n : 可变长度的参数,本质是一个数组,一个参数列表只能有一个,必须放在参数列表的最后一个
public static void test(String a ,int...n){
    System.out.println(a+n);
}

public static void main(String[] args) {
    test("樊",0,1,2,3);            //樊[I@1b6d3586
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EnndmeRedis

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值