只能放单值
Collection接口中定义的方法
public static void collectionMethods() { Collection collection = new ArrayList(); Collection collection2 = new ArrayList(); collection2.add(99); collection2.add(66); // isEmpty()集合有无元素 System.out.println("集合是否无元素 " + collection.isEmpty()); // add() 添加集合元素 int-> Integer boolean add = collection.add(123); boolean add1 = collection.add("abc"); System.out.println("添加是否成功 " + add); // size()集合大小 int num = collection.size(); // isEmpty()集合是否无元素 System.out.println("集合是否无元素 " + collection.isEmpty()); System.out.println("元素个数 " + num); //contains()是否包含某个对象 boolean contains = collection.contains("abc"); System.out.println("是否包含123这个对象 " + contains); // toArray() Collection实现类转数组 Object[] objects = collection.toArray(); System.out.println("转数组 " + Arrays.toString(objects)); // remove() 从集合中移除某个对象 boolean abc = collection.remove("abc"); System.out.println("移除 " + abc); // containsAll() 是否包含另一个集合的所有对象 boolean b = collection.containsAll(collection2); System.out.println("是否包含 另外一个集合的所有元素 " + b); // addAll() 添加另一个集合的所有元素 boolean b1 = collection.addAll(collection2); // retainAll() 只保留另一个集合中存在的元素 其余移除 boolean b2 = collection.retainAll(collection2); // clear() 清空集合 collection.clear(); System.out.println("集合元素内容 " + collection); }
List中数据是可重复的
List中数据存取有顺序
List接口中的方法
public static void listMethods() { List list = new ArrayList(); list.add("abc"); list.add("dd"); list.add("aa"); list.add("cc"); list.add("dd"); // get() 指定索引获取对象 索引值不能超过size大小 Object o = list.get(1); System.out.println("获取对象" + o);//abc // set()指定索引位置进行替换对象 Object set = list.set(0, 666); // insert 指定索引进行插入 list.add(1, 999); // remove(int index)指定索引移除对象 Object remove = list.remove(0); // indexOf() 返回指定对象首次出现索引值 int dd = list.indexOf("dd"); System.out.println("指定对象首次出现的索引值 " + dd); int dd1 = list.lastIndexOf("dd"); System.out.println("指定对象最后一次出现的索引值 " + dd1); // 截取子集合 结束索引不包含 [) List list1 = list.subList(1, 3); System.out.println("元素内容 " + list); System.out.println("子list "+list1); }
ArrayList底层是数组
查询效率高 指定索引新增/删除效率较低
如果调用无参构造方法 数组其实是空数组
在第一次add的时候进行扩容 生成一个默认长度为10的数组
底层是双向链表
查询效率较低 指定索引新增/删除性能较高
ArrayList与LinkedList的区别
ArrayList底层是数组 内存空间连续 在进行删除和添加的时候会进行数组动态扩容和数组元素移动
LinkeList底层是双链表
ArrayList查询性能较高是因为地址空间连续 ; 在指定位置新增/删除元素较低是因为有会发生元素移动
LinkedList查询要对链表进行遍历(从头或从未)所以性能较低 ; 在新增/删除元素的时候只需要改动前后两个节点所以性能较高
底层也是数组
方法内部增加了线程同步 , 线程安全
1. 普通for循环遍历
2. 增强for循环遍历
3. 迭代器遍历
4. forEach遍历
public static void main(String[] args) { ArrayList<String> arrayList = new ArrayList<>(); arrayList.add("hello"); arrayList.add("java"); arrayList.add("oracle"); // System.out.println(arrayList.size()); System.out.println("-----1普通for循环遍历"); // 0- size-1 for (int i = 0; i < arrayList.size(); i++) { System.out.println(arrayList.get(i)); } System.out.println("------2 增强for循环遍历"); for (String str : arrayList) { System.out.println(str); } System.out.println("-------3 迭代器进行遍历"); Iterator<String> iterator = arrayList.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } System.out.println("------4 forEach()"); // 匿名内部类 一次性使用 arrayList.forEach(new Consumer<String>() { @Override public void accept(String s) { // System.out.println(s); } }); // jdk8以后 lambda表达式 JS箭头函数 ()=>{} arrayList.forEach((s)->{ System.out.println(s); }); // arrayList.forEach(new CustomConsumer()); }
set中数据不能重复
Set中的数据存取无序 没有索引值
HashSet底层是HashMap(hash表 数组+链表)
LinkedSet底层是LinkeHashMap
TreeSet底层是TreeMap (TreeMap底层是红黑树一颗平衡树 ( 每个树节点都有颜色 红色或黑色 ) 泛型类型要实现接口Comparable或在TreeMap构造方法中直接提供比较器)
1. 增强for循环
2. iterator迭代器
3. forEach
public static void main(String[] args) { // HashSet底层式HashMap(哈希表 数组+链表) Set<String> str1 = new HashSet<>(); // LinkedHashSet底层LinkedHashMap Set<String> str2 = new LinkedHashSet<>(); // TreeSet 底层是 TreeMap底层是红黑树是一颗平衡树(每个树节点都有颜色 要么是红色 要么是黑色) Set<String> str3 = new TreeSet<>(); str1.add("abc"); str1.add("www"); str1.add("abc"); System.out.println(str1); System.out.println("-----1 增强for"); for (String s : str1) { System.out.println(s); } System.out.println("----2 iterator"); Iterator<String> iterator = str1.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } System.out.println("---3 forEach "); str1.forEach((str) -> { System.out.println(str); }); }
Map内存放的是成对的元素 key-value
key不能重复
put()增添元素
size()获取元素个数
containsKey()是否包含某个key
containsValue()是否包含某个value
remove()根据指定key移除元素
keyset()获取key的集合
values()获取value的集合
replace()覆盖指定key的value
public static void mapMethods() { // 第一个是key的类型 第二个是value类型 Map<Integer, String> map = new HashMap(); // 1->or map.put(1, "a"); map.put(2, "w"); map.put(6, "java"); String s = map.get(1); System.out.println("key 1对应的value值 " + s); System.out.println("size " + map.size()); System.out.println("是否包含某个key " + map.containsKey(2)); System.out.println("是否包含指定value " + map.containsValue("w")); // 按照指定的key移除map元素 // String remove = map.remove(2); // 按照指定的key和value移除元素 boolean hello = map.remove(2, "hello"); Set<Integer> integers = map.keySet(); System.out.println("key的集合 " + integers); Collection<String> values = map.values(); System.out.println("value的集合 " + values); boolean replace = map.replace(6, "java", "oracle"); System.out.println(map); }
底层使用哈希表(hash表/映射表)
哈希表是一种数据结构 , 其中使用了hash算法
hash算法 : 将一个无限的数据映射到有限的范围内 ; 根据出现不同的数据只用hash算法映射到同一个值上面 , 这种情况就是hash冲突(hash冲突 两个对象的hash值不同 , 但是映射到的索引值相同) ; hash算法是为了让数据尽量分散 , 然而hash冲突无法避免 只能尽量减少
HashMap内部使用 : 数组+链表的映射关系实现hash表 ; JDK8之后加入了红黑树(目的是为了提高搜索性能) 数组+链表+红黑树
Hash表的出现中和了数组和链表的优势
/** * 对数进行二次加工 减少hash冲突 * 00101010 00101011 00101010 00101010 第一个key对象的hash值 * 00000000 00000000 00101010 00101011 * ----------------------------------- * 00000001 * 1111 * 0001 * <p> * 00101010 00101010 00101010 00111010 * 00000000 00000000 00101010 00101010 * ----------------------------------- * 00010000 * 1111 * 0000 0 * ------------------------------------------- * 让高位和低位都参与运算 是为了让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<K, V>[] tab; Node<K, V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0) // 如果数组初始为null就扩容 新数组长度赋值为 n=16 n = (tab = resize()).length; // 0-15 20 % 16 取模 4 4 20; hash & (n-1) 与hash%n 结果等价(前提条件是 n是2的幂次方) if ((p = tab[i = hash % n]) == null) // 数组元素赋值 tab[i] = newNode(hash, key, value, null); else { // 表示索引对应的数组元素有值 Node<K, V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; // hash冲突 TreeNode是红黑树节点类型 else if (p instanceof TreeNode) // 向红黑树中新增元素 e = ((TreeNode<K, V>) p).putTreeVal(this, tab, hash, key, value); else { // for (int binCount = 0; ; ++binCount) { // 表示p节点后面没有节点了 if ((e = p.next) == null) { // p的next指向新节点 p.next = newNode(hash, key, value, null); // 目前是否需要把链表转红黑树 链表节点个数达到8 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } // 找到是待替换的 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; //下一个节点 p = e; } } // 当hash值相同 并且两个key对象 equals时候 其实就是value的替换 if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } // 记录被改动多少次 ++modCount; if (++size > threshold) { // hash数组扩容 resize(); } afterNodeInsertion(evict); return null; }
final Node<K, V> getNode(int hash, Object key) { // Node[] tab; Node first, e; int n; Object k; // if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { //直接找到的是第一个map元素 if (first.hash == hash && // always check first node ((k = first.key) == key || (key != null && key.equals(k)))) return first; // 找到的元素虽然索引一样 单不是要找到 元素 e = first.next; if (e != null) { // 判断节点是否是 红黑树节点 if (first instanceof TreeNode) return ((TreeNode<K, V>) first).getTreeNode(hash, key); // 表示此节点是链表节点 do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { return e; } e = e.next; } while (e != null); } } return null; }
final Node<K, V>[] resize() { // Node[] oldTab = table; // oldCap 代表旧数组长度 int oldCap = (oldTab == null) ? 0 : oldTab.length; // 旧的临界值 int oldThr = threshold; // newCap新数组长度 int newCap, newThr = 0; if (oldCap > 0) { if (oldCap >= MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return oldTab; // 新数组的长度是 旧数组长度2倍 } 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 // 给新数组长度 赋值 16 newCap = DEFAULT_INITIAL_CAPACITY; // 给新的临界值 赋值 newThr = (int) (DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } if (newThr == 0) { float ft = (float) newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float) MAXIMUM_CAPACITY ? (int) ft : Integer.MAX_VALUE); } // 给成员变量 临界值 赋值 24 threshold = newThr; @SuppressWarnings({"rawtypes", "unchecked"}) // 第一次数组扩容 长度是16 Node<K, V>[] newTab = (Node<K, V>[]) new Node[newCap]; // 成员变量 hash表 赋值引用地址 table = newTab; // 判断是否为 首次扩容 if (oldTab != null) { // 遍历旧数组 for (int j = 0; j < oldCap; ++j) { Node<K, V> e; // 遍历到的数组元素不为null 的时候 if ((e = oldTab[j]) != null) { //为了GC回收对象 oldTab[j] = null; // 此节点没有兄弟节点 if (e.next == null) // 3 3+16 /** *11111 *00011 3 * ----- * 3 在新数组中的索引位置 有可能发生改变 */ newTab[e.hash & (newCap - 1)] = e; //判断此节点是否为树节点 else if (e instanceof TreeNode) ((TreeNode<K, V>) e).split(this, newTab, j, oldCap); // 后面是链表 else { // preserve order // 低位的头 低位的尾部 15 Node<K, V> loHead = null, loTail = null; // 高位的 31 Node<K, V> hiHead = null, hiTail = null; Node<K, V> next; do { // 看高位是否为0 如果为0继续放在低位 否则放入到高位 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; } next = e.next; } 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; }
LinkedHashMap时HashMap的子类
能够保证存放和读取出来的顺序
LinkedHashMap是双链表和hash表的结合
public static void linkedHashMapMethod1() { /* LinkedHashMap<String, String> stringLinkedHashMap = new LinkedHashMap<>(); stringLinkedHashMap.put("a", "java");*/ LinkedHashMap<String, String> stringHashMap = new LinkedHashMap<>(); stringHashMap.put("c", "java"); stringHashMap.put("cc", "sun"); stringHashMap.put("ccc", "oracle"); // String cc = stringHashMap.get("cc"); System.out.println(stringHashMap); }
底层也是hash表
在多线程情况下使用
HashTable线程安全
底层是红黑树
遍历出来的结果有顺序 , 按照key进行排序
Tree的key要实现Comparable或者在TreeMap构造方法中直接提供比较器
public static void treeMapMethod() { TreeMap<Money, String> stringTreeMap = new TreeMap<>((o1, o2) -> { return o1.getValue() - o2.getValue(); }); stringTreeMap.put(new Money(10), "sm"); stringTreeMap.put(new Money(120), "sun"); stringTreeMap.put(new Money(50), "java"); System.out.println(stringTreeMap); stringTreeMap.put(new Money(50), "oracle"); System.out.println(stringTreeMap); }
可以用于抽象类或接口中 , 和普通类定义使用并无区别 只是一个无名的实现类 一次性使用所以没有名字 所以叫匿名内部类
匿名内部类中只需要重写重写抽象方法即可
常用于线程 , 集合
//抽象类或接口的定义 public abstract class Inter { public abstract void method1(); public abstract void method2(); } //实用类 public class Demo { public void demo(Inter inter) { inter.method1(); inter.method2(); } }
//匿名内部类测试 public static void main(String[] args) { Demo demo = new Demo(); //匿名内部类 demo.demo(new Inter() { @Override public void method1() { System.out.println("method1 invoked "); } @Override public void method2() { System.out.println("method1 invoked "); } }); }
仅用于接口中只有一个抽象方法时 , 匿名内部类可以简化为lambda表达式
@FunctionalIntaerFace 函数式接口 凡是接口上有这种注释的都表示里面只有一个抽象方法 都可以使用lambda表达式
//普通接口定义 public interface Inter { void method1(Integer a, String b); } //实用类 public class Demo { public void demo(Inter inter) { inter.method1(16, "abc"); } }
//lambda表达式测试类 public static void main(String[] args) { Demo demo = new Demo(); demo.demo((a, b) -> { System.out.println(a); System.out.println(b); } ); }
原文:https://www.cnblogs.com/Leo-Heng/p/14618842.html