JAVA基础——集合

文章目录

  • 前言
  • 一、集合概要及框架体系
    • 1、集合的理解
    • 2、数组的缺点
    • 3、集合的好处
    • 4、集合的框架体系
  • 二、Collection集合(单列集合)
    • Collection集合特点
    • Collection集合遍历元素方式
      • 1、迭代器的遍历方式
      • 2、增强for循环的遍历方式
    • 1、List
      • Ⅰ. ArrayList底层结构和源码分析
      • Ⅱ Vector 底层结构和源码分析
          • Vector子类——Stack(栈)
      • Ⅲ LinkedList底层结构和源码分析
      • Ⅳ. ArrayList 和 LinkedList 比较
    • 2.Set
      • 2.1HashSet
        • 2.1.1LinkedHashSet
      • 2.2TreeSet
  • 三、Map集合(双列集合)
      • 1、HashMap
      • 2、HashTable
        • 2.1 properties
      • 3、TreeMap
  • 总结:开发中如何选择这些集合?


前言

此篇为记录Java基础中集合章节的有关知识点,主要包括Collection接口、Map接口下的实现类,以及他们之间的联系和区别


一、集合概要及框架体系

1、集合的理解

  • 集合跟数组一样,它也是用来保存数据的。那么为什么还会出现集合呢?那是因为数组有许多不足的地方。集合主要是两组(单列集合、双列集合)

2、数组的缺点

  • 长度开始时必须指定,而且一旦指定,不能更改。
  • 保存的数据类型必须为同一类型的元素。
  • 使用数组进行增加/删除元素时比较麻烦(每次都要进行扩容或者缩减)

3、集合的好处

  • 可以动态保存任意类型的对象或元素,使用方便
  • 提供一系列的操作对象的方法,不用自己实现它,可以直接使用,进行添加删除新元素方便。

4、集合的框架体系

1、Java的集合类很多,主要有Collection 接口和 Map 接口两大类,它们都继承了 Iterable 接口。
2、Collection接口 最常见的是Set 和List 接口;

  • Set接口中的常用实现类有:HashSet、TreeSet
  • List接口中的常用实现类有:ArrayList 、LinkedList、Vector,其中Vector的子类有常见的Stack(栈)类

3、Map接口中常见的实现类有HashMap、TreeMap、HashTable

  • HashMap常用的继承子类有LinkedHashMap
  • HashTable常用的继承子类有Properties

JAVA基础——集合_第1张图片

二、Collection集合(单列集合)

Collection集合特点

  • Collection 实现子类可以存放多个元素,每个元素可以是Object
  • 有些Collection的实现类可以存放重复的元素,有些不可以
  • 有些Collection 的实现类是有序的(List),有些不是有序(Set)
  • Collection接口没有直接实现的子类,是通过它的子接口List和Set来实现的

Collection集合遍历元素方式

1、迭代器的遍历方式

//以hashSet为例:
        HashSet hashSet = new HashSet();
        Iterator iterator = hashSet.iterator();//1. 先得到 col 对应的 迭代器
        //使用 while 循环遍历,此循环可以使用 itit 快捷键
        //显示所有的快捷键的的快捷键 ctrl + j
        while (iterator.hasNext()) {//判断是否还有数据
            Object obj =  iterator.next();//返回下一个元素,类型是 Object
            System.out.println(obj);
        }
//当退出 while 循环后 , 这时 iterator 迭代器,指向最后的元素 iterator.next();
//NoSuchElementException 
//如果希望再次遍历,需要重置我们的迭代器 
iterator = hashSet.iterator();

注意:

  • 多次使用此迭代器时需要重置一下,因为每次循环完之后迭代器的
  • 在调用iterator.next()方法之前必须要调用iterator.hasNext()进行检测。若不调用,且下一条记录无效,直接调用it.next()会抛出NoSuchElementException异常。

2、增强for循环的遍历方式

//以HashSet为例
HashSet hashSet = new HashSet();
 for (Object obj : hashSet) {//此时可用快捷键 hashSet.I
            System.out.println(obj);
        }

1、List

List 接口是Collection接口的子接口。

  1. List集合内的元素是有序的(添加顺序和取出顺序一致)、且可以重复。
  2. List集合的每个元素都有其对应的顺序索引,即支持索引
  3. List容器中的元素是都对应一个整数型的序号记载其在容器的位置

Ⅰ. ArrayList底层结构和源码分析

一、注意事项

  1. ArrayList底层是由数组实现数据存储的。
  2. ArrayList基本等同于Vector,除了ArrayList是线程不安全的(执行效率高),而Vertor是线程安全的。不建议使用ArrayList.

二、底层操作机制源码分析
扩容机制结论:

  1. ArrayList 底层维护了一个Object类型的数组elementData。

  2. 当创建ArrayList对象时它有两种构造器,一种是无参构造,另一种是有参构造。当它创建时的长度为0时使用无参构造,则扩容elementData为10,如需要再次扩容,则是elementData的1.5倍。

  3. 当创建elementData给定一个长度时用有参构造,如需要扩容,则直接扩容为elementData的1.5倍。

  4. 在添加元素的时候,先调用特定的方法来确定是否要扩容(每次都要看是否需要扩容)

 public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

确定要扩容之后把需要扩容的大小设置为10,然后表示扩容的次数的变量自增1,如果大小不够,就调用grow()扩容(扩容到原来的1.5倍(length + length >> 1)),容量够了就不会调用grow()方法了。

private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

grow()方法源码:

 private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

Ⅱ Vector 底层结构和源码分析

一、注意事项

  • Vector集合跟ArrayList底层大致相同,只不过Vector跟ArrayList相比,它的线程安全(synchronized)
  • 它的扩容机制也跟ArrayList类似,在扩容时如果是没有给定大小,那么第一次扩容为10,但是第二次开始是按2倍扩容。
  • 与ArrayList相比,它的线程安全,但执行效率稍低。

二、debug源码

//底层同样跟ArrayList是elementData数组
public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }
private void ensureCapacityHelper(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
 private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        /**二倍扩容机制
		*/
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
Vector子类——Stack(栈)

Ⅲ LinkedList底层结构和源码分析

一、注意事项

  • LinkedList中维护了两个属性,first和last ,first指向第一个节点Node,Last指向最后一个节点Node。
  • 每个Node节点中有prev(指向前一个节点)、next(指向下一个节点)、item(内容)这三个属性,最终实现双向链表
  • LinkedList底层维护了一个双向链表,而不是数组,所以添加和删除效率比ArrayList和Vector都高。

二、DebugLinkedList的添加元素 源代码

//DebugLinkedList的添加元素 源代码
 public boolean add(E e) {
       linkLast(e);
       return true;
   }
 void linkLast(E e) {
       final Node<E> l = last;
       final Node<E> newNode = new Node<>(l, e, null);
       last = newNode;
       if (l == null)
           first = newNode;
       else
           l.next = newNode;
       size++;
       modCount++;
   }
    private static class Node<E> {
       E item;
       Node<E> next;
       Node<E> prev;

       Node(Node<E> prev, E element, Node<E> next) {
           this.item = element;
           this.next = next;
           this.prev = prev;
       }
   }

三、DebugLinkedList的插入元素 源代码

public void add(int index, E element) {
      checkPositionIndex(index);

      if (index == size)
          linkLast(element);
      else
          linkBefore(element, node(index));
  }
    /**
   * Inserts element e before non-null Node succ.
   */
  void linkBefore(E e, Node<E> succ) {
      // assert succ != null;
      final Node<E> pred = succ.prev;
      final Node<E> newNode = new Node<>(pred, e, succ);
      succ.prev = newNode;
      if (pred == null)
          first = newNode;
      else
          pred.next = newNode;
      size++;
      modCount++;
  }
   private static class Node<E> {
     E item;
     Node<E> next;
     Node<E> prev;

     Node(Node<E> prev, E element, Node<E> next) {
         this.item = element;
         this.next = next;
         this.prev = prev;
     }
 }

Ⅳ. ArrayList 和 LinkedList 比较

  • ArrayList底层是通过数组实现的,如果使用查改的操作比较多,就选ArrayList。
  • LinkedList底层是通过链表实现的,如果使用增删的操作比较多,就选LinkedList。
  • 一般来说使用改查的操作比较多,大多使用ArrayList;有时会结合使用。

2.Set

一、注意事项

  • Set集合是无序的,因此它没有索引,也就不能通过索引来获取元素,也不能用普通循环通过索引遍历元素(要用迭代器或者增强for循环)。
  • Set集合无序,所以不能存储重复的元素

2.1HashSet

一、注意事项

  • HashSet实现了Set接口
  • HashSet的底层实际上是HashMap(数组+链表+ 红黑树)
  • HashSet不保证元素是有序的。

二、HashSet添加元素的扩容机制

1.HashSet的底层是HashMap,添加一个元素时,先得到它的hash值,然后通过算法转化成索引值。第一次添加时,table数组扩容到16,临界值是12(个元素),到达临界时就会进行第二次扩容(临界值 = 容量 * 0.75),扩容时按两倍容量扩容.
2. 找到存储数据表table(是一个Node类型的数组),看这个索引位置是否已经存放的有元素,如果没有则直接添加;如果有,则调用equals方法比较(equals方法是程序员自己设置的比较方法(重写)),如果相同,则放弃添加,反之则加到最后。
3. 在Java8中,如果一条链表的元素到达8个,并且table的大小达到64个,那么会树化(形成红黑树).如果一条链表达到8个但是table的大小还没到64,则会继续扩容table表,并且在同一条链表上,那么每加一个就会扩容一次(因为都大于8),直到满足table表大于64,才会树化。

三、HashSet的源码解读
1、HashSet底层实际上是HashMap

 public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

2、底层调用的是HashMap的put()

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
    /**
    hash(key)的值是通过hash方法得来的
    而hash方法里又有hashXCode()以及相应的算法(h >>> 16)
    */
    //hash()源码: 
static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
//hashCode()源码:
  public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

3、关键点在putVal()上:

  1. 先判断数组(table表)是否为空,如果数组为空那么就给数组一个初始值,这个默认值为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;//这是一些临时变量
        /**
			如果数组为空那么就给数组一个初始值,这个默认值为16
		*/
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
             /**
				判断数组表中的位置是否为空,如果为空,则在数组table表中添加这个新的元素(结点)
					
					*/
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
             /**
				否则就定义一个节点,
				1、如果要添加元素的hash值与p的hash值(当前table表的某个Node,它的链表的头节点位置的hash值)相等
				或者这个要添加元素的key值与当前链表的头结点的key值相等(equals方法是程序员自己重写的,具体通过什么来判断是不确定的)
				那么就把当前结点赋值给e
				2、如果p是一个红黑树的节点,就执行树的添加机制
				
					*/
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
                /**
					3.1否则(不是同一个元素),循环判段table表的这个元素的这个链表,
					如果有相同的就放弃添加,
					如果判断完了没有相同的就加在链表的最后。
					如果链表的长度大于等于8以后,就执行treeifyBin(),也就是会再看table表的长度有没有64(会进入判断是否树化的方法中).
				*/
            else {
                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);
                        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;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

2.1.1LinkedHashSet

一、注意事项

  1. LinkedHashSet与HashSet类似,只不过LinkedHashSet底层是LinkedHashMap,它维护了一个数组 + 双向链表。
  2. 这个双向链表通过Before 和after属性形成的。
  3. 这个双向链表串起来所有的元素(相当于数组每个位置都能有一个双向链表,并且与其它位置的链表也是链接在一起的)。
  4. 因为有双向链表,所以看起来存储和取出的顺序一样。
  5. 它同样不允许添加重复的元素(怎么算重复也是自己可以重写hashCode() 和equals() 来决定)

二、底层源码分析

  1. 通过创建一个LinkedHashSet对象,然后往里面添加元素,可以发现,第一次拿到对象添加元素时便将table扩容到16了。
  2. map里有一个head头结点和tail尾节点,

JAVA基础——集合_第2张图片
3. table是一个多态数组,它的编译类型是Node,但是里面存放的是Entry,说明Entry继承了Node(事实上他们都是内部类,Entey内部类也确实继承了Node内部类)
JAVA基础——集合_第3张图片

/**
    * HashMap.Node subclass for normal LinkedHashMap entries.
    */
   static class Entry<K,V> extends HashMap.Node<K,V> {
       Entry<K,V> before, after;
       Entry(int hash, K key, V value, Node<K,V> next) {
           super(hash, key, value, next);
       }
   }```

```java

2.2TreeSet

一、注意事项

  1. TreeSet的底层是TreeMap,它能实现排序是因为有一个内部类方法(comparetor),这个comparetor的排序规则可以自己更改(例如:当添加元素时,可以认为长度相等就是相等的元素,就会添加不进去)
 TreeSet treeSet = new TreeSet(new Comparator() {
           @Override
           public int compare(Object o1, Object o2) {
               //按照K(string)的大小进行排序
               return ((String)o1).compareTo((String)o2);
               //也可按照K(string)的长度进行排序
               //return ((String)o1).length - ((String)o2).length;
           }
       });

二、源码分析(以添加元素为例)
在add时底层也是调用put()

public boolean add(E e) {
       return m.put(e, PRESENT)==null;
   }

//而关键就在于put(),它里面有一个compare方法,用来判断两个Key是否相等,然后执行相应的添加机制。

    public V put(K key, V value) {
        Entry<K,V> t = root;
        if (t == null) {
            compare(key, key); // type (and possibly null) check

            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator;
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        else {
            if (key == null)
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }


    public int compareTo(String anotherString) {
        int len1 = value.length;
        int len2 = anotherString.value.length;
        int lim = Math.min(len1, len2);
        char v1[] = value;
        char v2[] = anotherString.value;

        int k = 0;
        while (k < lim) {
            char c1 = v1[k];
            char c2 = v2[k];
            if (c1 != c2) {
                return c1 - c2;
            }
            k++;
        }
        return len1 - len2;
    }


三、Map集合(双列集合)

一、注意事项

  1. Map集合跟Collection集合是并列关系,它与Collection集合不同的是,它是双列集合(K-V形式存放数据)。
  2. Map的Key值不允许重复(只能有一个NULL值),但是Value可以重复。并且它们的类型可以是任意值,但是常用String来作为Key的类型。
  3. Map集合中可以通过Key值来找到相对应的Value(支持索引)。

1、HashMap

一、注意事项:

  1. HashMap是Map的实现类中最常用的,它通过K-V键值对的方式来存储数据(HashMap$Node类型)
  2. Key不能重复,但是Value值可以重复或者被替换(添加相同的Key和Value时,key不会被替换,但是Value会被替换)。
  3. HashMap跟HashSet类似,不能保证映射的顺序,因为底层是通过hash表(算法)来决定存储的位置的。
  4. HashMap没有实现同步,因此是线程不安全的,它没有实现synchronized。

二、底层机制

  1. HashMap与HashSet的底层是相同的(HashSet在构造器中创建了一个HashMap对象)。
  2. HashMap在创建时默认为null,它首先初始化一个加载因子为0.75,并且每次扩容都是按2倍,第一次扩容默认为16,临界值为 16*0.75 = 12。
  3. 在添加key-value时,会根据key的hash值来确定它们的存放位置;存放时判断当前的key值与要添加的key值是否相等,相等则替换value;如果不等,看结构是链表还是树结构,如果是链表结构再判断链表的大小和table的大小来决定是否要树化,(在java8中如果一条链表的长度超过8,并且table大小超过64就会树化)如果是树的结构也会判断equals方法…做相应的处理;如果容量不够则需要扩容。
    三、源码分析(以添加元素为例)
    1、在创建对象时,构造器里会先初始化一个加载因子,它的值为0.75.
 public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }

2、添加时调用put()方法,里面实际上是一个putVal() 和一个hash算法(hash(key)是为了得到key对应得hash值,以此来确定索引的位置)

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

3、详细看代码的注释:

    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是判断当前的table是否为空,如果为空则默认扩容为16
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        //这里是判断当前hash值对应的索引位置是否为空,如果为空,直接将元素放在这。
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {//到这里说明这个hash值对应table的索引位置有元素了,所以要开始进一步判断
            Node<K,V> e; K k;
            //①如果(它们的hash值相等)&&(要存放元素的key与当前的Key相等 || 它们的equals相等)
            //此时相当于用新的元素来替换掉当前元素,当然Key是不变的(key值唯一,替换value值)
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            //②如果当前的元素是一个树结构,那么直接放在树的节点后。
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            //以上①②都不满足,说明要添加的元素要放的位置是当前链表
            else {
            //循环判断当前链表的所有元素,如果没有相同的,就把它添加到链表最后
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        //如果链表的元素 >= TREEIFY_THRESHOLD(8) - 1,
                        //则进入到treeifyBin(判断table的大小有没有64,再决定要不要树化) 
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    //如果链表中的元素与要添加的元素相同,那么就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;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)//这是加载因子,如果达到临界值就按2倍扩容
            resize();
        afterNodeInsertion(evict);
        return null;
    }

2、HashTable

一、注意事项

  • HashTable底层有一个数组(HashTable$Node)
  • HashTable 也是通过K-V的方式来存储数据的,并且它的key和value都不能为空。
  • 它是线程安全的,有synchonized处理
  • 它的加载因子也是0.75,初始容量为11,扩容是按2倍加1来扩容的。

二、底层源码

public synchronized V put(K key, V value) {
       // Make sure the value is not null
       if (value == null) {
           throw new NullPointerException();
       }

       // Makes sure the key is not already in the hashtable.
       Entry<?,?> tab[] = table;
       int hash = key.hashCode();
       int index = (hash & 0x7FFFFFFF) % tab.length;
       @SuppressWarnings("unchecked")
       Entry<K,V> entry = (Entry<K,V>)tab[index];
       for(; entry != null ; entry = entry.next) {
           if ((entry.hash == hash) && entry.key.equals(key)) {
               V old = entry.value;
               entry.value = value;
               return old;
           }
       }

       addEntry(hash, key, value, index);
       return null;
   }

2.1 properties

一、注意事项

  • properties继承了HashTable,其存储方式也是k-v,并且key和value的值都不能为null。
  • 如果有相同的kry,那么value的值会被替换。

二、使用入门

public class PropertiesTest {
  public static void main(String[] args) throws Exception {

      Properties properties = new Properties();//1、先创建properties对象
      properties.load(new InputStreamReader(//2、加载配置文件,注意编码格式
              new FileInputStream("src\\mysql.properties"),"GBK"));
      //这里要设置编码,编码格式要与properties的格式一致,这里Properties的编码是GBK,所以也将它设置为GBK
      properties.list(System.out);//可以输出配置文件
      String name = properties.getProperty("name");//也可以根据键值对 取出自己想要的值
      String id = properties.getProperty("id");
      String pwd = properties.getProperty("pwd");
      System.out.println("name = " + name +"\nid = "+ id +"\npwd = "+ pwd);


      Properties properties1 = new Properties();
      properties1.setProperty("基本信息","内容");//设置信息
      properties1.setProperty("姓名","韩顺平");
      properties1.setProperty("年龄","21");
      //将上述内容创建一个目标文件并保存在里面。
      //这里的null是注释,如果有的话,在创建的时候会把注释写在文件上面,一般置空
      properties1.store(new FileWriter("src\\mysql2.properties"),null);
      properties1.list(System.out);




  }
}

3、TreeMap

总结:开发中如何选择这些集合?

一、数据是单列存放的(选择Collection)
1、允许重复(选择List)

  • 查改操作多:ArrayList
  • 增删操作多:LinkedList

2、不允许重复(选择Set)

  • 无序:HashSet
  • 插入和取出顺序一致:LinkedHashSet
  • 排序:TreeSet
    二、数据是双列(k-v)存放的(选择Map)
  • 键无序:HashMap
  • 键排序:TreeMap
  • 键插入和取出顺序一致:LinkedHashMap
  • 对文件进行操作:Properties

你可能感兴趣的:(java,开发语言)