首页 > 其他 > 详细

集合知识点总结

时间:2021-04-05 21:39:13      阅读:13      评论:0      收藏:0      [点我收藏+]

集合

  • 数组是定长的 , 长度一旦确定就无法更改   
  • 集合是不限长的
  • 数组中数组元素类型是一致的
  • 集合中不限制类型 可以放任意数据类型(集合中不能有基本数据类型 , 只能放对象)
  • 集合中提供了较多的数据结构

Collection接口

只能放单值

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中数据存取有顺序

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);
    }

 

List接口的实现类

ArrayList

ArrayList底层是数组 

查询效率高  指定索引新增/删除效率较低

如果调用无参构造方法 数组其实是空数组 

在第一次add的时候进行扩容 生成一个默认长度为10的数组

LinkedList

底层是双向链表

查询效率较低  指定索引新增/删除性能较高

ArrayList与LinkedList的区别

ArrayList底层是数组 内存空间连续 在进行删除和添加的时候会进行数组动态扩容和数组元素移动

LinkeList底层是双链表  

ArrayList查询性能较高是因为地址空间连续 ; 在指定位置新增/删除元素较低是因为有会发生元素移动

LinkedList查询要对链表进行遍历(从头或从未)所以性能较低 ; 在新增/删除元素的时候只需要改动前后两个节点所以性能较高

Vector

底层也是数组

方法内部增加了线程同步 , 线程安全

对List进行遍历

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中数据不能重复

Set中的数据存取无序 没有索引值

set的实现类

HashSet底层是HashMap(hash表  数组+链表)

LinkedSet底层是LinkeHashMap

TreeSet底层是TreeMap  (TreeMap底层是红黑树一颗平衡树 ( 每个树节点都有颜色 红色或黑色 ) 泛型类型要实现接口Comparable或在TreeMap构造方法中直接提供比较器)

Set的遍历

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接口

Map内存放的是成对的元素 key-value

key不能重复

Map接口中的方法

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);
    }

 

Map的实现类

HashMap

底层使用哈希表(hash表/映射表)

哈希表是一种数据结构 , 其中使用了hash算法

hash算法 : 将一个无限的数据映射到有限的范围内 ; 根据出现不同的数据只用hash算法映射到同一个值上面 , 这种情况就是hash冲突(hash冲突 两个对象的hash值不同 , 但是映射到的索引值相同) ; hash算法是为了让数据尽量分散 , 然而hash冲突无法避免 只能尽量减少

HashMap内部使用 : 数组+链表的映射关系实现hash表 ; JDK8之后加入了红黑树(目的是为了提高搜索性能) 数组+链表+红黑树

Hash表的出现中和了数组和链表的优势

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);
    }

 

 put()代码

 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;
    }

 

get()代码 

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;
    }

 

 sesize()代码

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

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);
    }

 

 HashTable/ConcurrentHashMap

底层也是hash表

在多线程情况下使用

HashTable线程安全

TreeMap

底层是红黑树

遍历出来的结果有顺序 , 按照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);
    }

 

匿名内部类&lambda表达式

匿名内部类

可以用于抽象类或接口中 , 和普通类定义使用并无区别 只是一个无名的实现类 一次性使用所以没有名字 所以叫匿名内部类

匿名内部类中只需要重写重写抽象方法即可

常用于线程 , 集合

//抽象类或接口的定义
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表达式

仅用于接口中只有一个抽象方法时 , 匿名内部类可以简化为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

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!