Colletion接口
java.util.Collection是单集合操作的最大父接口, 在该接口中定义有所有的单值数据的处理操作
重要的操作方法
| NO | 方法名称 | 类型 | 方法 |
|---|---|---|---|
| 1 | public boolean add(E e) | 重要 | 向集合追加数据, 每次保存的肯定是单个的数据 |
| 2 | public boolean addAll(Collection <? extends E> e) | 普通 | 向集合追加一组数据 |
| 3 | public void clear() | 普通 | 清空集合, 让根节点为空, 同时执行gc处理 |
| 4 | public boolean contains(Object o) | 普通 | 查询数据是否存在, 需要equals的支持 |
| 5 | public boolean remove(Object o) | 普通 | 数据删除, 需要equals的支持 |
| 6 | public Object [] toArray() | 普通 | 将集合转成对象数组返回 |
| 7 | public Iterator<E> iterator | 重要 | 将集合变成iterator返回 |
| 8 | public int size() | 普通 | 获取集合长度 |
List
List是Collection的子接口, 其最大的特点是允许保存有大量重复元素数据
接口定义
public interface List<E> extends Collection<E>
重要的方法操作
| NO | 方法名称 | 类型 | 方法 |
|---|---|---|---|
| 1 | public E get(int index) | 重要 | 获取指定索引上的数据 |
| 2 | public E set(int index, E element) | 普通 | 修改指定索引数据 |
| 3 | public ListIterator<E> listIterator | 重要 | 返回ListIterator接口对象 |
List是一个接口, 如果要使用接口则要使用子类来完成定义, 在List中有三个常用的子类:
ArrayListVectorLinkedList
ArrayList
ArrayList是List子接口使用最多的一个子类.
类的定义
public class ArrayList<E> extends AbstractList<E> implements List<E>,
RandomAccess, Cloneable, Serializable
通过ArrayList实例化父接口List
public class JavaAPIDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("Hello");
list.add("Hello");
list.add("World");
list.add("!!!!");
System.out.println(list);
}
}
输出结果
[Hello, Hello, World, !!!!]
通过以上代码可以发现List的存储特征:
- 保存的顺序就是其存储顺序
List集合里面允许存在有重复数据
在JDK1.8以后Iterable父接口之中定义了有一个forEach()方法的输出支持(不是标准输出)
方法定义
default void forEach(Consumer<? super T> action);
利用forEach()方法输出
List<String> list = new ArrayList<String>();
list.add("Hello");
list.add("Hello");
list.add("World");
list.add("!!!!");
list.forEach(str->{
System.out.print(str + ",");
});
输出结果
Hello,Hello,World,!!!!,
LinkedList
基于链表的定义
通过LinkedList实例化父接口List
public class JavaAPIDemo {
public static void main(String[] args) {
List<String> list = new LinkedList<String>();
list.add("Hello");
list.add("Hello");
list.add("World");
list.add("!!!!");
System.out.println(list);
}
}
ArrayList和LinkedList的区别
ArrayList是数组实现的集合操作, 而LinkedList是链表实现的集合操作- 在使用
List集合中的get()方法根据索引获取数据时,ArrayList的时间复杂度是O(1), 而LinkedList的时间复杂度是O(n) ArrayList在使用的时候默认初始化对象数组的大小长度为10, 如果空间不足则采用2倍的形式进行容量的扩充, 如果保存大数据量, 则可能造成垃圾的产生, 这个时候可以使用LinkedList
Vector
Vector的定义
public class Vector<E> extends AbstractList<E> implements List<E>,
RandomAccess, Cloneable, Serializable
从定义的结构来看和ArrayList的定义完全相同.
Vector的使用
public class JavaAPIDemo {
public static void main(String[] args) {
List<String> list = new Vector<String>();
list.add("Hello");
list.add("Hello");
list.add("World");
list.add("!!!!");
System.out.println(list);
}
}
Vector类如果使用的是无参构造方法, 则一定默认开辟一个长度为10个长度的数组, 而后的实现和ArrayList基本相同, 通过源代码可以分析得出Vector类之中的操作方法都是采用synchronized同步处理, 而ArrayList并没有做同步处理, 所以Vector类之中的方法在多线程访问的时候是线程安全的, 但是性能不如ArrayList高.
Set
Set集合不允许保存重复元素. 是Collection的子接口
在JDK1.9以前Set集合和Collection集合的定义并没有明显的差别,
Set的定义
public interface Set<E> extends Collection<E>
Set无法像List那样使用get()方法, 也就是说Set无法实现指定索引的数据获取.
在
JDK1.9以后, Set也提供了类似List的of方法.
Set使用of
Set<String> all = Set.of("Hello", "World", "World");
all.forEach(System.out::println);
代码将会跑出异常, 因为保存了两个相同的元素.
在Set之中有2个常用的之类, 分别是HashSet和TreeSet
HashMap
HashMap是Set的子类中用的最多的一个子类, 其最大的特点就是保存的数据时无序的.
HshMap的类定义
public class HashSet<E> extends AbstractSet<E> implements Set<E>,
Cloneable, Serializable
HashSet应用
Set<String> all = new HashSet<all.add("Nihao");
all.add("Wrold");
all.add("Code");
all.add("Code");
all.add("Write");
System.out.println(all);
输出结果
[Write, Nihao, Code, Wrold]
HashSet是无序的HashSet无法保存相同的数据
TreeSet
与HashSet相比, TreeSet内部保存的数据是有序的.
TreeSet类定义
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>,
Cloneable, Serializable
TreeSet的使用
public static void main(String[] args) throws Exception {
Set<String> all = new TreeSet<String>();
all.add("Nihao");
all.add("Wrold");
all.add("Code");
all.add("Code");
all.add("Write");
System.out.println(all);
}
输出
[Code, Nihao, Write, Wrold]
- 没有重复
- 按照首字母的升序进行排序
TreeSet排序的本质
TreeSet本质是利用TreeMap子类实现的集合数据的存储, 而TreeMap就需要根据Comparable来确定大小关系, 重复元素的消除和排序都是利用Comparable- 如果存储的是自定义类, 那么需要手动实现
comparable, 且要讲类中所有的属性进行大小关系的匹配, 否则某一个或者某个属性相同的时候, 也会认为是相同属性. - 如果类中有较多的属性, 那么需要实现的比较多, 所以在日常开发中还是首选
HashSet
HashSet进行重复元素消除的实现
HashSet判断重复元素的方式并不是利用Comparable接口完成的, 它是利用Object内部提供的方法
- 对象编码
public int hashCode();
- 对象比较
public boolean equals(Object obj)
在进行重复元素判断的时候首先利用hashCode()进行编码的匹配, 如果该编码不存在, 则表示数据不存在, 那么不存在重复, 如果该编码存在了, 则进一步进行对象的比较处理, 如果发现重复了, 则此数据不允许保存
Iterator迭代输出
Iterator的类定义
public Iterator<T> iterator();
Iterator的接口方法
| NO | 方法名称 | 类型 | 方法 |
|---|---|---|---|
| 1 | public boolean hasNext() | 普通 | 判断是否有数据 |
| 2 | public E next()) | 普通 | 取出当前数据 |
| 3 | public default void remove() | 普通 | 删除 |
Iterator的使用
public static void main(String[] args) throws Exception {
Set<String> all = new TreeSet<String>();
all.add("Nihao");
all.add("Wrold");
all.add("Code");
all.add("Write");
Iterator iter = all.iterator();
while (iter.hasNext()) {
String str = (String) iter.next();
System.out.println(str);
}
}
注意:
在迭代的过程中, 不能够使用Collection中的remove(), 会导致迭代失败. 使用Iterator的remove()可以在迭代中删除原始的数据
Collection.remove和Iterator.remove()的区别
- 在迭代输出的时候如果使用了
Collection.remove()则会造成并发更新异常,Iterator.remove()方法实现正常的删除处理.
ListIterator双向迭代
ListIterator的操作方法
- 判断是否有前一个元素
public boolean hasPrevious();
获取当前元素
public E previous();
实现双向迭代
public static void main(String[] args) throws Exception {
List<String> all = new ArrayList<String>();
all.add("Nihao");
all.add("Wrold");
all.add("Code");
all.add("Write");
ListIterator iter = all.listIterator();
System.out.println("从前往后");
while (iter.hasNext()) {
String str = (String) iter.next();
System.out.print(str + ";");
}
System.out.println("从后往前" + ";");
while (iter.hasPrevious()) {
System.out.print(iter.previous() + ";");
}
}
Map
接口定义
public interface Map<K, V>
核心操作方法
| NO | 方法名称 | 类型 | 方法 |
|---|---|---|---|
| 1 | public V put(key, value) | 重要 | 向集合之中保存数据 |
| 2 | public V get(Object key) | 重要 | 根据key查询数据 |
| 3 | public Set<Map, Entry<K, V>> entrySet() | 重要 | 将Map集合转成Set集合 |
| 4 | public boolean containsKey(Object key) | 普通 | 查询指定的Key是否存在 |
| 5 | public Set<K> keySet() | 普通 | 将Map集合中的Key转成Set集合 |
| 6 | public V remove(Object key) | 普通 | 根据key删掉指定的数据 |
HashMap
HashMap是常用的Map子类, 是无序存储.
类的定义
public class HashMap<K, V>
extends AbstractMap<K, V>
implements Map<K, V>, Cloneable, Serializable
HashMap的使用
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("one", 1);
map.put("two", 1);
map.put("three", 1);
map.put("one", 101);
map.put(null, 1012);
map.put("five", null);
System.out.println(map.get("one"));
System.out.println(map.get("ten"));
System.out.println(map.get(null));
}
输出结果
101
null
1012
通过代码可以发现, HashMap可以通过key或者value保存空的数据, 而且保存相同的数据也不会报错, 而是新的数据替换旧的数据, 而put()方法将旧数据的value返回.
在进行 HashMap的put()操作的收, 如何实现容量扩充的?
- 在
HashMap类中, 提供了一个DEFAULT_INITIAL_CAPACITY常量, 作为初始化容量的配置, 默认大小为16个元素, 也就是说默认可以保存的最大内容为16 - 当保存的内容超过了阈值(
DEFAULT_LOAD_FACTOR=0.75f), 相当于"容量 * 阈值 = 12", 相当于保存12个元素的时候就进行容量的扩充. - 在进行扩充的时候
HashMap采用的是成倍的扩充模式, 即: 每次扩充2倍的容量
HashMap的工作原理(JDK1.8以后)
- 在
HashMap中进行数据存储的依然是利用了Node类完成的, 那么在这种情况下就可以证明可以使用的数据结构只有2种: 链表(O(n))和二叉树(O(logn)) - 从JDK1.8开始
HashMap的实现发生了改变, 因为要适应于大数据时代的海量数据, 所以对其存储发生了改变, 并且HashMap内部存在一个常量static final int TREEIFY_THRESHOLD = 8. 在使用HashMap进行数据保存的时候, 如果保存的数据个数没有超过阈值8, 会按照链表的形式进行存储, 如果超过了阈值, 那么会将链表转为红黑树以实现树的平衡, 并且利用左旋和右旋保证数据的查询性能
如果在进行HashMap的数据操作的时候, 出现了Hash冲突(Hash码相同), HashMap是怎么解决的
当出现了Hash冲突以后为了保证程序的正常执行, 会在冲突的位置上将所有的Hash冲突的内容转为链表保存
LinkedHashMap
LinkedHashMap和HashMap的区别在于LinkedHashMap数据保存的顺序就是数据添加的顺序
LinkedHashMap的定义
public class LinkedHashMap<K, V>
extends HashMap<K, V>
implements Map<K, V>
既然是链表存储, 所以一般在使用LinkedHashMap类的时候数据量不会太大, 不然会造成时间复杂度的攀升.
LinkedHashMap是HashMap的子类
LinkedHashMap的使用
public static void main(String[] args) {
Map<String, Integer> map = new LinkedHashMap<>();
map.put("one", 1);
map.put("two", 1);
map.put("three", 1);
map.put("one", 101);
map.put(null, 1012);
map.put("five", null);
System.out.println(map);
}
输出结果
{one=101, two=1, three=1, null=1012, five=null}
验证LinkedHashMap数据保存的顺序就是数据添加的顺序
Queue队列
Queue描述的是一个队列, 而队列的主要特点是实现先进先出的操作形式.
如果将队列应用在多线程的"生产者与消费者"的模型处理上, 那么对于生产者生产过快的情况下就不需要等待消费者获取数据了, 可以将所有的内容直接保存在队列之中. 队列的实现可以使用LinkedList来完成.
Queue的重要操作方法
- 向队列之中追加数据:
public boolean offer(E e), 可以直接使用add()方法 - 通过队列获取数据:
public E poll(), 弹出后删除数据
使用LinkedList实现一个队列
public static void main(String[] args) {
Queue<String> queue = new LinkedList<String>();
queue.offer("A");
queue.offer("B");
queue.offer("C");
queue.offer("D");
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
}
输出结果
A
B
C
D
除了LinkedList以外, 可以使用PriorityQueue实现优先级队列, PriorityQueue的使用和LinkedList一样
Collections
数据的全部追加
public static void main(String[] args) {
List<String> all = new ArrayList<String>();
Collections.addAll(all, "Hello", "World", "Thks");
System.out.println(all);
}
数据的翻转
public static void main(String[] args) {
List<String> all = new ArrayList<String>();
Collections.addAll(all, "Hello", "World", "Thks");
Collections.reverse(all);
System.out.println(all);
}
数据的二分查找
public static void main(String[] args) {
List<String> all = new ArrayList<String>();
Collections.addAll(all, "Hello", "World", "Thks");
// 二分查找之前必须排序
Collections.sort(all);
System.out.println(Collections.binarySearch(all, "Hello"));
}
Collection和Collections的区别
- Collection是集合接口, 允许保存单值对象
- Collections是集合操作的工具类
Stream基本操作
Stream主要是进行数据分析处理, 同时主要是针对于集合中的数据进行分析操作.
获取Stream接口对象
public default Stream<E> stream();
Stream基础操作
- 获取元素的个数
public static void main(String[] args) {
List<String> all = new ArrayList<String>();
Collections.addAll(all, "Java", "JS", "JSP", "OC", "Swift", "GO", "Ruby");
Stream<String> stream = all.stream(); // 获取Stream接口对象
System.out.println(stream.count());
}
输出
7
- 判断包含字母j(不区分大小写)的元素的个数
public static void main(String[] args) {
List<String> all = new ArrayList<String>();
Collections.addAll(all, "Java", "JS", "JSP", "OC", "Swift", "GO", "Ruby");
Stream<String> stream = all.stream(); // 获取Stream接口对象
System.out.println(stream.filter(ele->ele.toLowerCase().contains("j")).count());
}
输出结果
3
- 判断包含字母j(不区分大小写)的元素
public static void main(String[] args) {
List<String> all = new ArrayList<String>();
Collections.addAll(all, "Java", "JS", "JSP", "OC", "Swift", "GO", "Ruby");
Stream<String> stream = all.stream(); // 获取Stream接口对象
List<String> list = stream.filter(ele->ele.toLowerCase().contains("j")).collect(Collectors.toList());
System.out.println(list);
}
输出
[Java, JS, JSP]
在Stream数据流处理的过程中还允许进行数据的分页处理, 提供两个方法:
- 设置取出的最大数据量:
public Stream<T> limit(long maxSize);
- 跳过指定的数据量:
public Stream<T> skip(long n)
观察分页
public static void main(String[] args) {
List<String> all = new ArrayList<String>();
Collections.addAll(all, "Java", "JS", "JSP", "OC", "Swift", "GO", "Ruby", "JSON", "JavaScript");
Stream<String> stream = all.stream(); // 获取Stream接口对象
List<String> list = stream.filter(ele->ele.toLowerCase().contains("j")).skip(2).limit(2).collect(Collectors.toList());
System.out.println(list);
}
输出结果
[JSP, JSON]