java基础之集合

  集 合

1. 泛型: 就是一个占位符, JDK1.5版本之后出来的特性;

好处:
1> 不符合要求的数据进不来
2> 省去了强制转换的麻烦
3> 将运行时期(java)的错误, 提前到编译器(javac)

使用场景:
1> 集合中: 集合类型<E>变量名 = new 集合类型<E>();
2> 类中: 可以使用在类中,创建对象的时候指定泛型类型
格式: public class 类名<E>{}

3> 方法中: 有两种形式, 第一种和类名保持一致(类的泛型是什么,那么方法的泛型也是什么)
格式: public void method(E e){}

第二种和类不一样,本方法中独有
格式: public <T> void method(T t){};

4> 接口中: 供实现类去使用
格式: public interface 接口名<E>{}

使用接口中的格式有两种:
1> 实现类确定接口中的泛型类型
格式: class 类名称 implements 接口<具体类型>{...}

2> 实现类继续携带泛型,在创建对象的时候指定
格式: class 类名称<E> implements 接口<E>{....}


* 泛型通配符: ?, 一般用来作为方法的参数使用
比如: 需要定义一个方法,遍历两个集合, 两个集合的泛型类型不一样

补充泛型上下界问题:
? extends E: 问号(?)所表示的类型需要是 E或者E的子类型,(上界)
? super E : 问号(?)所表示的类型需要是E或者E的父类(下界)

2. Collection: 是集合框架体系中的根接口
Collection中提供了一些对数据常见操作的成员方法:
1> 添加数据: public boolean add(E e);
2> 删除元素: public boolean remove(E e);
3> 清空元素: public void clear();
4> 是否包含: public boolean contains(E e);
5> 判断空白: public boolean isEmpty()
6> 集合长度: public int size();
7> 转成数组: public Object[] toArray();

直接输出Arraylist集合对象名,输出的并不是地址值,说明已经覆盖了Object中的toString方法

3. 由于Collection集合体系中,很多的集合存储数据的方式不一样,那么他们遍历的方式也不太一样
如果用之前学习过的for循环没有办法做统一的遍历, 会产生很多种遍历方式,有没有一种通用的遍历方式呢?

所以Java中提供了集合的一种通用遍历方式,使用迭代器(Iterator);
Iterator: 是一个接口,专门用来遍历Collection集合体系的.


Iterator的使用步骤:
1> 导入包: import java.util.Iterator;
2> 获取对象:
由于Iterator是一个接口,不能直接new对象
Collection中提供了一个方法来获取迭代器对象:
public Iterator<E> iterator();// E和集合中的泛型保持一致的

3> 使用成员方法
public boolean hasNext();//判断是否有下一个元素
public E next();// 获取到下一个元素

迭代的步骤:
1> 通过Collection获取Iterator对象
2> 通过迭代器对象方法hasNext判断是否有下一个元素
3> 通过迭代器对象方法next获取下个元素
4> 再次判断使用hasNext是否还有下一个元素(循环)

*迭代器: 遍历集合容器

*增强for: 遍历集合容器, 就是通过迭代器实现的

*注意点:
*不管是使用迭代器,还是增强for,都不能在迭代的时候对容器进行删除增加操作
*容易造成并发修改异常:ConcurrentModificationException

4. 使用迭代器对集合内容进行遍历的时候, 会记录集合中的元素内容和个数, 在迭代的过程中不能增加删除集合中的数据
这样有可能会出现并发修改异常;
并发修改异常: ConcurrentModificationException

* 优点: 简化代码, 遍历方便
* 缺点: 但凡使用到索引相关的场景,使用不了, 在遍历的过程中只能看,不能摸.

5. 以前如果想要遍历数组,或者是集合,只能使用普通的for循环
JDK1.5之后,出现了一个新特性,增强for ,也成为"for-each";

可以遍历的内容:
1. 数组
2. Collection集合体系()

格式:
for(数据类型 遍历名 : 数组/集合){
}

好处:简化代码, 省去了索引的繁琐
缺点:凡是带有索引的操作, 不能使用增强for循环

增强for的快捷键: forea 按 alt + / 回车

7. java.util.List子接口, 特点:
1> 有序, 也就是存储和取出的顺序一致.
2> 有索引, 也就是具体的角标
3> 可以存储重复的元素.

List接口中常见的实现类: ArrayList, LinkedList, Vector;

成员方法:
添加元素:
public boolean add(E e);
public boolean add(int index, E e);

删除元素:
public boolean remove(E e);

修改元素:
public E set(int index , E e);

查询:
public E get(int index);
public int size();

8. Vector是List接口的实现类, JDK1.0之后出来的集合.-
特点:
1> 完全符合List接口的特点,有序,有索引,可以存储重复元素
2> 线程安全,效率低

ArrayList也是List的实现类,JDK1.2之后出来
特点:
1> 完全符合List接口的特点, 有序,有索引,可以存储重复元素
2> 线程不安全,效率高
3> 底层维护的是一个数组,查询速度快, 增删速度慢.

注意: 在开发中由于效率的问题, 基本不使用Vector


9. LinkedList是List接口的实现类
特点:
1. 完全符合List接口的特点, 有序,有索引,可以存储重复元素
2. 底层是维护了一个链表, 增删速度快,查询速度慢
3. 线程不安全,效率高

额外添加了一些和头尾相关的方法:
1. public void addFirst(E e);//添加元素在开头位置
2. public void addLast(E e);// 添加元素在结尾位置
3. public E getFirst();// 获取开头位置元素
4. public E getLast();// 获取结尾位置元素
5. public E removeFirst();// 移除开头位置元素
6. public E removeLast();// 移除结尾位置元素
7. public E pop();// 弹出开头位置元素
8. public void push(E e);// 压入开头位置元素
9. public boolean isEmpty();// 判断集合是否为空

10. java.uitl.Set接口是Collection子接口;
特点:
1> 无序的, 存储和取出的顺序不能保证
2> 无索引
3> 不能存储重复元素 (存储的元素必须覆盖了equals方法和hashCode);

Set接口中常见实现类: HashSet, LinkedHashSet

注意点:
由于Set集合没有索引,不能使用普通for循环
只能使用,增强for, 和迭代器遍历!

11. HashSet,是Set接口的实现类,特点:
1> 完全符合Set接口的特点, 无序,无索引,存储元素不重复
2> 底层维护的是一个hash表, 只要使用hash表实现的地方都可以提高性能.
3> 线程不安全, 效率高.

在HashSet中的底层实现,是维护了一种hash表, hash表的本质也是一个数组.
哈希表: 是一种数据结构, 他的计算依赖于哈希值
哈希值: 通过一定的算法计算出来的.
hashSet中存储内容的时候,之所以去除重复的元素,依赖于hashCode(哈希值), equals

使用HashSet存储JDK中提供的数据String,如何去除重复的元素
因为: String类中已经覆盖了hashCode方法和equals方法

使用HashSet存储自定义数据Person对象,如何去除重复的元素
如果自定义的类,需要去除重复的元素根据具体的内容,也需要重写Object中的hashCode方法和equals方法
快捷键: alt + shift + s 按 h 自动生成即可

如果要将一个十进制的整数转成16进制,使用Integer中的, toHexString(int i)

12. java.util.LinkedHashSet是Set接口的一个实现类;
特点:
1> 有序, 存储和取出的顺序是一致的.
2> 无索引
3> 存储元素不能重复(需要存储的元素,已经覆盖了hashCode和equals方法)

注意点:
父类中没有某种功能,不代表子类一定不能有.
LinkedHashSet虽然实现了Set接口,但是它能够保证有序.


13. hashCode方法,是Object中的一个方法;
public int hashCode();

所以所有对象都可以调用hashCode方法,获取对象的哈希值.

注意点
1> 哈希值并不是地址值, 在java中是看不到对象的真正地址值的, 地址值的计算仅用到了哈希值而已.
2> 对于引用数据类型来讲, == 确实比较的是地址值.

14. ArrayList注意点: add方法, contains方法
public boolean contains(E e)
依赖于hashCode和equals方法
ArrayList可以存储重复的元素, 因为ArrayList中的add方法并没有依赖于hashCode和equals方法
ArrayList中的Contains, 依赖了hashCode和equals方法,判断哈希值和内容判断是否有这个元素.
只要是Arraylist的add方法,一定返回true

HashSet注意点, add方法, contains方法
两个方法都依赖了HashCode和Equals方法.

15. Map集合是所有双列集合的根接口,有如下特点:
1. 是以键值对的形式存储对象
2. 并且key值需要唯一
3. 值可以是多个
4. 是无序的, 存取的顺序不能保证

场景成员方法:
1. public V put(K key, V value);// 添加一组键值对,返回被替换的值.
2.public V get(K key);// 根据给定的key获取值
3.public V remove(K key);// 根据key删除键值对.
4.public void clear();//清除所有的键值对
5.public int size();// 获取集合的长度
6.public boolean containerKey(K key);//判断集合是否包含指定的key
7.public boolean containerValue(V value);// 判断集合是否包含指定的value


Map集合中常见的实现类: HashMap, LinkedHashMap, Properties

16. 如何遍历Map集合?
由于Map集合并没有继承自java.lang.Iterable接口,所以不能使用迭代器和增强for;
但是, Map集合中提供了一个方法,keySet, 可以获取到所有key的Set集合.

获取到所有键的集合方法:
public set<K> keySet();

Map集合的遍历步骤:
1> 使用ketSet方法获取到所有的键的Set集合
2> 获取到Set集合的迭代器
3> 遍历迭代器,根据key获取到对应的value

Map集合中提供了第二种遍历方式, 使用Entry内部接口的方式来实现.
Entry<K, V>: Map集合的内部接口
有两个常用成员方法:
public K getKey();
public V getValue();

使用Entry的方式来遍历Map集合的步骤:
1. 创建Map集合
2. 添加元素, put
3. 调用map集合中的entrySet(), 获取到映射关系对象(结婚证)的Set集合
4. 获取到Set集合的迭代器
5. 使用增强for循环遍历


17. HashMap集合是Map集合的实现类,特点:
1> 完全符合map集合特点(无序,键唯一,值可以多个)
2> 可以存储null和null值, 比较少使用
3> 底层是通过哈希表实现
4> 线程不安全,效率高


18. LinkedHashMap是Map集合的一个实现类, 继承自HashMap;
特点:
1> 符合Map接口的特点(除了无序之外的特点都有)
2> 有序, 存取的顺序一致
3> 双向链表和哈希表
4> 线程不安全,效率高

19. 为什么使用Map集合存储对象的时候key不能重复.
key依赖了: hashCode和equals方法

因为JDK中的String类已经覆盖了Object中的 equals方法和hashCode
如果自定义的对象,需要通过内容去判断对象是否是同一个对象,
也需要覆盖Object中的equals方法和hashCode方法

20. Properties类是Map集合的实现类, 继承自HashTable;
HashTable是JDK1.0的一个集合,线程安全,目前已经弃用;

但是他的子类Properties依然活跃在开发舞台;
Properties特点:
1> 拥有Map集合的特点
2> 类中并没有在创建对象的时候指定泛型, 默认的泛型是String
3> 可以配合IO流, 来读取或者存储文件的内容到集合.
4> 以后的时候场景: 主要是读取配置文件中的信息到集合中

* 将文件中的内容读取到集合中Properties
* public void load(Reader reader);// 使用字符流读取文件内容到集合

* 将集合Properties中的内容,存储到文件中;
* public void store(Writer writer, String comments);
* writer: 表示需要传入一个字符输出流
* comments: 就是一个描述信息

21. 可变参数: 写在方法参数当中的类型, 本质上就是一额数组

特点:
1> 格式: 数据类型 ... 变量名;// 必须是三个点,
2> 使用的使用, 传入的是同种数据类型的多个元素, 可以是0-n个
3> 作用: 就是对数组的简化使用

注意点:
1> 不能在同一个方法中写两个可变参数
2> 如果有多个参数需要传递, 那么可变参数需要写在最后面

22. Collections类, 工具类, 构造方法已经被私有化了
所以提供了一些常见的静态方法:
public static void shuffle(List<?> list);// 将List集合乱序
public static void sort(List<T> list);// 排序List集合, 默认升序, 使用的是ASCII/Unicode

补充如何降序:
需要使用方法: public static void sort(List<?>list, Comparator c);

原文地址:https://www.cnblogs.com/youyouxiaosheng-lh/p/8178986.html