运维开发网

新手了解java 集合基础知识(一)

运维开发网 https://www.qedev.com 2021-07-06 10:08 出处:网络 作者: 王星伟QAQ
目录一、概述java集合体系结构:二、collection1、List1)ArrayList2)LinkedList2、set1)HashSet2)TreeSet1.实体类2.测试类:3.实体类4.测试类三、Map1、HashMap2、TreeMap3.ConcurrentHashMap总结一、概述
目录
  • 一、概述
    • java集合体系结构:
  • 二、collection
    • 1、List
      • 1)ArrayList
      • 2)LinkedList
  • 2、set
    • 1)HashSet
      • 2)TreeSet
        • 1.实体类
        • 2.测试类:
        • 3.实体类
        • 4.测试类
    • 三、Map
      • 1、HashMap
        • 2、TreeMap
          • 3.ConcurrentHashMap
          • 总结

            一、概述

            集合是一种长度可变,存储数据的数据结构多样,存储对象多样的一种数据容器。Java中集合可分为:List集合、Set集合、HashMap集合,等。

            Java集合体系结构:

            新手了解java 集合基础知识(一)

            二、collection

            collection是Java中所有值存储集合的顶级接口,因此它的所有直接或者间接实现类都有它的非私有方法,我们可以从它的方法开始了解这个体系的功能实现。

             boolean add(E e) 
                      确保此 collection 包含指定的元素。 
             boolean addAll(Collection<? extends E> c) 
                      将指定 collection 中的所有元素都添加到此 collection 中。 
             void clear() 
                      移除此 collection 中的所有元素。 
             boolean contains(Object o) 
                      如果此 collection 包含指定的元素,则返回 true。 
             boolean containsAll(Collection<?> c) 
                      如果此 collection 包含指定 collection 中的所有元素,则返回 true。 
             boolean equals(Object o) 
                      比较此 collection 与指定对象是否相等。 
             int hashCode() 
                      返回此 collection 的哈希码值。 
             boolean isEmpty() 
                      如果此 collection 不包含元素,则返回 true。 
             Iterator<E> iterator() 
                      返回在此 collection 的元素上进行迭代的迭代器。 
             boolean remove(Object o) 
                      从此 collection 中移除指定元素的单个实例,如果存在的话)。 
             boolean removeAll(Collection<?> c) 
                      移除此 collection 中那些也包含在指定 collection 中的所有元素。 
             boolean retainAll(Collection<?> c) 
                      仅保留此 collection 中那些也包含在指定 collection 的元素。 
             int size() 
                      返回此 collection 中的元素数。 
             Object[] toArray() 
                      返回包含此 collection 中所有元素的数组。 
            <T> T[] 
             toArray(T[] a) 
                      返回包含此 collection 中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同。 
            

            1、List

            List,是单列集合,存储的是一组插入有序的数据,并且数据可以重复。

            List集合

            • LinkedList
            • ArrayList

            1)ArrayList

            示例:

            public class CollectionTest {
                public s编程客栈tatic void main(String[] args) {
                    List list = new ArrayList();
                    //添加元素,boolean add(E e) 确保此 collection 包含指定的元素
                    lwww.cppcns.comist.add("张三");
                    list.add(1);
                    list.add('A');
                    System.out.println(list);//[张三, 1, A]
                    //boolean addAll(Collection<? extends E> c)
                    //          将指定 collection 中的所有元素都添加到此 collection 中
                    List list1 = new ArrayList();
                    list.add("java");
                    list.add("mysql");
                    list.addAll(list1);
                    System.out.println(list);//[张三, 1, A, java, MySQL]
                    //boolean contains(Object o)
                    //          如果此 collection 包含指定的元素,则返回 true。
                    System.out.println(list.contains("java"));//true
                    //boolean remove(Object o)
                    //          从此 collection 中移除指定元素的单个实例,如果存在的话)。
                    Sy编程客栈stem.out.println(list.remove("java"));//true
                    // int size()
                    //          返回此 collection 中的元素数。
                    System.out.println(list.size());//4
                    //set(int index, E element)
                    //          用指定的元素替代此列表中指定位置上的元素。
                    //并返回被修改的值
                    System.out.println(list.set(1, "李四"));
                    //get(int index) 
                    //          返回此列表中指定位置上的元素。
                    System.out.println(list.get(1));
                    // Iterator<E> iterator()
                    //          返回在此 collection 的元素上进行迭代的迭代器。
                    //集合的遍历
                    Iterator iterator = list.iterator();
                    while (iterator.hasNext()){
                        System.out.println(iterator.next());
                    }
                }
            

            说明:ArrayList底层是使用数组的形式创建集合的,因此基于数组的特性,此集合对数据的查找很快速,但是在删除或移动大量数据操作上会显得缓慢。它适合用于快速查找,但不适合做删除多的操作。

            2)LinkedList

            LinkedList:双向链表,内部没有声明数组,而是定义了Node类型的first 和last,用于记录首末元素。同时,定义内部类Node,作为LinkedList中 保存数据的基本结构。Node除了保存数据,还定义了两个变量:

            • prev变量记录前一个元素的位置
            • next变量记录下一个元素的位置

            特点:

            • 数据有序
            • 底层结构为链表

            ArrayList比较:

            • LinkedList的添加元素速度比ArrayList快;
            • LinkedList的查询速度比ArrayList慢;
            • 底层数据结构不同:LinkedList用的是链表结构,而ArrayList底层使用 的是数组结构;

            说明:LinkedList一般用于添加频繁的操作,ArrayList一般用于频繁查询 的操作。

            新手了解java 集合基础知识(一)

            示例:

            public class Stack {
                private LinkedList data = null;
                public Stack(){
                    data = new LinkedList();
                }
                // 添加元素
                public boolean push(Object element) {
                    data.addFirst(element);
                    return true;
                }
                // 获取元素
                public Object pop() {
                    return data.pollFirst();
                }
                // 判断集合是否为空
                public boolean isEmpty() {
                    return data.isEmpty();
                }
                // 迭代元素
                public void list() {
                    Iterator it = data.iterator();
                    while(it.hasNext()){
                        System.out.println(it.next());
                    }
                }
            }
            public class MyStack {
                public static void main(String[] args) {
                    Stack stack = new Stack();
                    stack.push("张三");
                    stack.push("李四");
                    stack.push("王五");
                    stack.list();
                    System.out.println("-------------");
                    Object pop = stack.pop();
                    System.out.println(pop);
                }
            }
            

            2、set

            1)HashSet

            HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用 这个实现类。

            • HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取、 查找、删除性能。
              • HashSet 具有以下特点:不能保证元素的排列顺序
              • HashSet 不是线程安全的
              • 集合元素可以是 null
              • 不能添加重复元素
            • HashSet 集合判断两个元素相等的标准:两个对象通过 hashCode() 方法比较相等,并且两个对象的 equals() 方法返回值也相等。
            • 对于存放在Set容器中的对象,对应的类一定要重写equals()和 hashCode(Object obj)方法,以实现对象相等规则。即:“相等的对象必须具有相等的散列码”。

            示例:

             public static void main(String[] args) {
                    Set set = new HashSet();
                    // 添加
                    // boolean add(E e) :把指定的元素添加到集合中
                    set.add("hello");
                    set.add("world");
                    set.add("world");
                    set.add(null);
                    System.out.println(set);
                    // 注:Set集合中元素是无序,并且不能重复
                    // boolean addAll(Collection<? extends E> c) :把指定的集合添加到集合中
                    Set set1 = new HashSet();
                    set1.add("aaa");
                    set1.add("Linux");
                    ;
                    set.addAll(set1);
                    System.out.println(set);
                    // boolean remove(Object o) :从集合中删除指定元素
                    set.remove("hello");
                    System.out.println(set);
                    // boolean removeAll(Collection<?> c) :从集合中删除指定集合中的所有元素
                    set1.add("aaa");
                    set1.add("Linux");
                    set.removeAll(set1);
                    System.out.println(set);
                    // void clear() :清空集合中所有元素
                    set.clear();
                    System.out.println(set);
                    // int size() :获取集合的元素个数
                    int size = set.size();
                    System.out.println(size);
                    // boolean contains(Object o) :判断集合中是否包含指定元素,包含为true,否则为false;
                    System.out.println(set.contains("aaa"));
            
                    // boolean isEmpty() :判断集合是否为空
                    System.out.println(set.isEmpty());
                }
            

            说明:在HashSet添加元素时,会首先比较两个元素的hashCode值是不相等,如 果不相等则直接添加;如果相等再判断两个元素的equals的值是否相等, 如果相等则不添加,如果不相等则添加。

            2)TreeSet

            • TreeSet和TreeMap采用红黑树的存储结构
            • 特点:有序,查询速度比List快

            使用TreeSet集合是,对象必须具有可比较性。而要让对象具有可比较性有 两种方式:

            第一种:实现Comparable接口,并重写compareTo()方法:

            第二种:写一个比较器类,让该类去实现Comparator接口,并重写 comare()方法。

            示例:

            1.实体类

            public class Student implements Comparable<Student>{
                private String name;
                private int age;
                private String sex;
                private int height;
            
                public Student() {
                }
                public Student(String name, int age, String sex, int height) {
                    this.name = name;
                    this.age = age;
                    this.sex = sex;
                    this.height = height;
                }
                public String getName() {
                    return name;
                }
                public void setName(String name) {
                    this.name = name;
                }
                public int getAge() {
                    return age;
                }
                public void setAge(int age) {
                    this.age = age;
                }
                public String getSex() {
                    return sex;
                }
                public void setSex(String sex) {
                    this.sex = sex;
                }
                public int getHeight() {
                    return height;
                }
                public void setHeight(int height) {
                    this.height = height;
                }
                @Override
                public boolean equals(Object o) {
                    if (this == o) return true;
                    if (o == null || getClass() != o.getClass()) return false;
                    Student student = (Student) o;
                    return age == student.age &&
                            height == student.height &&
                            Objects.equals(name, student.name) &&
                            Objects.equals(sex, student.sex);
                }
                @Override
                public int hashCode() {
                    return Objects.hash(name, age, sex, height);
                }
                @Override
                public String toString() {
                    return "Student{" +
                            "name='" + name + '\'' +
                            ", age=" + age +
                            ", sex='" + sex + '\'' +
                            ", height=" + height +
                            '}';
                }
            
                @Override
                public int compareTo(Student stu) {
                    if (stu.getAge() > this.getAge()){
                        return 1;
                    }
                    if (stu.getAge() < this.getAge()){
                        return -1;
                    }
                    return stu.getName().compareTo(this.getName());
                }
            }
            

            2.测试类:

            public class TreeSetTest {
                public static void main(String[] args) {
            
                    TreeSet treeSet = new TreeSet();
                    Student student1 = new Student("张三", 20, "男", 165);
                    Student student2 = new Student("李四", 21, "男", 170);
                    Student student3 = new Student("王五", 19, "女", 160);
                    Student student4 = new Student("赵六", 18, "女", 165);
                    Student student5 = new Student("田七", 20, "男", 175);
                    treeSet.add(student1);
                    treeSet.add(student2);
                    treeSet.add(student3);
                    treeSet.add(student4);
                    treeSet.add(student5);
                    System.out.println(treeSet);
                }
            }
            

            3.实体类

            public class Teacher {
                private String name;
                public Teacher(){}
                public Teacher(String name){
                    this.name = name;
                }
                public String getName() {
                    return name;
                }
                public void setName(String name) {
                    this.name = name;
                }
                @Override
                public String toString() {
                    return "Teacher{" +
                            "name='" + name + '\'' +
                            '}';
                }
            }
            

            4.测试类

            public class TreeSetTest2 {
                public static void main(String[] args) {
                    Teacher teacher1 = new Teacher("11");
                    Teacher teacher2 = new Teacher("12");
                    Teacher teacher3 = new Teacher("13");
                    Teacher teacher4 = new Teacher("14");
                    Teacher teacher5 = new Teacher("15");
                    TreeSet treeSet1 = new TreeSet(new  Comparator() {
                        @Override
                        public int compare(Object o1, Object o2) {
                            return o1.hashCode() - o2.hashCode();
                        }
                    });
                    treeSet1.add(teacher1);
                    treeSet1.add(teacher2);
                    treeSet1.add(teacher3);
                    treeSet1.add(teacher4);
                    treeSet1.add(teacher5);
                    System.out.println(treeSet1);
                }
            }
            

            说明:HashSet去重是依靠hashCodeequals()方法,而TreeSet去重则 依靠的是比较器。

            三、Map

            ​ 存储的双列元素,Key是无序的,不可重复,而Value是无序,可重复的。

            1、HashMap

            public class HashMapDemo {
                private Map map = null;
                public void init() {
                    map = new HashMap();
                    map.put("a", "aaa");
                    map.put("b", "bbb");
                    map.put("c", "ccc");
                    System.out.println(map);
                }
                // 添加元素
                public void testPut() {
                    // V put(K key, V value) :把指定的key和value添加到集合中
                    map.put("a1", "aaa");
                    map.put("b1", "bbb");
                    map.put("c1", "ccc");
                    System.out.println(map);
                    // void putAll(Map<? extends K,? extends V>m) :把指定集合添加集合中
                    Map map1 = new HashMap();
                    map1.put("e", "eee");
                    map1.put("f", "fff");
                    map.putAll(map1);
                    System.out.println(map);
                    // default V putIfAbsent(K key, V value) :如果key不存在就添加
                    map.putIfAbsent("a", "hello");
                    System.out.println(map);
                    map.putIfAbsent("g", "ggg");
                    System.out.println(map);
                }
                // 修改元素
                public void testModify() {
                    // V put(K key, V value) :把集合中指定key的值修改为指定的值
                    map.put("a", "hello");
                    map.put("a", "world");
                    System.out.println(map);
                    // 说明,当key相同时,后面的值会覆盖前面的值。
                    // default V replace(K key, V value) :根据key来替换值,而不做增加操作
                    Object replace = map.replace("b1", "java");
                    System.out.println(replace);
                    System.out.println(map);
                    //default boolean replace(K key, V oldValue,V newValue)
                }
                // 删除元素
                public void testRemove() {
                    // V remove(Object key) :根据指定key删除集合中对应的值
                    Object c = map.remove("c");
                    System.out.println(c);
                    System.out.println(map);
                    // default boolean remove(Object key, Objectvalue) :根据key和value进行删除
                    map.remove("b", "bbb1");
                    System.out.println(map);
                    // void clear() :清空集合中所有元素
                    map.clear();
                    System.out.println(map);
                }
                // 判断元素
                public void testJudge() {
                    // boolean isEmpty() :判断集合是否为空,如果是返回true,否则返回false
                    System.out.println(map.isEmpty());
                    // boolean containsKey(Object key) :判断集合中是否包含指定的key,包含返回true,否则返回false
                    boolean flag = map.containsKey("a");
                    System.out.println(flag); // true
                    flag = map.containsKey("a1");
                    System.out.println(flag); // false
                    // boolean containsValue(Object value) :判断集合中是否包含指定的value,包含返回true,否则返回false
                    flag = map.containsValue("aaa");
                    System.out.println(flag); // true
                    flag = map.containsValue("aaa1");
                    System.out.println(flag); // false
                }
                // 获取元素
                public void testGet() {
                    // int size() :返回集合的元素个数
                    int size = map.size();
                    System.out.println(size);
                    // V get(Object key) :根据Key获取值,如果找到就返回对应的值,否则返回null
                    Object val = map.get("a");
                    System.out.println(val);
                    val = map.get("a1");
                    System.out.println(val); // null
                    // default V getOrDefault(Object key, VdefaultValue) :根据Key获取值,如果key不存在,则返回默认值
                    val = map.getOrDefault("a1", "hello");
                    System.out.println(val);
                    // Collection<V> values() :返回集合中所有的Value
                    Collection values = map.values();
                    for (Object value : values) {
                        System.out.println(value);
                    }
                    // Set<K> keySet() :返回集合中所有的Key
                    Set set = map.keySet();
                    for (Object o : set) {
                        System.out.println(o);
                    }
                }
                // 迭代元素
                public void testIterator() {
                    // 第一种:通过key获取值的方式
                    Set keySet = map.keySet();
                    Iterator it = keySet.iterator();
                    while (it.hasNext()) {
                        Object key = it.next();
                        Object val = map.get(key);
                        System.out.println(key + "=" + val);
                    }
                    System.out.println("------------------------ ");
                    // 第二种:使用for循环
                    for (Object key : map.keySet()) {
                        System.out.println(key + "=" +
                                           map.get(key));
                    }
                    System.out.println("------------------------ ");
                    // 第三种:使用Map接口中的内部类来完成,在框架中大量使用
                    Set entrySet = map.entrySet();
                    for (Object obj : entrySet) {
                        Map.Entry entry = (Map.Entry) obj;
                        System.out.println(entry.getKey() + "=" +
                                           entry.getValue());
                    }
                }
            }
            

            说明:在HashMap中键-值允许为空,但键唯一,值可重复。hashMap不是线程安全的。

            2、TreeMap

            是一个有序的集合,默认使用的是自然排序方式。

            public class Person implements Comparable {
                private String name;
                private int age;
                @Override
                public int compareTo(Object o) {
                if (o instanceof Person) {
                Person p = (Person) o;
                return this.age - p.age;
                }
                return 0;
            }
                public Person() {}
                public Person(String name, int age) {
                    this.name = name;
                    this.age = age;
                }
                public int getAge() {
                    return age;
                }
                public void setAge(int age) {
                    this.age = age;
                }
                @Override
                public String toString() {
                    return "Person{" +
                        "name='" + name + '\'' +
                        ", age=" + age +
                        '}';
                }
            }
            

            测试

            public class TeeMapDemo {
                @Test
                public void testInteger() {
                    TreeMap tm = new TreeMap();
                    tm.put(3, 333);
                    tm.put(2, 222);
                    tm.put(11, 111);
                    tm.put(2, 222);
                    System.out.println(tm);
                }
                @Test
                public void testSt编程客栈ring() {
                    TreeMap tm = new TreeMap();
                    tm.put("hello", "hello");
                    tm.put("world", "world");
                    tm.put("about", "");
                    tm.put("abstract", "");
                    System.out.println(tm);
                }
                @Test
                public void testPerson() {
                    TreeMap tm = new TreeMap(new Comparator(){
                        @Override
                        public int compare(Object o1, Object o2) {
                            if (o1 instanceof Person && o2
                                instanceof Person) {
                                Person p1 = (Person) o1;
                                Person p2 = (Person) o2;
                                return p1.getAge() - p2.getAge();
                            }
                            return 0;
                        }
                    });
                    tm.put(new Person("张三",18), null);
                    tm.put(new Person("李四",17), null);
                    System.out.println(tm);
                }
            }
            

            说明:从上面的代码可以发现,TreeMap的使用和TreeSet的使用非常相似,观察HashSet集合的源代码可以看出,当创建 HashSet集合时,其实是底层使用的是HashMap。

            public HashSet() {
            	map = new HashMap<>();
            }
            

            HashSet实际上存的是HashMap的Key。

            3.ConcurrentHashMap

            在Map集合中我们介绍了HashMapTreeMap,在多线程的情况下这些集合都不是线程安全的,因此可能出现线程安全的问题。

            在Java中Hashtable是一种线程安全的HashMapHashtable在方法上与HashMap并无区别,仅仅只是在方法使用了synchronized以此来达到线程安全的目的,我们观察Hashtable的源码。

                public synchronized V get(Object key) {
                    Entry<?,?> tab[] = table;
                    int hash = key.hashCode();
                    int index = (hash & 0x7FFFFFFF) % tab.length;
                    for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
                        if ((e.hash == hash) && e.key.equals(key)) {
                            return (V)e.value;
                        }
                    }
                    return null;
                }
            

            以上是Hashtable的get源码,可以看出它仅仅只是在方法上添加了锁,这大大降低了线程的执行效率,以牺牲效率的形式来达到目的,这显然不是我们在实际中想要的,因此我们需要一种既能在线程安全方面有保障,在效率上还可以的方法。

            ConcurrentHashMap采用的是分段锁的原理,我们观察源码。

             public V put(K key, V value) {
                    return putVal(key, value, false);
                }
            final V putVal(K key, V value, boolean onlyIfAbsent) {
                    if (key == null || value == null) throw new NullPointerException();
                    iJMLpMzyRnt hash = spread(key.hashCode());
                    int binCount = 0;
                    for (Node<K,V>[] tab = table;;) {
                        Node<K,V> f; int n, i, fh;
                        if (tab == null || (n = tab.length) == 0)
                            tab = initTable();
                        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                            if (casTabAt(tab, i, null,
                                         new Node<K,V>(hash, key, value, null)))
                                break;                   // no lock when adding to empty bin
                        }
                        else if ((fh = f.hash) == MOVED)
                            tab = helpTransfer(tab, f);
                        else {
                            V oldVal = null;
                            synchronized (f) {
                                if (tabAt(tab, i) == f) {
                                    if (fh >= 0) {
                                        binCount = 1;
                                        for (Node<K,V> e = f;; ++binCount) {
                                            K ek;
                                            if (e.hash == hash &&
                                                ((ek = e.key) == key ||
                                                 (ek != null && key.equals(ek)))) {
                                                oldVal = e.val;
                                                if (!onlyIfAbsent)
                                                    e.val = value;
                                                break;
                                            }
                                            Node<K,V> pred = e;
                                            if ((e = e.next) == null) {
                                                pred.next = new Node<K,V>(hash, key,
                                                                          value, null);
                                                break;
                                            }
                                        }
                                    }
                                    else if (f instanceof TreeBin) {
                                        Node<K,V> p;
                                        binCount = 2;
                                        if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                                       value)) != null) {
                                            oldVal = p.val;
                                            if (!onlyIfAbsent)
                                                p.val = value;
                                        }
                                    }
                                }
                            }
                            if (binCount != 0) {
                                if (binCount >= TREEIFY_THRESHOLD)
                                    treeifyBin(tab, i);
                                if (oldVal != null)
                                    return oldVal;
                                break;
                            }
                        }
                    }
                    addCount(1L, binCount);
                    return null;
                }
            

            从源码中可以看出ConcurrentHashMap仅仅是在当有线程去操作当前数据的时候添加了锁,因此效率大大提高了。

            在线程安全的情况下提高了效率。

            总结

            本篇文章就到这里了,希望能对你有所帮助,也希望您能够多多关注我们的更多内容!

            0

            精彩评论

            暂无评论...
            验证码 换一张
            取 消