新增(自动封装) 查询
数据在数据库在缓存
集合: 将多个数据(元素)存储在一起。也是一个容器。 理论上存储任意多个"不同数据类型"的数据。 只能存储 "引用数据类型的数据" 底层已经封装好了空间不足或者移动位置等这些操作。 //实际开发中 存储还是相同类型的数据(避免出现类型转换的异常) 数组: 一个容器。存储相同数据类型指定length个数据。 空间固定 数据类型必须相同。 数组可以存储什么类型的数据? int[] String[] Bike[] "所有的类型都可以。" 优势: 查询/修改性能最快。index 弊端: 新增/删除性能比较低。 空间固定----> 一值新增 手动扩容 数据是连贯的---->后面的数据向左移动位置
数组 VS 集合 相同点:容器。存储很多元素/数据。 不同点: 数组: 空间固定 数据类型必须相同 集合: 空间可变 数据类型可以相同
Collection VS Map
元素 |
元素是否可重复 |
|
Collection |
存储的是单值 元素(一次存储一个) |
具体看子级List/set |
Map |
存储的是一组元素(key-value) |
key必须唯一 value可重复的 |
元素是否有序(索引位置) |
元素是否可重复 |
|
List |
有序 |
可重复 |
Set |
无序 |
不可重复 |
方法 |
功能 |
boolean``add(E e)` |
将指定的数据添加集合中 |
|
清空集合里面所有的元素 |
|
判断集合里面是否包含指定的数据 |
|
判断集合是否是空 |
|
获得集合对象的迭代器对象(遍历集合元素) |
|
删除集合里面元素 |
|
获得集合元素个数 |
|
集合转数组 |
|
|
|
删除满足条件的多个元素 |
|
获得集合对象的并行化Stream对象 |
|
获得集合串行化Stream对象 |
|
(遍历集合元素) |
Iterator
boolean hasNext() 判断指针/光标后面是否有更多的数据需要迭代】 E next() 获得指针之后的元素数据 default void remove() 从底层集合中删除此迭代器返回的最后一个元素(可选操作)。
private static void demo1() { //创建集合对象 增删改查集合元素 Collection collection = new ArrayList(); //System.out.println(collection.isEmpty()); System.out.println(collection);//[] //1.新增元素---> 存储不同类型的数据 collection.add(100); collection.add("hello"); collection.add("hello"); collection.add("hello"); collection.add(true); collection.add(null); collection.add(new UserInfo()); collection.add(100.123); System.out.println(collection); System.out.println("集合的元素个数:" + collection.size()); //2.删除集合元素 /* collection.remove("hello"); collection.remove("hello"); collection.remove("hello");*/ //循环 遍历集合里面的每个元素 删除满足条件的数据 /*collection.removeIf(new Predicate() { @Override public boolean test(Object data) { return Objects.equals(data, "hello"); } });*/ //面向函数式编程----> 面向方法编程---->重写的那个方法 //lamda: ()->{} //类名/引用::方法名 //collection.removeIf(a -> Objects.equals(a, "hello")); //collection.removeIf("hello"::equals); //判断集合里面是否包含指定的数据 //System.out.println(collection.contains(100)); //清空集合元素 //collection.clear(); //集合转数组 //Object[] array = collection.toArray(); //System.out.println(Arrays.toString(array)); System.out.println(collection); System.out.println(collection.size()); }
private static void demo2() { Collection collection = new ArrayList(); collection.add(1); collection.add(10); collection.add(2); collection.add("abc"); collection.add(null); //循环遍历输出集合里面的每个元素数据 //增强for /*for(Object data:collection){ System.out.println(data); }*/ /* //1.获得集合对象的迭代器对象 Iterator it = collection.iterator();//集合里面的所有的数据都在it //2.判断指针之后是否有更多的数据需要迭代 while (it.hasNext()) { //3.获得指针之后的数据 Object data = it.next(); System.out.println(data); }*/ //1.8+ forEach /*collection.forEach(new Consumer() { @Override public void accept(Object obj) { System.out.println(obj); } });*/ //使用lamda替换上面的匿名内部类 //:: // collection.forEach(o->System.out.println(o)); collection.forEach(System.out::println); //遍历Collection: //1.增强for循环 //2. iterator() //3. forEach();
public static void main(String[] args) { //使用泛型修饰集合 //泛型+集合: 限定集合元素数据类型。 只存储一种类型的数据。 CollectionnameCollection = new ArrayList<>(); //泛型只在编译期间有效 在运行期间其实泛型还是Object---->反射 泛型擦除 nameCollection.add("张三"); nameCollection.add("李四"); nameCollection.add("王五"); nameCollection.add("赵六"); //遍历集合元素 /*for (String name : nameCollection) { System.out.println(name); }*/ //nameCollection.forEach(System.out::println); /* Iterator it = nameCollection.iterator(); while (it.hasNext()) { String str = it.next(); System.out.println(str); }*/ //nameCollection.remove("张三"); //nameCollection.removeIf("张三"::equals); System.out.println(nameCollection); //集合转数组 //无参的toArray()不推荐 返回值类型Object[] 需要进行手动类型转换 /*Object[] array1 = nameCollection.toArray(); for (Object o : array1) { String s= (String) o; }*/ // T[] toArray(T[] a); //集合转数组: 推荐数组的空间 0 //length>size 有很多null 很容易NPE 空间内存浪费 //size 0 并发的时候 多次将集合转数组 GC进行回收无用的数组引用 //length==0 String[] strings1 = nameCollection.toArray(new String[0]); System.out.println("strings1:" + Arrays.toString(strings1)); }
只能是引用数据类型。
作用: 实现类型的自动转换。
泛型修饰类 ----> 泛型类 泛型修饰方法 ----> 泛型方法 泛型修饰接口 ----> 泛型接口
@Data public class MyClass { private Integer id; private String name; //使用data维护了一个不定类型数据 private Object data; }
private static void demo1() { //创建MyClass类对象 对属性进行取值以及赋值 MyClass myClass = new MyClass(); myClass.setId(1001); myClass.setName("zhangsan"); myClass.setData(100); //每次获取数据的时候 都要手动进行类型的强制转换 //有可能会出现类型转换的异常 Integer id = myClass.getId(); String name = myClass.getName(); Integer data = (Integer) myClass.getData(); data = data + 100; MyClass myClass1 = new MyClass(); myClass1.setData("hello"); String d = (String) myClass1.getData(); }
可以满足使用data变量维护不同类型的数据。但是每次获得数据的时候 都要进行类型的强制转换 进而实现其他的功能
在这种情况下:
目标: 不使用类型的强制转换 实现类型的自动转换 。
使用泛型进行解决。
//MyClass代表是一个泛型类。T:参数化类型 //不清楚T的真实的数据类型。Object @Data public class MyClass { private Integer id; private String name; //使用data维护了一个不定类型数据 private Object data; //参数化数据类型---->指定好的规则 A-Z T E K V private T data1; //实例方法: 对象/引用.方法 //参数化到底是什么类型? 创建对象的时候 指定的参数化类型具体是什么类型 public T a(T t) { return null; } }
public static void main(String[] args) { //需求: "实现类型的自动转换" //一般类 接口是泛型类或者泛型接口 一般都会指定参数化类型 MyClassmyClass = new MyClass<>(); myClass.setData1("hello"); String data1 = myClass.getData1(); MyClass myClass1 = new MyClass<>(); myClass1.setData1(100); Integer data11 = myClass1.getData1(); }
在任意一个类里面 都可以使用泛型修饰方法。
使用参数化类型修饰方法 这个方法就是泛型方法。
//K:是一个参数化类型 不指定 默认Object public class FanXingClass{ private K a; //只是参数化类型 充当形参和返回值 //K类型是固定的 只能是一种 创建对象的时候 指定的数据类型 public K getA() { return a; } public void setA(K a) { this.a = a; } //泛型方法 //T: 不定类型 //K: 创建对象的时候类型 m1是一个实例方法 public void m1(T abc, K bb) { } //静态方法 //是否可以使用参数化数据类型? 创建对象的时候指定参数化类型 //在泛型类里面 提供了很多静态的方法 里面使用参数化类型 这些静态方法都是"泛型方法" public static void m2(K aaa) { } //一个泛型类里面 有很多的静态的方法 这个类一般都是工具类 用来封装一些的数据。 }
//后期有很多模块: User Product // 新增 删除 修改 查询 //T 就是 User Product.... class User { } class Product { } public interface BaseDao{ void add(T t); void delete(); void update(); T findOne(); Collection findAll(); } class ProductDaoImpl implements BaseDao { @Override public void add(Product product) { } @Override public void delete() { } @Override public void update() { } @Override public Product findOne() { return null; } @Override public Collection findAll() { return null; } } class UserDaoImpl implements BaseDao { @Override public void add(User user) { } @Override public void delete() { } @Override public void update() { } @Override public User findOne() { return null; } @Override public Collection findAll() { return null; } }
//使用 T extends 具体类型 规定参数化类型的上限 //只能:Number以及Number类型所有的子类 public class DemoTest{ //?是一个通配符 统配任意一个类型 //下限: ?只能是String以及String的父级类型。 public static void a(Collection super String> collection) { } } class Test3 { public static void main(String[] args) { Collection collection = new ArrayList<>(); DemoTest.a(collection); } }
有序可重复。 index: 0-size()-1
数据结构 |
性能 |
安全 |
|
ArrayList |
动态数组 |
查询/修改最快 新增/删除慢 |
不安全 |
LinkedList |
双向链表 |
新增/删除最快 查询/修改慢 |
不安全 |
Vector |
动态数组 |
一般 |
安全 sync |
CopyOnWriteArrayList |
动态数组 |
很慢 |
安全 sync |
链表: 单向链表: 一个元素的引用会“记着下一个元素引用” 以及当前元素数据 双向链表: 当前元素的引用会“记着下一个元素引用” 还会记着“上一个元素的引用” 以及当前元素数据
方法 |
作用 |
|
指定索引位置新增新的数据 |
|
获得指定索引数据 |
|
遍历list集合 |
|
删除指定索引的元素数据 |
|
修改指定索引的元素数据 |
|
排序 |
|
截取 |
public class ArrayListextends AbstractList implements List , RandomAccess, Cloneable, Serializable RandomAccess: 快速随机访问接口 标记接口 查询性能最快 O(1) 使用普通循环遍历方式 性能最快。
ArrayList() 构造一个初始容量为十的空列表。 array.length ArrayList(int initialCapacity) //推荐 10 initialCapacity: 存储集合元素个数/负载因子+1 扩容 ArrayList(Collection extends E> c)
transient Object[] elementData;//就是存储ArrayList集合元素数据 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
protected transient int modCount = 0;//维护操作集合元素次数。(add/remove) private int size;//维护集合元素的个数 private static final int DEFAULT_CAPACITY = 10;//默认容量 public boolean add(E e) { modCount++; add(e, elementData, size); return true; } private void add(E e, Object[] elementData, int s) { if (s == elementData.length)//size的数据超出数组length的时候 elementData = grow(); elementData[s] = e; size = s + 1; } private Object[] grow() { return grow(size + 1);//1 } private Object[] grow(int minCapacity) {//1 return elementData = Arrays.copyOf(elementData, newCapacity(minCapacity)); } private int newCapacity(int minCapacity) {//1 // overflow-conscious code int oldCapacity = elementData.length;//获得原数组的长度 0 int newCapacity = oldCapacity + (oldCapacity >> 1); //计算新数组的容量 1.5 if (newCapacity - minCapacity <= 0) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) return Math.max(DEFAULT_CAPACITY, minCapacity); if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return minCapacity; } return (newCapacity - MAX_ARRAY_SIZE <= 0) ? newCapacity : hugeCapacity(minCapacity); }
E elementData(int index) { return (E) elementData[index]; }
public E set(int index, E element) { Objects.checkIndex(index, size); E oldValue = elementData(index); elementData[index] = element; return oldValue; }
public boolean remove(Object o) { final Object[] es = elementData; final int size = this.size; int i = 0; found: {//找指定元素所在的索引位置 if (o == null) { for (; i < size; i++) if (es[i] == null) break found; } else { for (; i < size; i++) if (o.equals(es[i])) break found; } return false; } fastRemove(es, i);//带着index 带着数组 快速删除元素数据 return true; }
private void fastRemove(Object[] es, int i) { modCount++; final int newSize; if ((newSize = size - 1) > i) System.arraycopy(es, i + 1, es, i, newSize - i);//真正删除操作 es[size = newSize] = null;//Gc回收无用对象 }
元素 数据 节点
public class LinkedListextends AbstractSequentialList implements List , Deque , Cloneable, Serializable
LinkedList()
public boolean add(E e) { linkLast(e); return true; } transient Nodelast;//null transient Node first;//null transient int size = 0; void linkLast(E e) {//100 final Node l = last;//null final Node newNode = new Node<>(l, e, null); last = newNode; if (l == null)//第一次新增数据的时候 将第一个节点对象赋值给first first = newNode; else l.next = newNode; size++; modCount++; }
手动编写双向链表。
private static class Node{//维护LinkedList底层的双向链表的数据结构 E item;//数据 Node next;//下一个节点的引用 Node prev;//上一个节点的引用 Node(Node prev, E element, Node next) { this.item = element; this.next = next; this.prev = prev; } }
void linkBefore(E e, Nodesucc) {// e 新的元素数据 succ: 指定索引的节点对象 // assert succ != null; final Node pred = succ.prev;//获得指定索引对象的上一个 final Node newNode = new Node<>(pred, e, succ); succ.prev = newNode; if (pred == null)//指定索引为0的时候 first = newNode; else pred.next = newNode; size++; modCount++; }
Nodenode(int index) { // assert isElementIndex(index); if (index < (size >> 1)) { Node x = first; for (int i = 0; i < index; i++) x = x.next; return x; } else { Node x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; } }
public E set(int index, E element) { checkElementIndex(index); Nodex = node(index); E oldVal = x.item; x.item = element; return oldVal; }
public boolean remove(Object o) { if (o == null) { for (Nodex = first; x != null; x = x.next) { if (x.item == null) { unlink(x); return true; } } } else { for (Node x = first; x != null; x = x.next) { if (o.equals(x.item)) { unlink(x);//解除节点与节点之间的关系 return true; } } } return false; }
//将元素添加到首尾 linkedList.add(0, 101); linkedList.addFirst(1); linkedList.addLast(400); linkedList.push(1111); //获得链表首尾元素 System.out.println(linkedList.get(0));// Node.item System.out.println(linkedList.getFirst()); System.out.println(linkedList.getLast()); System.out.println(linkedList.element()); System.out.println(linkedList.peek()); System.out.println(linkedList.peekFirst()); System.out.println(linkedList.peekLast()); //删除首尾元素 System.out.println(linkedList.remove()); System.out.println(linkedList.removeFirst()); System.out.println(linkedList.removeLast()); System.out.println(linkedList.poll()); System.out.println(linkedList.pollFirst()); System.out.println(linkedList.pollLast()); System.out.println(linkedList.pop());
线程安全。 synchronized
package com.javasm; import java.util.ArrayList; import java.util.List; import java.util.Vector; import java.util.concurrent.TimeUnit; /** * @author: Lisa * @className: VectorDemo * @description: * @date: 2022/3/2 16:11 * @version: 0.1 * @since: jdk11 */ public class VectorDemo { //使用一个成员变量维护存储很多数据 //每个线程共同操作同一个集合对象 private static Vectorlist = new Vector<>(); public static void main(String[] args) { //并发环境 //创建10个线程 都操作同一个集合对象 list List threadList = new ArrayList<>(10); for (int i = 0; i < 10; i++) { threadList.add(new Thread(() -> { for (int j = 0; j < 1000; j++) { list.add(1); } })); } threadList.forEach(Thread::start); threadList.forEach(thread -> { try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } }); //等待着前面10个子线程执行完毕---->等待着子线程死亡 //获得集合里面的元素个数 //让主线程休眠 5 System.out.println(list.size());//10000 } }
public boolean add(E e) { synchronized (lock) {//底层安全的实现方式 Object[] es = getArray(); int len = es.length; es = Arrays.copyOf(es, len + 1); es[len] = e; setArray(es); return true; } }
遍历期间不能删除。
常用的数据结构: 数组 链表 哈希表 红黑树
数据结构 |
元素是否可以为null |
线程安全 |
|
HashSet |
哈希表(底层就是HashMap 位桶+哈希+红黑树) |
ok |
不安全 |
LinkedHashSet |
链表+哈希表(LinkedHashMap) |
ok |
不安全 |
TreeSet |
红黑树(TreeMap) |
不可以 |
不安全 |
CopyonWriteArraySet |
数组 |
不可以 |
安全 |
Set接口里面的方法与父接口Collection的功能一模一样。
常用构造
HashSet() 构造一个新的空集合; 背景HashMap实例具有默认初始容量(16)和负载因子(0.75)。 HashSet(int initialCapacity) 构造一个新的空集合; 背景HashMap实例具有指定的初始容量和默认负载因子(0.75)。
元素特征:
private static void testHashSet() { HashSetset = new HashSet<>(); set.add(50); set.add(3); set.add(1); set.add(10); set.add(2); //无序: 元素没有索引 新增的顺序与遍历的顺序不一致的 System.out.println(set); //遍历set集合 /*for (Integer num : set) { System.out.println(num); }*/ /* Iterator it = set.iterator(); while (it.hasNext()) { System.out.println(it.next()); }*/ set.forEach(System.out::println); }
常用构造
LinkedHashSet(int initialCapacity) 构造一个具有指定初始容量和默认负载因子(0.75)的新的,空的链接散列集。
元素特征: 元素有序 新增顺序与遍历输出顺序是一致。
private static void testLinkedHashSet() { LinkedHashSetset = new LinkedHashSet<>(); set.add(50); set.add(3); set.add(1); set.add(10); set.add(2); set.add(2); set.add(null); System.out.println(set); }
TreeSet集合元素的数据类型 必须有排序规则。
1.保证集合元素数据类型实现Comparable接口 内部比较器
2.自定义提供排序规则 实现Comparator 外部比较器
常用构造
TreeSet() TreeSet(Comparator super E> comparator)
元素特征: 元素有序。 都会按照自然顺序排列。(升序/降序)
Comparable解决排序:
//类实现Comparable: 类与接口的耦合比较高 一般不使用内部比较器。推荐使用外部比较器 public class UserInfo implements Comparable{ @Override public int compareTo(UserInfo o) { return this.age-o.age; } }
TreeSetuserInfoSet = new TreeSet<>(); userInfoSet.add(new UserInfo(1, "张三", 20)); userInfoSet.add(new UserInfo(2, "李四1", 180)); userInfoSet.add(new UserInfo(3, "李四1", 181)); userInfoSet.add(new UserInfo(4, "李四1", 118)); userInfoSet.add(new UserInfo(5, "李四1", 108)); userInfoSet.forEach(System.out::println);
Comparator解决排序:
public class UserInfo {}//类很单一
private static void testSet1() { //TreeSet: 集合元素数据类型必须实现Comparable 排序 //数据唯一的问题: 只看排序的规则(哪个属性参与排序 元素唯一就看那个属性)。 与重写equals+hashcode没有关系。底层的数据结构是树结构。 /*TreeSetuserInfoSet = new TreeSet<>(new Comparator () { @Override public int compare(UserInfo o1, UserInfo o2) { return o2.getAge()-o1.getAge(); } });*/ //TreeSet userInfoSet = new TreeSet<>((user1,user2)->user1.getAge()-user2.getAge()); /* TreeSet userInfoSet = new TreeSet<>(Comparator.comparing(new Function () { @Override public Integer apply(UserInfo userInfo) { return userInfo.getAge(); } }).reversed());*/ TreeSet userInfoSet = new TreeSet<>(Comparator.comparing(UserInfo::getAge).reversed()); userInfoSet.add(new UserInfo(1, "张三", 20)); userInfoSet.add(new UserInfo(2, "李四1", 180)); userInfoSet.add(new UserInfo(3, "李四1", 181)); userInfoSet.add(new UserInfo(4, "李四1", 118)); userInfoSet.add(new UserInfo(5, "李四1", 108)); userInfoSet.forEach(System.out::println); }
public boolean addIfAbsent(E e) { Object[] snapshot = getArray(); return indexOfRange(e, snapshot, 0, snapshot.length) < 0 && addIfAbsent(e, snapshot); }
存储的是一组数据。 key---value key值必须唯一 value可以重复的。
在开发中: key的类型一般就是String/Integer/Long
static interface Map.Entry维护了map集合里面的每组数据。
方法 |
作用 |
|
新增一组元素 |
|
根据key删除value 没有找到key返回null |
|
根据指定的key/value删除元素 |
|
根据指定的key修改value |
|
根据指定的key/value,使用新的value进行修改 |
|
根据key获得value |
|
根据key获得value 找不到特定的key 返回值的是默认值 |
|
遍历map集合元素 |
|
获得map集合里面 每组数据 都存储set集合中 |
|
获得map里面所有的key |
|
private static void demo1() { //创建map集合对象 //使用map集合维护一个用户完整信息 id=20 name=张三 age=20 pass=1234 MapuserInfoMap = new LinkedHashMap<>(); //1.新增数据----> key值重复 等价新值会覆盖旧的数据 userInfoMap.put("id", 1001); userInfoMap.put("name", "张三"); userInfoMap.put("age", 20); userInfoMap.put("pass", "1234"); //2.删除 根据key删除 key是唯一 //System.out.println(userInfoMap.remove("pass")); //System.out.println(userInfoMap.remove("pass", "12345")); //3.修改 //System.out.println(userInfoMap.replace("pass", "12345")); //System.out.println(userInfoMap.replace("pass", "1234","12345")); //4.查询 //System.out.println(userInfoMap.get("name1")); //前提: 集合存储相同类型的数据 //System.out.println(userInfoMap.getOrDefault("name1", "")); //5.判断 System.out.println(userInfoMap.isEmpty()); System.out.println(userInfoMap.size()); System.out.println(userInfoMap.containsKey("name")); System.out.println(userInfoMap); }
private static void demo2() { MapuserInfoMap = new LinkedHashMap<>(); userInfoMap.put("id", 1001); userInfoMap.put("name", "张三"); userInfoMap.put("age", 20); userInfoMap.put("pass", "1234"); //遍历map集合元素 /* userInfoMap.forEach(new BiConsumer () { @Override public void accept(String key, Object value) { System.out.println(key + "----" + value); } });*/ //userInfoMap.forEach((key, value) -> System.out.println(key + "----" + value)); //jdk1.8之前的遍历方式 /*Set > entrySet = userInfoMap.entrySet();//推荐 //将map集合里面的每组数据都依次的封装成一个个的entry对象 再将entry对象存储set集合中 for (Map.Entry entry : entrySet) {// map集合中每组数据 都在entry里面 String key = entry.getKey(); Object value = entry.getValue(); System.out.println(key + "----" + value); }*/ /* Set keySet = userInfoMap.keySet();//获得map集合里面每个key 存储set集合中 for (String key : keySet) { System.out.println(key + "----" + userInfoMap.get(key)); }*/ }
private static void demo3() { String str = "djhdf77gggaf6t3gdshgfsa"; //使用map集合实现: 统计每个字符出现的次数 a=2 Mapmap = new HashMap<>(); //1.获得字符串里面的每个字符 int length = str.length(); for (int index = 0; index < length; index++) { String s = String.valueOf(str.charAt(index));//获得每个字符数据 // V merge(K key, V value, BiFunction super V, ? super V, ? extends V> remappingFunction) map.merge(s, 1, Integer::sum); /*//判断这个s字符是否存在于map集合中 Integer num = map.get(s); if (num != null) { num += 1; } else { num = 1; } //第一次存储字符 map.put(s, num); //之前存储过相同的字符 获得之前的次数+1*/ } System.out.println(map); }
数据结构 |
key以及value是否可以为null |
线程安全 |
|
HashMap |
位桶+链表+红黑树 |
k/v都可以为null |
否 |
LinkedHashMap |
链表+哈希表 |
都可以为null |
否 |
TreeMap |
红黑树 |
key:不能为null value:可以 |
否 |
HashTable |
哈希表 |
都不能为null |
是 syn |
ConcurrentHashMap |
哈希表 |
都不能为null |
是 CAS |
HashMap() 构造一个空的 HashMap ,默认初始容量(16)和默认负载系数(0.75)。 HashMap(int initialCapacity) 构造一个空的 HashMap具有指定的初始容量和默认负载因子(0.75)。
static final float DEFAULT_LOAD_FACTOR = 0.75f; final float loadFactor;//0.75 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;//16 static final int MAXIMUM_CAPACITY = 1 << 30; static final int TREEIFY_THRESHOLD = 8;//链表转数结构的阈值 static final int UNTREEIFY_THRESHOLD = 6;//树结构转链表结构 static final int MIN_TREEIFY_CAPACITY = 64;//链表转数结构的一个条件 数组的length transient Node[] table;//位桶 数组 int threshold;//执行再次扩容的阈值 0 12 public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted }
解决hashmap的hash冲突的方案: 1. hash一致 key值得数据一致 新的value会覆盖掉oldvalue 2. hash一致 key的数据不同 使用单向链表解决。 链表元素个数>=TREEIFY_THRESHOLD-1的时候 && length>64 将链表结构转树结构 3.使用树结构TreeNode解决。
public V put(K key, V value) {// 1 1 return putVal(hash(key), key, value, false, true); }
//计算key的哈希值 采取的方式高16位的位运算 目的: 减少hash的碰撞 static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node[] tab; Node p; int n, i; // n就是的数组长度 length i维护索引值 if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((p = tab[i = (n - 1) & hash]) == null)//采取位运算求索引 tab[i] = newNode(hash, key, value, null); else {//解决hash冲突的问题 Node e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p;// key的hash一致 且key的数据一致 else if (p instanceof TreeNode) e = ((TreeNode )p).putTreeVal(this, tab, hash, key, value); else { //key的hash是一样的 但是key的数据不同的 //使用单向链表解决hash冲突的问题 for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); //链表里面的元素个数>=7 数组的length>=64 //会将链表结构的数据转换成树结构的数据 break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value;//新的value覆盖旧的value afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold)//新增元素的个数>阈值 执行扩容 resize(); afterNodeInsertion(evict); return null; }
第一次执行put的时候。
final Node[] resize() { Node [] oldTab = table;//旧数组 int oldCap = (oldTab == null) ? 0 : oldTab.length;//旧容量 int oldThr = threshold;//旧阈值 int newCap, newThr = 0; if (oldCap > 0) { if (oldCap >= MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return oldTab; } else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) newThr = oldThr << 1; // double threshold } else if (oldThr > 0) // initial capacity was placed in threshold newCap = oldThr; else { // zero initial threshold signifies using defaults //第一次put的时候 执行else逻辑 newCap = DEFAULT_INITIAL_CAPACITY;//16 newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);//12 } if (newThr == 0) { float ft = (float)newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE); } threshold = newThr; @SuppressWarnings({"rawtypes","unchecked"}) Node [] newTab = (Node [])new Node[newCap]; table = newTab; if (oldTab != null) { for (int j = 0; j < oldCap; ++j) { Node e; if ((e = oldTab[j]) != null) { oldTab[j] = null; if (e.next == null) newTab[e.hash & (newCap - 1)] = e; else if (e instanceof TreeNode) ((TreeNode )e).split(this, newTab, j, oldCap); else { // preserve order Node loHead = null, loTail = null; Node hiHead = null, hiTail = null; Node next; do { next = e.next; if ((e.hash & oldCap) == 0) { if (loTail == null) loHead = e; else loTail.next = e; loTail = e; } else { if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null); if (loTail != null) { loTail.next = null; newTab[j] = loHead; } if (hiTail != null) { hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } } return newTab; }
在HashMap里面 何时会执行扩容?
1. put: 1. 第一次put的时候 初始化数组空间 2. size>threhold 会执行再次扩容 2. 链表转树结构的时候: treefyBin() 扩容: 2倍
是jdk1.8+之后新特性。作用: 操作集合Collection元素。聚合数据
使用lamda的原因: 简化 匿名内部类(接口)操作、接口必须是个@FunctionalInterface
private static void demo1() { //1.获得集合对象Collection Listlist = List.of(1, 2, 3, 4); //2.获得Stream的实例 //Collection集合的方法 Stream stream = list.parallelStream();//并行化流 Stream stream1 = list.stream();//串行化stream //并行 VS 串行 //计算机计算的能力。 //案例: 1000个数字 求最大值。 //串行: 类似于冒泡排序 max 有序 //并行: 同时执行多个任务。 将1000个数据 放到多个任务中(多个线程)。 //10 每个任务: 随机分配100个数据 10个最值 //Stream stream2 = Stream.of(1, 2, 3, 4); //随机数 Random Random random = new Random(); //随机获得5个随机的数字 1000-10000 IntStream intStream = random.ints(5, 1000, 10000); //提供对于基本数据类型封装Stream---> 减少过多的拆箱+装箱 intStream.forEach(System.out::println); //Arrays Stream stream2 = Arrays.stream(new Integer[]{1, 2, 3}); }
private static void demo2() { Listlist = new ArrayList<>(1000); for (long i = 10000000; i >= 0; i--) { list.add(i); } //需求: 对list集合元素去重并升序/降序排列 long begin1 = System.nanoTime(); // TreeSet treeSet = new TreeSet<>(); // list.forEach(treeSet::add); long begin2 = System.nanoTime(); // System.out.println(begin2 - begin1);//3709702827 1036846933 //Stream类似于Iterator //parallelStream 性能没有 stream 快 begin1 = System.nanoTime(); //Stream stream = list.stream();//list集合里面的数据都在stream //List list1 = stream.distinct().sorted().collect(Collectors.toList());//使用Stream 聚合数据 List list1 = list.stream().sorted().distinct().collect(Collectors.toList()); //是否可以再次使用stream? 无法使用第二次 //System.out.println(stream.count()); //获得运算之后的结果 begin2 = System.nanoTime(); System.out.println(begin2 - begin1); }
private static void demo4() { Listlist = List.of(10, 20, 40, 60, 60, 50); //留下来集合元素>30的数据 存储到另外一个集合里面 List newList = new ArrayList<>(10); for (Integer num : list) { if (num > 30) { newList.add(num); } } System.out.println(newList); //Stream.filter 留下来满足条件的数据 /*list.parallelStream().filter(new Predicate () { @Override public boolean test(Integer num) {//stream每个元素 return num>30; } })*/ List collect = list.parallelStream().filter(num -> num > 30).distinct().sorted().collect(Collectors.toList()); System.out.println(collect); }
private static void demo5() { ListuserInfoList = new ArrayList<>(10); Collections.addAll(userInfoList, new UserInfo(1, 20, "jim"), new UserInfo(2, 20, "tom"), new UserInfo(3, 20, "zhangsan") ); //查询名称里面包含m一个信息 List collect = userInfoList.parallelStream().filter(userInfo -> userInfo.getName().contains("m")).collect(Collectors.toList()); System.out.println(collect); }
一对一
private static void demo6() { ListnameList = Arrays.asList("jim", "tom", "zhangsan", "lily", "lilei", "hanmeimei"); //将集合里面的每个元素都装大写 /*List upperNameList = new ArrayList<>(10); for (String name : nameList) { upperNameList.add(name.toUpperCase()); } System.out.println(upperNameList);*/ //Stream 一对一: Stream里面的每个元素都要执行相同的逻辑 /*nameList.parallelStream().map(new Function () { @Override public String apply(String s) { return s.toUpperCase(); } });*/ //List collect = nameList.parallelStream().map(String::toUpperCase).collect(Collectors.toList()); List collect = nameList.parallelStream().peek(new Consumer () { @Override public void accept(String s) { s = s.toUpperCase(); System.out.println(s); } }).collect(Collectors.toList()); System.out.println(collect); }
private static void demo7() { ListuserInfoList = new ArrayList<>(10); Collections.addAll(userInfoList, new UserInfo(1, 21, "jim"), new UserInfo(2, 22, "tom"), new UserInfo(3, 23, "zhangsan") ); //所有人年龄+1 /* List
多个Stream转换成 一个Stream
private static void demo8() { Listlist1 = List.of(1, 3, 5, 3, 8, 10); List list2 = List.of(11, 32, 53, 3); List list3 = List.of(100, 37, 58, 39, 8, 10); //3个数据源 //将3个集合里面的元素都存储到一个集合中(去重/排序) //"将3个集合里面的所有的数据都放在一个Stream" //多个数据源数据存储到一个Stream /*Stream.of(list1, list2, list3).flatMap(new Function , Stream>>() { @Override public Stream> apply(List
list) { return list.parallelStream(); } })*/ List collect = Stream.of(list1, list2, list3).flatMap(List::parallelStream).distinct().sorted().collect(Collectors.toList()); System.out.println(collect); }
public class Demo { private static Listfamily; static { family = new ArrayList<>(10); //parentId=0 代表这个人就是顶级 family.add(new Person(1, "张三丰", null, 0)); family.add(new Person(2, "张翠山", null, 1)); family.add(new Person(3, "张翠翠", null, 1)); family.add(new Person(4, "张无忌", null, 2)); family.add(new Person(5, "张天真", null, 2)); family.add(new Person(6, "张翠花", null, 3)); family.add(new Person(7, "张二狗", null, 3)); family.add(new Person(8, "张三", null, 5)); family.add(new Person(9, "张三四", null, 0)); family.add(new Person(10, "张1", null, 9)); } public static void main(String[] args) { //查询父节点下面的所有的子级的成员 /* List personList = family.parallelStream() .filter(person -> person.getParentId().equals(0))//person集合里面的每个元素 .peek(parent -> parent.setChild(getChild(parent)))//person1代表就是过滤转换parentId=0的对象 .collect(Collectors.toList()); personList.forEach(System.out::println);*/ List list = new ArrayList<>(10); for (Person person : family) { if (person.getParentId().equals(0)) { person.setChild(getChildList(person)); list.add(person); } } list.forEach(System.out::println); //找孩子---->对child属性赋值 } private static List getChildList(Person parent) { List list = new ArrayList<>(10); for (Person person : family) { if (person.getParentId().equals(parent.getId())) { person.setChild(getChildList(person)); list.add(person); } } return list; } private static List getChild(Person parent) { return family.parallelStream() .filter(person -> person.getParentId().equals(parent.getId()))//过滤下来二级节点的数据 .peek(p -> p.setChild(getChild(p)))//递归 .collect(Collectors.toList()); } }
public static void main(String[] args) { //1. List VS Set //list: 有序可重复 //set:无序不可重复 //2 List集合常用的实现类? 区别? //ArrayList: 动态数组 查询/修改快 新增/删除慢 不安全 //LinkedList: 双向链表 add/delete 快 get/set慢 不安全 //Vector: 动态数组 一般 安全 //3.Set集合常用的实现类? 区别? //HashSet ----> 底层就是一个HashMap 元素可以为null //LinkedHashSet---->底层就是一个LinkedHashMap 元素有序(新增顺序与遍历顺序一致) //TreeSet---->底层就是一个TreeMap 元素不可以为null(比较排序) //4. Map集合常用的实现类? 区别? //HashMap: 数组+链表+树 k/V可以为null 不安全 //LinkedHashMap: 数组+链表+树 k/V可以为null 不安全 //TreeMap:树 K不能为null v可以为null 不安全 //HashTable: 哈希表 K/V都不能为null 安全 同步方法 //ConcurrentHashMap: 哈希表 K/V都不能为null 安全 CAS+同步代码块 //5.HashMap底层是如何解决hash冲突的?(key的hash是一样的) //5.1 key的hash是一样 key的数据一致的 新的value值覆盖旧value put //5.2 key的hash是一样 key的数据不同的 // 挂在节点的next为null值得那个地方。p.next = new Node(key,hash,value,next) // 链表转树的前提: TREEIFY_THRESHOLD && MIN_TREEIFY_CAPACITY //5.3 直接使用TreeNode解决hash冲突的问题 //6.HashMap底层的扩容机制如何实现? resize //new HashMap() 第一次put的时候 扩容 默认容量16 threshold = 0.75*16 //再次扩容: table= 2*oldCap threshold=2*oldThr //对之前旧数组里面的所有的元素执行存储: 基于提高性能 减少hash碰撞问题 //假设有一个元素 100-next(null) 扩容之后 还是存储在原索引 //next不为null 证明是链表 hash碰撞可能会产生很多次 将链表元素里面的数据分成2部分进行处理 一部分在lowIndex 一部分在HiIndex //lowIndex还是旧数组里面的原索引值 HiIndex=lowIndex+oldCap //7.HashMap何时会进行树结构转链表? //在resize的时候 进行对树结构的数据进行拆分的时候 在split的方法中 //判断树结构里面TreeNode的节点的对象个数是否<=UNTREEIFY_THRESHOLD(6) 调用untreeify方法 将树结构转链表 //8.HashMap里面重要的几个属性? //loadFactor=0.75 //..... }
王者荣耀: 英雄: id name 语录 List<皮肤> price 分类 //上架 优化(修改) 查看单个 查询所有英雄信息 //查询指定类别里面所有的英雄 皮肤: id name price 皮肤关联英雄 皮肤状态 // 查询指定英雄的所有皮肤 // 上架 下架 查看所有皮肤
public class CollectionDemo { public static void main(String[] args) { demo1(); } private static void demo3() { Collection collection = new ArrayList(); collection.add(1); collection.add(10); collection.add(2); collection.add("abc"); //在字符串的数据里面 拼接 “aaa” for (Object data : collection) { if (data instanceof String) { //先判断,满足要求再转换以及拼接 //调用concat String s = (String) data; System.out.println(s.concat("aaa")); } } } private static void demo2() { Collection collection = new ArrayList(); collection.add(1); collection.add(10); collection.add(2); collection.add("abc"); collection.add(null); //循环遍历输出集合里面的每个元素数据 //增强for /*for(Object data:collection){ System.out.println(data); }*/ /* //1.获得集合对象的迭代器对象 Iterator it = collection.iterator(); //集合里面的所有的数据都在it迭代器里 //2.判断指针之后是否有更多的数据需要迭代 while (it.hasNext()) { //迭代器iterator里的功能方法hasNext(),判断光标/指针后面是否有更多元素需要迭代 //3.获得指针之后的数据 Object data = it.next(); //迭代器iterator里的功能方法next(),返回值为迭代中的下一个元素 System.out.println(data); }*/ //1.8之后的新方法(父接口Iterable提供的) forEach /*collection.forEach(new Consumer() { //集合的引用调forEach方法,形参类型Consumer(函数式接口) @Override public void accept(Object obj) { System.out.println(obj); } });*/ //使用lamda替换上面的匿名内部类 // collection.forEach(o->System.out.println(o)); //类名/引用::方法 这里方法是println(打印并换行) 是System.out调的该方法 collection.forEach(System.out::println); //遍历Collection: //1.增强for循环 //2. iterator() //3. forEach(); /*System.out.println(collection.size()); System.out.println(collection.contains("abc")); //System.out.println(collection.remove(1)); collection.removeIf(obj->Integer.valueOf(1).equals(obj)); System.out.println(collection);*/ } private static void demo1() { //创建集合对象 增删改查集合元素 Collection collection = new ArrayList(); //System.out.println(collection.isEmpty()); System.out.println(collection); //[] //1.新增元素---> 存储不同类型的数据 collection.add(100); collection.add("hello"); collection.add("hello"); collection.add("hello"); collection.add(true); collection.add(null); collection.add(new UserInfo()); collection.add(100.123); //Double类型 System.out.println(collection); //[100, hello, hello, hello, true, null, com.javasm.jihe.UserInfo@5594a1b5, 100.123] System.out.println("集合的元素个数:" + collection.size()); //2.删除集合元素 /* collection.remove("hello"); //用一下,删除一下 返回类型boolean,有就删,没有返false collection.remove("hello"); collection.remove("hello");*/ //removeIf 删除满足条件的多个数据(需要自定义规则) 底层还是循环遍历每个元素,删除满足条件的元素 /*collection.removeIf(new Predicate() { //removeIf的形参类型Predicate,一个函数式接口 @Override public boolean test(Object data) { //data是集合里的每个数据 return Objects.equals(data, "hello"); //不要用data.equals(),data可能为空值,产生异常 } });*/ //面向函数式编程----> 面向方法编程---->重写的那个方法 //lamda: ()->{} //类名/引用::方法名 //collection.removeIf((Object a)->{return Objects.equals(a, "hello");}); //删除大括号,return 删除小括号,Object //collection.removeIf(a -> Objects.equals(a, "hello")); //重写的方法的形参类型Object和返回值类型boolean 要与调的Objects.equals(data, "hello");所一致,这里两个参数 //改成collection.removeIf(a -> "hello".equals(a)); 其返回值类型和形参与重写的一样,可再优化。 类名/引用::方法 //collection.removeIf("hello"::equals); //"hello"调的方法 //判断集合里面是否包含指定的数据 //System.out.println(collection.contains(100)); //清空集合元素 //collection.clear(); //集合转数组 //Object[] array = collection.toArray(); //System.out.println(Arrays.toString(array)); System.out.println(collection); System.out.println(collection.size()); } }
public class UserInfo { }
@Setter @Getter public class MyClass{ //一个字母,一个参数化类型,不指定,默认Object private Integer id; private String name; //使用data维护了一个不定类型数据,但每次拿的时候都要进行类型转换,看demo1() private Object data; //类型不定的数据类型,一般叫参数化数据类型(提前制定好的规则,类比形参) //如下,用泛型去维护data private T data1; //实例方法: 对象/引用.方法 //参数化到底是什么类型? 创建对象的时候 指定的参数化类型具体是什么类型 public T a(T t) { //这并非是泛型方法,没有标识<字母> 类型T(参数化数据类型),就看对象调它时再确定 return null; } } //何时使用泛型? //封装数据---->数据类型是不定的. class Test { public static void main(String[] args) { //需求: "实现类型的自动转换" 这里用到泛型 //一般类,接口是泛型类或者泛型接口,一般都会指定参数化类型,如下: MyClass myClass = new MyClass<>(); myClass.setData1("hello"); String data1 = myClass.getData1(); MyClass myClass1 = new MyClass<>(); myClass1.setData1(100); Integer data11 = myClass1.getData1(); } private static void demo1() { //创建MyClass类对象 对属性进行取值以及赋值 MyClass myClass = new MyClass(); myClass.setId(1001); //对Id赋值 myClass.setName("zhangsan"); myClass.setData(100); //每次获取数据的时候 都要手动进行类型的强制转换 //有可能会出现类型转换的异常 Integer id = myClass.getId(); String name = myClass.getName(); Integer data = (Integer) myClass.getData(); //这里类型强转 data = data + 100; MyClass myClass1 = new MyClass(); myClass1.setData("hello"); String d = (String) myClass1.getData(); } }
泛型方法
public class FanXingClass{ //K:是一个参数化类型 不指定 默认Object private K a; public K getA() { //只是参数化类型,充当形参和返回值. K类型是固定的,只能是一种,创建对象的时候,指定的数据类型 return a; } public void setA(K a) { this.a = a; } public void m1(T abc, K bb) { //泛型方法 K: 创建对象的时候类型 m1是一个实例方法 } public static void m2(K aaa) { //在泛型类里面 提供了很多静态的方法 里面使用参数化类型 这些静态方法都是"泛型方法" } //一个泛型类里面 有很多的静态的方法 这个类一般都是工具类 用来封装一些的数据。 } class Demo { public void a(A a) { } } class Test2 { public static void main(String[] args) { FanXingClass myObj = new FanXingClass<>(); myObj.setA("abc"); myObj.m1(1000, "hello"); /*myObj.m1(true); myObj.m1(new UserInfo());*/ } }
泛型接口
//后期有很多模块: User Product //新增 删除 修改 查询 //T 就是 User Product.... class User { } class Product { } public interface BaseDao{ void add(T t); //新增 void delete(); //删除 void update(); //修改 T findOne(); //查询 Collection findAll(); } class ProductDaoImpl implements BaseDao { @Override public void add(Product product) { } @Override public void delete() { } @Override public void update() { } @Override public Product findOne() { return null; } @Override public Collection findAll() { return null; } } class UserDaoImpl implements BaseDao { @Override public void add(User user) { } @Override public void delete() { } @Override public void update() { } @Override public User findOne() { return null; } @Override public Collection findAll() { return null; } }
public class DemoTest{ //使用 规定参数化类型的上限 //只能:Number以及Number类型所有的子类 public static void a(Collection super String> collection) { //?是一个通配符 ,只写?,则通配任意一个类型 //下限: 只能是String以及String的父级类型。 } } class Test3 { public static void main(String[] args) { Collection collection = new ArrayList<>(); DemoTest.a(collection); } }
集合与泛型
public class CollectionDemo1 { public static void main(String[] args) { //使用泛型修饰集合 //泛型+集合: 限定集合元素数据类型。 只存储一种类型的数据。 CollectionnameCollection = new ArrayList<>(); //泛型只在编译期间有效 在运行期间其实泛型还是Object---->反射 泛型擦除 nameCollection.add("张三"); nameCollection.add("李四"); nameCollection.add("王五"); nameCollection.add("赵六"); //遍历集合元素 /*for (String name : nameCollection) { System.out.println(name); }*/ //nameCollection.forEach(System.out::println); /* Iterator it = nameCollection.iterator(); while (it.hasNext()) { String str = it.next(); System.out.println(str); }*/ //nameCollection.remove("张三"); //nameCollection.removeIf("张三"::equals); System.out.println(nameCollection); //集合转数组 //无参的toArray()不推荐,其返回值类型Object[],而集合里元素都是String,需要进行手动类型转换 /*Object[] array1 = nameCollection.toArray(); for (Object o : array1) { String s= (String) o; }*/ //有参的toArray(T[] a),返 。 //数组长度length>集合元素个数size 数组有很多null,很容易NPE,空间内存浪费 //size>length>0 并发的时候,多次将集合转数组,GC进行回收无用的数组引用 //length==0 String[] strings1 = nameCollection.toArray(new String[0]); //集合转数组: 推荐数组的空间写0 System.out.println("strings1:" + Arrays.toString(strings1)); } }
public class CollectionTest { public static void main(String[] args) { demo1(); } private static void demo1() { Collectioncollection = new ArrayList<>(); collection.add(100); collection.add(100); collection.add(100); collection.add(200); //collection.remove(100); 删除一个100 collection.removeIf(Integer.valueOf(100)::equals); //删除多个100 //若遍历期间执行删除:jdk不是1.8的情况,此时不能使用removeIf,有三种:增强for ,迭代器,foeEach //增强for遍历期间能删除吗? /*for (Integer num : collection) { if (Objects.equals(num, 100)) { collection.remove(num); //删除 } //此处添加break,不会报错,但只删除一个100 }*/ //若不添加break,还是只删除一个,结果为[100,100,200],还会报错,ConcurrentModificationException //增强for循环底层还是用迭代器实现的 //总结:增强for遍历期间不能删除,会触发并发修改异常,原因是底层规则导致。 //集合里面有一个规则:底层提供了迭代器模式,fail-fast 快速失败 (底层有个变量modCount,维护该思想) //遍历期间执行删除: java.util.ConcurrentModificationException //迭代器能删除吗? //迭代器, 可以删除 不要使用集合类里面的remove 使用迭代器里面remove /*Iterator it = collection.iterator(); while (it.hasNext()) { Integer num = it.next(); if (Objects.equals(num, 100)) { it.remove(); //collection.remove() 集合里的remove()方法不行,也会触发该机制 } }*/ //forEach在循环期间能删除吗? 底层用增强for做的,不行 /*collection.forEach(num->{ if (Objects.equals(num, 100)) { collection.remove(num); } });*/ //总结:循环期间想删除,用迭代器或者removeIf //removeIf底层也是用迭代器实现的 System.out.println(collection); } }
public class CollectionTest { public static void main(String[] args) { demo2(); } private static void demo2() { Collectioncollection = new ArrayList<>(); collection.add("a"); collection.add("b"); collection.add("c"); //集合转数组 collection.toArray() 一个无参的,两个带参的 //Object[] objects = collection.toArray(); 无参的,返的对象,也是字符串,但每次要转换操作,一般不用 //String[] strings = collection.toArray(new String[0]); 带参的,返的字符串 数组长度给0 /*collection.toArray(new IntFunction () { //jdk1.11之后有的新方法,形参是个泛型数组 @Override public String[] apply(int value) { return new String[0]; 这里value的值就是0,可将0替换成value } });*/ //简化:collection.toArray((int value)->new String[value]); //再次简化:这里不是类名/引用调方法,可将new一个String数组,数组长度为0可写成如下 //String[] strings = collection.toArray(String[]::new); 结果为[a,b,c] String[] strings = collection.toArray(String[]::new); System.out.println(Arrays.toString(strings)); }
public class CollectionTest { public static void main(String[] args) { demo3(); } private static void demo3() { Integer[] array = {1, 2, 3, 4}; int[] arr = {1, 22, 1}; //数组转集合 集合类没有该方法 //操作数组元素的工具类Arrays有方法,但返回的是listList integers = Arrays.asList(array); //这里是Integer List ints = Arrays.asList(arr); //这里是int[],故数组转集合,不要给int[]型数组,集合里面只存储引用数据类型的数据 }
public class ListDemo { public static void main(String[] args) { demo1(); } private static void demo1() { ListmyList = new ArrayList<>(); //创建List集合对象 myList.add(10); myList.add(1); myList.add(100); //带一个参数的更好用,默认往后添 //myList.add(1, 200); //add()带两个参数的,指定索引去添。新增的范围: 0-size() [10,200,1,100] //myList.add(0,1); //System.out.println(myList); [1,200,10,1,100] //删除 //myList.remove(Integer.valueOf(100)); //根据元素删,返回boolean //myList.remove(0); //根据索引删 索引范围:0-size()-1 返删除前的元素 //修改 //Integer oldNum = myList.set(0, 1000); //根据索引修改,将第一个元素改成1000. 返回修改前的元素 //System.out.println("oldNum:"+oldNum); //截取 //List subList = myList.subList(0, 2); //截取索引为0,1的元素(包头不包尾) 返回的是新的集合 //System.out.println("myList:" + myList); //System.out.println("subList:" + subList); //System.out.println("--------------------"); //截取之后,对新集合做增,删,修改等更新操作,都会还原到原集合上 //subList.add(111); //subList.remove(0); //在截取之后,操作原集合,再操作截取集合,会出现异常 (只操作原集合,不操作截取后的新集合,没问题) //myList.add(200); //System.out.println("myList:" + myList); //System.out.println("subList:" + subList.toString()); //连输出新集合都不行,因为底层还会调用toString() //查询 System.out.println(myList.get(2)); //查询指定索引的数据 } }
public class ListDemo { public static void main(String[] args) { demo2(); } private static void demo2() { //循环遍历list集合元素 Listlist = new ArrayList<>(); list.add("hello1"); list.add("hello2"); list.add("hello3"); list.add("hello4"); list.add("hello4"); list.add(null); //1. 普通for循环(while,do-while都可以) index----> 推荐 ArrayList //推荐用普通for循环,底层是数组 /*int size = list.size(); for (int index = 0; index < size; index++) { System.out.println(list.get(index)); }*/ //2.增强for 底层就是迭代器思想实现 /*for (String s : list) { System.out.println(s); }*/ //3.迭代器 /*Iterator it = list.iterator(); //继承的父接口 while (it.hasNext()) { System.out.println(it.next()); }*/ /*ListIterator listIterator = list.listIterator(); while (listIterator.hasNext()) { System.out.println(listIterator.next()); } System.out.println("----------------"); // 1 2 3 4 代码从左到右走,光标开始在1左边 while (listIterator.hasPrevious()) { //逆向遍历,需要先正向遍历,使光标在4后面才行 System.out.println(listIterator.previous()); }*/ //4.forEach list.forEach(System.out::println); }
public class ArrayListDemo { public static void main(String[] args) { } private static void demo1() { //面试: 底层 //使用普通方式创建集合对象 ArrayListlist = new ArrayList<>(); //无参构造, 初始化容器为10 //增加 Collections.addAll(list, "a", "b", "c", "e", "f"); //操作集合元素工具类 Collections 一次添加多个数据 System.out.println(list); //删除 //System.out.println(list.remove("b")); //数组维护集合的数据 index System.out.println(list.remove(0)); System.out.println(list); /*for (int i = 1; i <= 10; i++) { list.add("hell0" + i); } list.add("abc"); System.out.println(list.get(0)); // elementData[index] System.out.println(list.set(0, "zhangsan")); // elementData[index] = zhangsan System.out.println(list);*/ } }
public class LinkedListDemo { public static void main(String[] args) { testRemove(); } private static void testRemove() { LinkedListlinkedList = new LinkedList<>(); linkedList.add(100); linkedList.add(105); linkedList.add(200); linkedList.add(300); //linkedList.remove(Integer.valueOf(105)); //linkedList.remove(1); /*System.out.println(linkedList.remove()); System.out.println(linkedList.removeFirst()); System.out.println(linkedList.removeLast()); System.out.println(linkedList.poll()); System.out.println(linkedList.pollFirst()); System.out.println(linkedList.pollLast()); System.out.println(linkedList.pop());*/ System.out.println(linkedList); } private static void testSet() { LinkedList linkedList = new LinkedList<>(); linkedList.add(100); linkedList.add(101); linkedList.add(200); linkedList.add(300); System.out.println(linkedList.set(1, 105)); System.out.println(linkedList); } private static void testGet() { LinkedList linkedList = new LinkedList<>(); linkedList.add(100); linkedList.add(101); linkedList.add(200); linkedList.add(300); //System.out.println(linkedList.get(0));// Node.item //System.out.println(linkedList.getFirst()); //System.out.println(linkedList.getLast()); //System.out.println(linkedList.element()); //System.out.println(linkedList.peek()); //System.out.println(linkedList.peekFirst()); //System.out.println(linkedList.peekLast()); // System.out.println(linkedList.poll()); // System.out.println(linkedList.pollFirst()); // System.out.println(linkedList.pollLast()); System.out.println(linkedList); } private static void testAdd() { LinkedList linkedList = new LinkedList<>(); linkedList.add(100); linkedList.add(200); linkedList.add(300); linkedList.add(0, 101); linkedList.addFirst(1); linkedList.addLast(400); linkedList.push(1111); System.out.println(linkedList); } }
private static class Node{ //维护LinkedList底层的双向链表的数据结构 E item; //数据 Node next; //下一个节点的引用 Node prev; //上一个节点的引用 Node(Node prev,E element,Node next){ this.item = element; this.next = next; this.prev = prev; } }
//Vector是安全的,ArrayList是不安全的。 //下面的代码中,一个每次的结果都一样,另一个结果就不同 public class VectorDemo { private static Vectorlist = new Vector<>(); //不管用不用都会去创建一个容量为10的数组。对于ArrayList,不放数据在无参里不会在无参里创建容量,当执行新增时才会扩容 public static void main(String[] args) { List threadList = new ArrayList<>(10); //开辟10个空间 //使用一个成员变量维护存储很多数据,每个线程共同操作同一个集合对象 for (int i = 0; i < 10; i++) { //创建10个线程,都操作同一个集合对象list threadList.add(new Thread(() -> { //将线程放在集合里 for (int j = 0; j < 1000; j++) { //每个线程又循环1000次 list.add(1); //每个线程都往里面放数据 } })); } //上面一共11个线程,其中有一个主线程(main)。主线程里创建了10个新的线程 threadList.forEach(Thread::start); //启动子线程 //等待子线程执行完毕 threadList.forEach(thread -> { try { thread.join(); //等待着前面10个子线程执行完毕(等待着子线程死亡) 用join()方法 } catch (InterruptedException e) { e.printStackTrace(); } }); //或者让主线程等等 /*try { TimeUnit.SECONDS.sleep(5); //让主线程休眠 5s(1s就已经很久了,cpu很快的) } catch (InterruptedException e) { e.printStackTrace(); }*/ System.out.println(list.size());//10000 并发走完之后,获得集合里的元素个数(主线程走完,才走新的线程) } } //只用安全的才能作为成员变量使用,不安全的只能当局部变量
public class CopyListDemo { public static void main(String[] args) { //底层是动态数组 CopyOnWriteArrayListlist = new CopyOnWriteArrayList<>(); list.add(100); } }
public class SetDemo { public static void main(String[] args) { testHashSet(); private static void testHashSet() { HashSetset = new HashSet<>(); set.add(50); set.add(3); set.add(1); set.add(10); set.add(2); //无序: 元素没有索引 新增的顺序与遍历的顺序不一致的 System.out.println(set); //遍历set集合(3种) /*for (Integer num : set) { System.out.println(num); }*/ /* Iterator it = set.iterator(); while (it.hasNext()) { System.out.println(it.next()); }*/ set.forEach(System.out::println); } } }
private static void testLinkedHashSet() { //HashSet的一个子类 元素有序,新增顺序与遍历顺序一致(不同与父类的一点) LinkedHashSetset = new LinkedHashSet<>(); set.add(50); set.add(3); set.add(1); set.add(10); set.add(2); set.add(2); set.add(null); System.out.println(set); }
private static void testTreeSet() { //元素特征:元素有序,都会按照自然顺序排列(从大到小或从小到大) //故要求其集合元素的数据类型必须有排序规则,这需要以下两条要求满足其一: //1.保证集合元素数据类型实现Comparable接口 //2.自定义提供排序规则,实现Comparator TreeSettreeSet = new TreeSet<>(); Collections.addAll(treeSet, 100, 90, 91, 82, 88); //若给汉字或者字符串,用第一个字符比较排序,不看后面 System.out.println(treeSet); System.out.println(treeSet.first()); //拿第一个值(最大值或最小值 ) System.out.println(treeSet.last()); /* TreeSet stringTreeSet = new TreeSet<>(); Collections.addAll(stringTreeSet, "hello", "abcw", "kkk", "bcd"); System.out.println(stringTreeSet);*/ }
在集合中存储对象的问题:(将多个用户对象存储集合中 List Set ,先不涉及TreeSet) public class UserInfo { private int id; private String name; private int age; }
private static void testList() { //将多个用户对象存储集合中 List ListuserInfoList = new ArrayList<>(10); //空间给10 userInfoList.add(new UserInfo(1, "张三", 20)); //匿名对象 userInfoList.add(new UserInfo(1, "张三", 20)); userInfoList.add(new UserInfo(1, "张三", 20)); userInfoList.add(new UserInfo(1, "张三", 20)); userInfoList.add(new UserInfo(1, "张三", 20)); //此时集合里有5个数据 System.out.println(userInfoList.size()); }
private static void testSet() { //将多个用户对象存储集合中 Set SetuserInfoSet = new HashSet<>(16); userInfoSet.add(new UserInfo(1, "张三", 20)); userInfoSet.add(new UserInfo(1, "张三", 20)); userInfoSet.add(new UserInfo(1, "张三", 20)); userInfoSet.add(new UserInfo(1, "张三", 20)); userInfoSet.add(new UserInfo(1, "张三", 20)); System.out.println(userInfoSet.size()); //也是5个数据 userInfoSet.forEach(System.out::println); //信息都一样 //这样做没有符合Set集合元素特征(一样的东西,就一个就行了,不重复) //如何保证同一个,得保证哈希值一样,要重写哈希就得重写equals //总结:自定义类类型的数据存储set集合,为保证相同信息的元素就一个,要重写equals+hashcode }
public class UserInfo { private int id; private String name; private int age; @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; UserInfo userInfo = (UserInfo) o; return Objects.equals(name, userInfo.name); } @Override public int hashCode() { return Objects.hash(name); } @Override public String toString() { return "UserInfo{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}'; } } 重写后,testSet()里就会输出一个对象。 对testList()没有影响。
若将对象放在TreeSet里: private static void TreeSet1() { //先添加一个 TreeSetuserInfoSet = new TreeSet<>(); userInfoSet.add(new UserInfo(1, "张三", 20)); System.out.println(userInfoSet); //添加不了,还会报异常(类型转换异常,用户类不能转成Comparable,用户类型里并没有compareTo方法) //原因:TreeSet集合的元素数据类型必须实现Comparable接口(它的元素要去排序) //这里要想让用户类存在该集合,必须要让用户类实现Comparable接口 //存到集合里,是用户和用户比,是用户的什么和什么比,这个规则自己去定义 }
下面让用户类实现Comparable接口,并重写compareTo方法。 同时在TreeSet1()里放两个用户,让他们去比较: @Setter @Getter @NoArgsConstructor @AllArgsConstructor public class UserInfo implements Comparable{ //这里是泛型接口,存放用户类的比较对象,用户和用户比,故存放用户 private int id; private String name; private int age; @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; UserInfo userInfo = (UserInfo) o; return Objects.equals(name, userInfo.name); } @Override public int hashCode() { return Objects.hash(name); } @Override public String toString() { return "UserInfo{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}'; } @Override public int compareTo(UserInfo o) { //重写比较规则 return this.age-o.age; //age是int型,不能调方法,故可用相减 } }
private static void TreeSet1() { TreeSetuserInfoSet = new TreeSet<>(); userInfoSet.add(new UserInfo(1, "张三", 20)); userInfoSet.add(new UserInfo(2, "李四", 18)); System.out.println(userInfoSet); //[UserInfo{id=2, name='李四', age=18}, UserInfo{id=1, name='张三', age=20}] }
其实: private static void TreeSet1() { TreeSetuserInfoSet = new TreeSet<>(); userInfoSet.add(new UserInfo(1, "张三", 20)); userInfoSet.add(new UserInfo(2, "李四1", 18)); userInfoSet.add(new UserInfo(3, "李四1", 18)); userInfoSet.add(new UserInfo(4, "李四1", 18)); userInfoSet.add(new UserInfo(5, "李四1", 18)); System.out.println(userInfoSet); //[UserInfo{id=2, name='李四1', age=18}, UserInfo{id=1, name='张三', age=20}] //这里得原因:树的分支,set集合的保证元素唯一,唯一是结合排序规则走的。 //故,TreeSet集合,在里面重写equals和hashCode没有意义。(底层和他俩没关系) //再次总结:TreeSet只看排序的规则(哪个属性参与排序 元素唯一就看那个属性)。与重写equals+hashcode没有关系。底层的数据结构是树结构。 }
不过:用类实现Comparable: 类与接口的耦合比较高 一般不使用内部比较器。(若不实现接口,相应的排序规则也没有了) 此时推荐使用外部比较器。 @Setter @Getter @NoArgsConstructor @AllArgsConstructor //类实现Comparable: 类与接口的耦合比较高 一般不使用内部比较器。推荐使用外部比较器 public class UserInfo { private int id; private String name; private int age; }
private static void testSet1() { //外部比较器 /* TreeSetuserInfoSet = new TreeSet<>(new Comparator () { //匿名内部类 @Override public int compare(UserInfo o1, UserInfo o2) { return o2.getAge()-o1.getAge(); } });*/ //用lamda简化 //TreeSet userInfoSet = new TreeSet<>((user1,user2)->user1.getAge()-user2.getAge()); //还可以写成这样: /* TreeSet userInfoSet = new TreeSet<>(Comparator.comparing(new Function () { @Override public Integer apply(UserInfo userInfo) { return userInfo.getAge(); } }).reversed());*/ //默认升序,想降序,在后面加.reversed() 反转一下 //用lamda简化: TreeSet userInfoSet = new TreeSet<>(Comparator.comparing(UserInfo::getAge).reversed()); userInfoSet.add(new UserInfo(1, "张三", 20)); userInfoSet.add(new UserInfo(2, "李四1", 17)); userInfoSet.add(new UserInfo(3, "李四1", 16)); userInfoSet.add(new UserInfo(4, "李四1", 15)); userInfoSet.add(new UserInfo(5, "李四1", 19)); userInfoSet.forEach(System.out::println); //遍历输出 }
//循环遍历期间能否删除元素? public class ListDemo { public static void main(String[] args) { demo1(); } private static void demo1() { /*Integer[] array = {100, 200, 300, 400}; Listlist = List.of(array);*/ //1.9之后也可用List自己的方法快速往里放数据,也是数组转集合的方法 //其实of方法返的是一个视图 ArrayList list = new ArrayList<>(10); Collections.addAll(list, 100, 200, 200, 300, 400); //快速往里放数据 //删除集合中所有的200的数据,用removeIf 或者遍历list: //遍历List的方式:普通for 增强for 迭代器 forEach System.out.println(list); //普通for 遍历期间可以删除 /*for (int index = 0; index < list.size(); index++) { //size必须动态去拿,不能写死,否则越界 if (Integer.valueOf(200).equals(list.get(index))) { list.remove(index); //删除这个,它的下一个元素自动左移,且size会减一 if (Integer.valueOf(200).equals(list.get(index))) { //保证相邻的元素也被删除 index--; //如果再拿一次,又是200,索引要减一下 } } }*/ //增强for,底层是迭代器实现的。 遍历期间不可删除 //forEach,遍历期间也不能删除元素 //迭代器在遍历期间可以删除 //增强for的底层模拟 Iterator it = list.iterator(); //先拿迭代器 for (; it.hasNext(); ) { Integer num = it.next(); if (Integer.valueOf(200).equals(num)) { //list.remove(num); //用集合的remove 只能删倒数第二个,删其他会报异常 it.remove(); //用迭代器里的remove就可以全部删除。 但要明白删除集合元素,底层永远都是调用的集合类里面的remove的方法 } } System.out.println(list); //Iterator模式: 循环遍历集合元素 //集合类里提供了一个属性,叫modCount,与循环遍历功能相关。 这个属性并不是必须的,jdk的所有的集合框架中,都提供了这样的一个机制 //该机制是在循环遍历期间,执行新增,删除等功能,可能会触发规则里的快速失败的机制 } }
private static void demo1() { //创建map集合对象 //使用map集合维护一个用户完整信息 id=20 name=张三 age=20 pass=1234 //上面的id,20 name,张三 age,20 pass,1234都是key和value的一组键值对 MapuserInfoMap = new LinkedHashMap<>(); //1.新增数据----> key值重复 等价新值会覆盖旧的数据 userInfoMap.put("id", 1001); //这些全部体现了多态 userInfoMap.put("name", "张三"); userInfoMap.put("age", 20); userInfoMap.put("pass", "1234"); //2.删除 根据key删除,因为key是唯一 //System.out.println(userInfoMap.remove("pass")); 返回值是删除的数据 若找不到要删除的元素,返null //System.out.println(userInfoMap.remove("pass", "12345")); 两个参,一个key,一个value。返回类型boolean。当两个参数完全匹配,才能删除成功 //3.修改 //System.out.println(userInfoMap.replace("pass", "12345")); 返回修改前的数据 修改的key不存在,返null //System.out.println(userInfoMap.replace("pass", "1234","12345")); 带三个参的和remove带两个参的一样 //4.查询 //System.out.println(userInfoMap.get("name1")); key不存在,返null,null值若调equals,有风险出现空指针 //为避免出现空指针,可用getOrDefault(),若找不到返""(空字符)。 前提: 集合存储相同类型的数据(value),这里有Integer。 //System.out.println(userInfoMap.getOrDefault("name1", "")); //5.判断 System.out.println(userInfoMap.isEmpty()); System.out.println(userInfoMap.size()); System.out.println(userInfoMap.containsKey("name")); //这里的参数可以是key,也可以是value,但拿value没意义 System.out.println(userInfoMap); }
private static void demo2() { //不能用快捷方式构建Map集合,即不能用Collections.addAll()。它的形参是collection //Map里有Map.of()方法 MapuserInfoMap = new LinkedHashMap<>(); userInfoMap.put("id", 1001); userInfoMap.put("name", "张三"); userInfoMap.put("age", 20); userInfoMap.put("pass", "1234"); //遍历map集合元素 //增强for不行,其里面是集合元素类型,这里两个类型,没法写 //迭代器不行,只有collection集合里才有迭代器 //用forEach方法(1.8之后),底层还是用迭代器(除了普通for,循环遍历底层都是用迭代器写的) /* userInfoMap.forEach(new BiConsumer () { @Override public void accept(String key, Object value) { System.out.println(key + "----" + value); } });*/ //用lamda简化: //userInfoMap.forEach((key, value) -> System.out.println(key + "----" + value)); //不能用::简化,重写的形参类型(两个)和调的形参类型(一个)不一样。 //jdk1.8之前的遍历方式 /*Set > entrySet = userInfoMap.entrySet(); //推荐使用 返回值set集合 //底层是将map集合里面的每组数据都依次的封装成一个个的entry对象 再将entry对象存储set集合中 //set集合里存的Map集合里的每一组数据。故遍历Map集合转成遍历Set集合,它遍历方式就多了,增强for,迭代器都可以 for (Map.Entry entry : entrySet) { // map集合中每组数据 都在entry里面 String key = entry.getKey(); Object value = entry.getValue(); System.out.println(key + "----" + value); }*/ //另一种方法:userInfoMap.keySet() 返回值类型,set集合 /* Set keySet = userInfoMap.keySet(); //获得map集合里面每个key 存储set集合中 for (String key : keySet) { System.out.println(key + "----" + userInfoMap.get(key)); }*/ }
private static void demo3() { String str = "djhdf77gggaf6t3gdshgfsa"; //使用map集合实现: 统计每个字符出现的次数 //key和value是什么类型?key要求唯一 字符是惟一的,故String,character都行。 value可为次数,用Integer Mapmap = new HashMap<>(); //1.获得字符串里面的每个字符 int length = str.length(); //拿字符串长度 for (int index = 0; index < length; index++) { String s = String.valueOf(str.charAt(index)); //获得每个字符数据 同时char转String // V merge(K key, V value, BiFunction super V, ? super V, ? extends V> remappingFunction) //BiFunction是一个函数式接口 /* map.merge(s, 1, new BiFunction () { @Override public Integer apply(Integer integer, Integer integer2) { return integer+integer2; } });*/ //简化如下: map.merge(s, 1, Integer::sum); //获得每个字符之后的操作也可这样: /*//判断这个s字符是否存在于map集合中 Integer num = map.get(s); //拿value,value是次数 if (num != null) { //若之前没放过,value为null,不走if num += 1; } else { num = 1; } map.put(s, num); //第一次存储字符 //之前存储过相同的字符 获得之前的次数+1*/ } System.out.println(map); }
private static void demo7() { //在并发中,使用ConcurrentHashMap ConcurrentHashMapmap = new ConcurrentHashMap<>(); map.put(1, 10); map.put(10, 10); map.put(11, 10); map.put(122, 10); map.put(23, 10); //CAS 性能比较高 System.out.println(map); } private static void demo6() { //hashtable比较老的一个类 //安全的方式: public synchronized V put(K key, V value) { Hashtable hashtable = new Hashtable<>(); hashtable.put(1, 10); hashtable.put(10, 10); hashtable.put(11, 10); hashtable.put(122, 10); hashtable.put(23, 10); hashtable.put(null, 10); System.out.println(hashtable); } private static void demo5() { //会使用自定义类对象充当map的key吗? 绝对不会 (就是写,就下面) 开发中,Map的类型就String/Interer/long /* TreeMap treeMap = new TreeMap<>(new Comparator () { @Override public int compare(UserInfo o1, UserInfo o2) { return 0; } });*/ //用lamda简化: TreeMap treeMap = new TreeMap<>((user1, user2) -> 0); treeMap.put(new UserInfo(), 100); System.out.println(treeMap); } static class UserInfo { /*implements Comparable { @Override public int compareTo(UserInfo o) { return 0; }*/ } private static void demo4() { //TreeSet: 元素不能为null 元素有序 (按照自然顺序排列) 底层就是用TreeMap写的 //TreeMap: k不能为null v可以为null key的数据的元素类型必须默认实现Comparable 要么提供Comparator TreeMap treeMap = new TreeMap<>(); treeMap.put(1, 10); treeMap.put(10, 10); treeMap.put(11, 10); treeMap.put(122, 10); treeMap.put(23, 10); System.out.println(treeMap); } private static void demo3() { //新增顺序与遍历顺序一致 LinkedHashMap linkedHashMap = new LinkedHashMap<>(); linkedHashMap.put(1, 10); linkedHashMap.put(10, 10); linkedHashMap.put(11, 10); linkedHashMap.put(122, 10); linkedHashMap.put(23, 10); linkedHashMap.put(null, null); System.out.println(linkedHashMap); } private static void demo2() { HashMap map = new HashMap<>(); map.put("Ma", 100); map.put("Ma", 200); map.put("NB", 300); map.get("NB"); //"Ma"与"NB"的哈希值一样,从而计算得索引一样,key数值却不同,产生哈希冲突,此时用单链表解决 /*int code1 = "Ma".hashCode(); int code2 = "NB".hashCode(); System.out.println(code1); System.out.println(code2); System.out.println(code1 ^ (code1 >>> 16)); System.out.println(code2 ^ (code2 >>> 16));*/ } private static void demo1() { HashMap map = new HashMap<>(); map.put(1, 1); map.put(1, 10); //modCount有没有运算? 没有 //key数据一致,hash冲突。新的数据替换oldvalue //(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); /* Integer num = Integer.valueOf(1); int hashCode = num.hashCode(); System.out.println(hashCode ^ (hashCode >>> 16));*/ } }
private static void demo9() { //List里套Map //创建多个用户的信息 使用集合维护多个用户 不要用户类 //name=jim id=1001 age=20 //Map.of(k1,v1,k2,v2....) 一个Map就是一个用户 Mapuser1 = Map.of("id", "1001", "name", "jim1", "age", "20"); Map user2 = Map.of("id", "1002", "name", "jim3", "age", "20"); Map user3 = Map.of("id", "1003", "name", "jim2", "age", "20"); List
private static void demo13() { //Arrays可转数组,转集合 Listlist = Arrays.asList("a", "b", "c"); list.forEach(System.out::println); //遍历这个集合没有问题,即查看没问题 //更新集合元素数据(add/remove),将报异常 修改没事(空间固定,没变化) 因为asList,返的是一个view(只能看,不允许更新) list.add(0,"hello"); list.forEach(System.out::println); //一个方法返的是集合,该集合能不能执行更新和修改就看底层返不返view,类似的下面也返view //Map.of().entrySet() Map.of().keySet } private static List list = Collections.synchronizedList(new ArrayList<>()); //将线程不安全的集合类转换成一个线程安全集合类 private static void demo12() { //很多集合类都是一些线程不安全的集合类 List threadList = new ArrayList<>(10); for (int i = 0; i < 10; i++) { threadList.add(new Thread(() -> { for (int j = 0; j < 1000; j++) { list.add(1); } })); } threadList.forEach(Thread::start); threadList.forEach(thread -> { try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } }); //等待着前面10个子线程执行完毕---->等待着子线程死亡 //获得集合里面的元素个数 //让主线程休眠 5 System.out.println(list.size());//10000 } private static void demo11() { Set set = new HashSet<>(); //完全无序 Collections.addAll(set, 10, 2, 32, 41, 5); Integer max = Collections.max(set); //获得最大值 System.out.println(max); System.out.println(Collections.min(set)); //获得最小值 } private static void demo10() { //java.util.Collections 提供很多静态方法(因为工具类),操作集合元素数据 //Collections.addAll(Collection c, E...e); 只能对collection集合批量添加 //Collections.shuffle(list); 打乱集合元素顺序 //Collections.sort(list) 对list集合元素进行升序排列 List list = new ArrayList<>(10); Collections.addAll(list, 10, 2, 32, 41, 5); //对list集合给数据 //如果想降序排序,则自定义规则,用Collections.sort()带两个参数的 /*Collections.sort(list, new Comparator () { @Override public int compare(Integer o1, Integer o2) { return o2.compareTo(o1); } });*/ //Collections.sort(list,Comparator.comparing(Integer::intValue).reversed()); 反转 //list.sort(Comparator.comparing(Integer::intValue).reversed()); list集合里也提供了comparator System.out.println(list); }
private static void demo1() { //1.获得集合对象Collection Listlist = List.of(1, 2, 3, 4); //List在java.util包里,别导错包 //2.获得Stream的实例 //法一:Collection集合的方法 (集合里提供的两个方法) 若用集合里的方法,要先获得集合对象 Stream stream = list.parallelStream(); //并行化流 Stream stream1 = list.stream(); //串行化stream //并行 VS 串行 都是指计算机计算的能力。 //案例: 1000个数字 求最大值。 //串行: 类似于冒泡排序 max 有序 //并行: 同时执行多个任务。 将1000个数据 放到多个任务中(多个线程)。 每个任务随机分配数据,每个任务都会获得一个最值,最后在这10个里面求最大值 //串行有序,并行无序。 串行效率低于并行 //法二:Stream本身是一个接口,接口里也提供了静态方法,如下,也可得到Stream的实例 //Stream stream2 = Stream.of(1, 2, 3, 4); //法三:随机数 Random类 //先创建Random实例,在其里面也提供了获得Stream实例的方法 Random random = new Random(); //随机获得5个随机的数字,范围是1000-10000。 正常写需要循环,用流如下: IntStream intStream = random.ints(5, 1000, 10000); //提供了对于基本数据类型封装Stream,目的:减少过多的拆箱和装箱处理 intStream.forEach(System.out::println); //法四:Arrays 将集合的元素放到Stream对象 Stream stream2 = Arrays.stream(new Integer[]{1, 2, 3}); }
private static void demo2() { /*Listlist = List.of(10,78,90,199,1,9,100,1,90,10); //对List集合元素去重并升序/降序排列 常规做法:遍历,然后将元素放到另一个集合里 性能不高 TreeSet treeSet = new TreeSet<>(); list.forEach(treeSet::add); System.out.println(treeSet);*/ //若往集合List里放元素,再对list集合元素去重并升序/降序排列 当元素个数足够大时,其性能更低 List list = new ArrayList<>(1000); //容量给了1000 for (long i = 10000000; i >= 0; i--) { list.add(i); } long begin1 = System.nanoTime(); //TreeSet treeSet = new TreeSet<>(); //ist.forEach(treeSet::add); long begin2 = System.nanoTime(); //System.out.println(begin2 - begin1);//3709702827 1036846933 //若用Stream去对List里的元素去重并升序/降序排列 //Stream stream = list.stream(); //list集合里面的数据都在stream //List list1 = stream.distinct().sorted().collect(Collectors.toList()); //使用Stream 聚合数据 //stream里的 去重distinct() 排序sorted() 收集collect(Collectors.toList())转List,转Set结合需求去做 //使用过Stream,还能再次使用吗? 无法使用第二次 Stream功能类似于Iterator,迭代器从左向右拿数据,拿完之后不能再回去,只能拿光标之后的数据 //Stream用完之后就关闭了,一般用它,都是链式的操作这些功能,一行代码写完,一次搞定。方法的顺序无所谓。 //获得运算之后的结果 begin2 = System.nanoTime(); System.out.println(begin2 - begin1); //parallelStream性能没有stream快 }
private static void demo3() { System.out.println(); String str = "2543716424"; //求字符串里的最小的字符数据 char[] chars = str.toCharArray(); //拿到字符内容 /*Arrays.sort(chars); System.out.println(chars[0]); ;*/ //用Stream去做 Listlist = new ArrayList<>(10); //装进集合 for (char aChar : chars) { list.add(aChar); } System.out.println(list.stream().sorted().findFirst()); //再用流 }
private static void demo4() { Listlist = List.of(10, 20, 40, 60, 60, 50); //留下来集合元素>30的数据 存储到另外一个集合里面 List newList = new ArrayList<>(10); for (Integer num : list) { if (num > 30) { newList.add(num); } } System.out.println(newList); //Stream.filter 留下来满足条件的数据 (过滤) /*list.parallelStream().filter(new Predicate () { @Override public boolean test(Integer num) { //num就是stream流里的 每个元素 return num>30; } })*/ List collect = list.parallelStream().filter(num -> num > 30).distinct().sorted().collect(Collectors.toList()); System.out.println(collect); }
@Setter @Getter @AllArgsConstructor @ToString class UserInfo { private Integer id; private Integer age; private String name; }
private static void demo5() { ListuserInfoList = new ArrayList<>(10); Collections.addAll(userInfoList, new UserInfo(1, 20, "jim"), new UserInfo(2, 20, "tom"), new UserInfo(3, 20, "zhangsan") ); /*userInfoList.parallelStream().filter(new Predicate () { @Override public boolean test(UserInfo userInfo) { return userInfo.getName().contains("m"); } });*/ //查询名称里面包含m的用户信息 List collect = userInfoList.parallelStream().filter(userInfo -> userInfo.getName().contains("m")).collect(Collectors.toList()); System.out.println(collect); }
private static void demo7() { ListuserInfoList = new ArrayList<>(10); Collections.addAll(userInfoList, new UserInfo(1, 21, "jim"), new UserInfo(2, 22, "tom"), new UserInfo(3, 23, "zhangsan") ); //所有人年龄+1 对于这种引用型,用peek就更方便 /* List
private static void demo8() { Listlist1 = List.of(1, 3, 5, 3, 8, 10); List list2 = List.of(11, 32, 53, 3); List list3 = List.of(100, 37, 58, 39, 8, 10); //3个数据源 将3个集合里面的元素都存储到一个集合中(结果还要去重/排序) //常规方法:用三个for循环,每次将一个集合的元素放到最终的集合中 //Stream做法:"将3个集合里面的所有的数据都放在一个Stream" Stream.of() 现在里面是三个集合对象 //多个数据源数据存储到一个Stream(多个集合或者流数据转到一个流里) Stream.flatMap() /*Stream.of(list1, list2, list3).flatMap(new Function , Stream>>() { @Override public Stream> apply(List
list) { return list.parallelStream(); } })*/ List collect = Stream.of(list1, list2, list3).flatMap(List::parallelStream).distinct().sorted().limit(5).collect(Collectors.toList()); System.out.println(collect); }
private static void demo9() { //reduce() 做聚合的方法(求总和,最大,最小,平均值等) Listlist = List.of(100, 89, 90); //成绩的总和 常规做法:遍历求和 //用Stream去做 Optional optional = list.parallelStream().reduce(new BinaryOperator () { @Override public Integer apply(Integer num1, Integer num2) { return num1 + num2; } }); //Optional是工具类,一般是用来执行null值运算 System.out.println(optional.get()); //用lamda替换: Integer result = list.parallelStream().reduce(Integer::sum).get(); System.out.println(result); //求最小值: //Integer result = list.parallelStream().reduce(Math::min).get(); //System.out.println(result); //reduce()带两个参数的,如下:每个元素加10再求总和 //Integer result = list.parallelStream().reduce(10, Integer::sum); //System.out.println(result); }
@Setter @Getter @AllArgsConstructor @NoArgsConstructor @ToString public class Person { private Integer id; //家族成员id private String name; //家族成员name private Listchild; //这个人下面的所有子级 private Integer parentId; //这个人父级的id }
public class Demo { private static Listfamily; //初始化家谱 static { family = new ArrayList<>(10); //parentId=0 代表这个人就是顶级 family.add(new Person(1, "张三丰", null, 0)); family.add(new Person(2, "张翠山", null, 1)); family.add(new Person(3, "张翠翠", null, 1)); family.add(new Person(4, "张无忌", null, 2)); family.add(new Person(5, "张天真", null, 2)); family.add(new Person(6, "张翠花", null, 3)); family.add(new Person(7, "张二狗", null, 3)); family.add(new Person(8, "张三", null, 5)); family.add(new Person(9, "张三四", null, 0)); family.add(new Person(10, "张1", null, 9)); } public static void main(String[] args) { //需求:查询父节点下面的所有的子级的成员 /*family.parallelStream().filter(new Predicate () { @Override public boolean test(Person person) { return person.getParentId().equals(0); } });*/ //filter(person -> person.getParentId().equals(0)) 找到张三丰,再找张三丰的子级,相当于对子级赋值 /*family.parallelStream().filter(person -> person.getParentId().equals(0)).peek(new Consumer () { @Override public void accept(Person person) { person.setChild(); } }); */ //找子级,一对一操作 /* List personList = family.parallelStream() .filter(person -> person.getParentId().equals(0)) //person集合里面的每个元素 .peek(parent -> parent.setChild(getChild(parent))) //person1代表就是过滤转换parentId=0的对象 .collect(Collectors.toList()); personList.forEach(System.out::println);*/ //普通方式的写法: List list = new ArrayList<>(10); for (Person person : family) { if (person.getParentId().equals(0)) { person.setChild(getChildList(person)); list.add(person); } } list.forEach(System.out::println); //找孩子---->对child属性赋值 } private static List getChildList(Person parent) { List list = new ArrayList<>(10); for (Person person : family) { if (person.getParentId().equals(parent.getId())) { person.setChild(getChildList(person)); list.add(person); } } return list; } private static List getChild(Person parent) { return family.parallelStream() .filter(person -> person.getParentId().equals(parent.getId())) //过滤下来二级节点的数据 .peek(p -> p.setChild(getChild(p))) //递归 .collect(Collectors.toList()); } }
@Setter @Getter @NoArgsConstructor @AllArgsConstructor @ToString public class Employee { private Integer id; private String name; private Double salary; //BigDecimal修饰钱最合适 public void show(){ System.out.println(toString()); } }
public class PM extends Employee{ public PM(Integer id, String name, Double salary) { super(id, name, salary); } public PM() { } }
public class SE extends Employee{ public SE() { } public SE(Integer id, String name, Double salary) { super(id, name, salary); } }
public class Exercise1 { public static void main(String[] args) { demo1(); demo2(); } private static void demo2() { ListemployeeList = new ArrayList<>(10); Collections.addAll(employeeList,new SE(1,"se",4000.0),new PM(2,"pm",7000D)); Map employeeMap = new HashMap<>(); for (Employee employee : employeeList) { employeeMap.put(employee.getId(),employee); } //遍历Map集合 forEach entrySet keySet //employeeMap.forEach((id,emp)->emp.show()); 法一 Set > entrySet = employeeMap.entrySet(); for (Map.Entry entry : entrySet) { System.out.println(entry.getKey()+"=="+entry.getValue()); } } private static void demo1() { //集合的元素是:类类型 List employeeList = new ArrayList<>(10); Collections.addAll(employeeList,new SE(1,"se",4000.0),new PM(2,"pm",7000D)); //遍历集合 //employeeList.forEach(Employee::show); 法一 /* Iterator it = employeeList.iterator(); //法二 while (it.hasNext()) { it.next().show(); //Employee对象调用show方法 }*/ } }
@Setter @Getter @AllArgsConstructor @NoArgsConstructor @ToString public class Emp { private Integer id; private String name; private BigDecimal salary; } class SE extends Emp { public SE() { } public SE(Integer id, String name, BigDecimal salary) { super(id, name, salary); } } class PM extends Emp { public PM() { } public PM(Integer id, String name, BigDecimal salary) { super(id, name, salary); } } class Test1 { public static void main(String[] args) { SE se = new SE(1, "se", new BigDecimal("47754")); PM pm = new PM(2, "pm", new BigDecimal("843754")); //ArrayList集合对象 /* ListempList = new ArrayList<>(10); Collections.addAll(empList, se, pm);*/ List empList = List.of(se, pm); Map empMap = new HashMap<>(16); //循环遍历list集合: for 增强for forEach 迭代器 empList.forEach(emp -> empMap.put(emp.getId(), emp)); //遍历map集合: forEach entrySet keySet Set > entrySet = empMap.entrySet(); for (Map.Entry entry : entrySet) { System.out.println(entry.getKey() + "---" + entry.getValue()); } //List empList1 = Arrays.asList(se, pm); } }
public class Exercise3 { public static void main(String[] args) throws ParseException { //demo1("2022-3-3"); //demo2("2022-3-3"); demo3("2022-03-03"); } private static void demo3(String dateStr) { Listlist = Arrays.asList("周日","周一","周二","周三","周四","周五","周六"); LocalDate localDate = LocalDate.parse(dateStr); DayOfWeek week = localDate.getDayOfWeek(); System.out.println(list.get(week.getValue())); } private static void demo2(String dateStr) throws ParseException { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); Date date = dateFormat.parse(dateStr); List list = Arrays.asList("周日","周一","周二","周三","周四","周五","周六"); Calendar calendar = Calendar.getInstance(); calendar.setTime(date); int day = calendar.get(Calendar.DAY_OF_WEEK); System.out.println(day); System.out.println(list.get(day-1)); } private static void demo1(String dateStr) throws ParseException { //字符串时间转换成日期对象 Date/Calendar(1.8之前) LocalDate/LocalDateTime(1.8之后) SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); Date date = dateFormat.parse(dateStr); //System.out.println(date.getDay()); //4 可看成一周的第几天 (该方法过时,但可以用) List list = Arrays.asList("周日","周一","周二","周三","周四","周五","周六"); System.out.println(list.get(date.getDay())); //周四 } }
private static String getWeekName(@NonNull String dateStr) { //字符串转LocalDate LocalDate localDate = LocalDate.parse(dateStr); //获得这一天这周的第几天 DayOfWeek dayOfWeek = localDate.getDayOfWeek(); ListweekList = List.of("星期一", "星期2", "星期3", "星期4", "星期5", "星期6", "星期7"); //根据所索引获得星期几的数据 return weekList.get(dayOfWeek.getValue() - 1); } }
@SneakyThrows private static String getWeekName1(String str) { //pattern: y M d H m s a E SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); Date date = dateFormat.parse(str); dateFormat = new SimpleDateFormat("E"); return dateFormat.format(date); }
@Setter @Getter @AllArgsConstructor @ToString class Student { private Integer id; private String name; private Integer score; @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return Objects.equals(id, student.id); } @Override public int hashCode() { return Objects.hash(id); } }
//从功能实现上: 可以使用TreeSet private static void demo1() { //属性包括id[1-40], score[0-100],所有属性随机生成。创建Set集合,保存20个对象,找到分数最高与最低的学生 /* TreeSetstudentTreeSet = new TreeSet<>(new Comparator () { @Override public int compare(Student stu1, Student stu2) { return stu1.getScore().compareTo(stu2.getScore()); } });*/ //TreeSet studentTreeSet = new TreeSet<>((stu1,stu2)->stu1.getScore().compareTo(stu2.getScore())); /* TreeSet studentTreeSet = new TreeSet<>(Comparator.comparing(new Function () { @Override public Double apply(Student student) { return student.getScore(); } }));*/ //在TreeSet使用哪个属性的值进行排序 因此数据的重复的问题 只看排序的那个属性的数据 TreeSet studentTreeSet = new TreeSet<>(Comparator.comparing(Student::getId));//根据学生的成绩进行升序排列 ThreadLocalRandom random = ThreadLocalRandom.current(); for (int i = 1; studentTreeSet.size() < 20; i++) { studentTreeSet.add(new Student(random.nextInt(1, 41), "张三" + i, random.nextInt(101))); } System.out.println(studentTreeSet.size()); studentTreeSet.forEach(System.out::println); System.out.println(); Student min = studentTreeSet.first(); System.out.println(min); Student max = studentTreeSet.last(); System.out.println(max); }
//id要唯一 score也要排序 private static void demo2() { HashSethashSet = new HashSet<>(20); ThreadLocalRandom random = ThreadLocalRandom.current(); for (int i = 1; hashSet.size() < 20; i++) { hashSet.add(new Student(random.nextInt(1, 41), "张三" + i, random.nextInt(101))); } System.out.println(hashSet.size()); hashSet.forEach(System.out::println); System.out.println(); //本来HashSet元素的数据是无序 根本无法去排序 //使用HashSet只是满足了id的唯一性 但是无法使用score进行排序? Comparable Comparator Student max = Collections.max(hashSet, Comparator.comparing(Student::getScore)); Student min = Collections.min(hashSet, Comparator.comparing(Student::getScore)); System.out.println(max); System.out.println(min); }
private static Map> countryGroup() { String countryStr = "科特迪瓦,阿根廷,澳大利亚,塞尔维亚,荷兰,尼日利亚,日本,美国,中国,新西兰,巴西,比利时,韩国,喀麦隆,洪都拉斯,意大利"; Map > result = new HashMap<>(16); String[] countryArray = countryStr.split(","); //数组转集合 List countryList = Arrays.asList(countryArray);// view countryList = new ArrayList<>(countryList); //1.国家参与1次分组 //2.如何获得随机的一个国家名称? Random random = new Random(); for (int i = 1; i <= 3; i++) { List list = new ArrayList<>(10); for (int j = 0; j < 4; j++) { list.add(countryList.remove(random.nextInt(countryList.size()))); } result.put(i, list); } result.put(4, countryList); return result; }
private long id;
private double balance;
private String password; 要求完善设计,使得该Account 对象能够自动分配自增id。给定一个List 如下:
List
list.add(new Account(10.00, “1234”));
list.add(new Account(15.00, “5678”));
list.add(new Account(0, “1010”)); 要求把List 中的内容放到一个Map 中,该Map 的键为id,值为相应的Account 对象。 最后遍历这个Map,打印所有Account 对象的id 和余额
@Setter @Getter @ToString class Account { private Long id; private Double balance; private String password; private static long idIndex = 1001; public Account(Double balance, String password) { this.id = idIndex++; this.balance = balance; this.password = password; } }
private static void demo3() { Listlist = new ArrayList<>(); list.add(new Account(10.00, "1234")); list.add(new Account(15.00, "5678")); list.add(new Account(0D, "1010")); Map map = new HashMap<>(16); for (Account account : list) { map.put(account.getId(), account); } map.forEach((k, v) -> System.out.println(k + "---" + v)); }