java基础知识--Map集合、Properties类

一、Map集合

1.1 概述

  用来存放具有一一对应这种映射关系数据的容器,即为java.util.Map集合。Map中存放的是两种对象,一种称为key(键),一种称为value(值),它们在在Map中是一一对应关系,这一对对象又称做Map中的一个Entry(项)Entry将键值对的对应关系封装成了对象,即键值对对象

  特点:

  • 元素是成对存在的(key-value)。
  • 双列集合,无序。
  • 集合不能包含重复的键,值可以重复,每个键只能对应一个值。

1.2 Map接口中的常用方法

  Map接口中定义了很多方法,常用的如下:

  • public V put(K key, V value):  把指定的键与指定的值添加到Map集合中。

    ps:使用put方法时,若指定的键(key)在集合中没有,则没有这个键对应的值,返回null,并把指定的键值添加到集合中。

              若指定的键(key)在集合中存在,则会覆盖已有键的值,并返回被覆盖的值。

  • public V remove(Object key): 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。

  • public V get(Object key) 根据指定的键,在Map集合中获取对应的值。

  • boolean containsKey(Object key) 判断集合中是否包含指定的键。

  • public Set<K> keySet(): 获取Map集合中所有的键,存储到Set集合中。

  • public Set<Map.Entry<K,V>> entrySet(): 获取到Map集合中所有的entry键值对对象的集合(Set集合)。

    ps:Entry是Map接口内部开放(public)静态(static)接口(interface),

    既然Entry表示了一对键和值,那么也同样提供了获取对应键和对应值得方法:

    • public K getKey():获取Entry对象中的键。
    • public V getValue():获取Entry对象中的值。
  • Collection<V> values():返回此映射中包含的值的 Collection 视图。

    ps:可以通过values方法返回的Collection对象来调用iterator()方法,从而实现取数据。(先存后取,后存先取)

1.3 Map常用子类

  1.3.1 HashMap<K,V>

    1.3.1.1 概述

    HashMap是map的子类,存储数据采用的哈希表结构,元素的key是没有顺序的,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。

  • 集合初始化时,最好指定集合初始值的大小。

   比如HashMap初始化HashMap(int initialCapacity):initialCapacity=(需要存储的元素个数│负载因子)+1.注意负载因子(即loaderfactor)默认为0.75

    1.3.1.2 LinkedHashMap<K,V>

    LinkedHashMap是HashMap的子类,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致(有序通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。会保存记录的插入顺序。

  1.3.2 Hashtable<K,V>

    存储数据采用的哈希表结构,此类实现一个哈希表,该哈希表将键映射到相应的值。任何非 null 对象都可以用作键或值。

/**
   HashMap 与 HashTable 的异同:
       相同点:① 都是哈希表数据结构;
       不同点:① HashMap 线程不安全,效率比较高,1.2版本才有;
(如果需要满足线程安全,可以用 Collections 的 synchronizedMap 方法使HashMap 具有线程安全的能力,或者使用 ConcurrentHashMap。)
                HashTable 线程安全,效率不高,1.0之前就有。
              ② HashMap 可以存储null;HashTable 不可以存储null。
        (HashMap 最多只允许一条记录的键为 null,允许多条记录的值为 null。)
*/

  1.3.3 TreeMap<K,V>

    基于红黑树(Red-Black tree)的 NavigableMap 实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。

    在使用 TreeMap 时,key 必须实现 Comparable 接口或者在构造 TreeMap 传入自定义的Comparator,否则会在运行时抛出 java.lang.ClassCastException 类型的异常。

  1.3.4 ConcurrentHashMap<K,V>

    ConcurrentHashMap 和 HashMap 思路是差不多的,但是因为它支持并发操作,所以要复杂一些。整个 ConcurrentHashMap 由一个个 Segment 组成,Segment 代表”部分“或”一段“的意思,所以很多地方都会将其描述为分段锁(java8时不是分段锁,而改为cas实现。)。
    简单理解就是,ConcurrentHashMap 是一个 Segment 数组,Segment 通过继承ReentrantLock 来进行加锁,所以每次需要加锁的操作锁住的是一个 segment,这样只要保证每个 Segment 是线程安全的,也就实现了全局的线程安全。

    1.3.4.1 并行度(默认 16)

    concurrencyLevel:并行级别、并发数、Segment 数,怎么翻译不重要,理解它。默认是 16,也就是说 ConcurrentHashMap 有 16 个 Segments,所以理论上,这个时候,最多可以同时支持 16 个线程并发写,只要它们的操作分别分布在不同的 Segment 上。这个值可以在初始化的时候设置为其他值,但是一旦初始化以后,它是不可以扩容的。再具体到每个 Segment 内部,其实每个 Segment 很像之前介绍的 HashMap,不过它要保证线程安全,所以处理起来要麻烦些。

    1.3.4.2 Java8实现(引入了红黑树)

    Java8 对 ConcurrentHashMap 进行了比较大的改动,Java8 也引入了红黑树。

1.4 HashMap 和 ConcurrentHashMap 的区别

  ConcurrentHashMap 是线程安全的 HashMap 的实现。

  (1) ConcurrentHashMap 对整个桶数组进行了分割分段(Segment),然后在每一个分段上都用 lock 锁进行保护,相对于 HashTable 的 syn 关键字锁的粒度更精细了一些,并发性能更好,而 HashMap 没有锁机制,不是线程安全的。

  (2) HashMap 的键值对允许有 null,但是 ConcurrentHashMap 都不允许。

HashMap 的工作原理及代码实现

参考:https://tracylihui.github.io/2015/07/01/Java 集合学习 1:HashMap 的实现原理

ConcurrentHashMap 的工作原理及代码实现

HashTable 里使用的是 synchronized 关键字,这其实是对对象加锁,锁住的都是对象整体, 当 Hashtable 的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的时间。

ConcurrentHashMap 算是对上述问题的优化,其构造函数如下:

// 默认传入的是 16,0.75,16。
public ConcurrentHashMap(int paramInt1, float paramFloat, int paramInt2) {
	//…
	int i = 0;
	int j = 1;
	while (j < paramInt2) {
		++i;
		j <<= 1;
	}
	this.segmentShift = (32 ‐ i);
	this.segmentMask = (j ‐ 1);
	this.segments = Segment.newArray(j);
	//…
	int k = paramInt1 / j;
	if (k * j < paramInt1)
		++k;
	int l = 1;
	while (l < k)
		l <<= 1;
	for (int i1 = 0; i1 < this.segments.length; ++i1)
		this.segments[i1] = new Segment(l, paramFloat);
}

public V put(K paramK, V paramV) {
	if (paramV == null)
		throw new NullPointerException();
	int i = hash(paramK.hashCode()); //这里的 hash 函数和 HashMap 中的不一样
	return this.segments[(i >>> this.segmentShift & this.segmentMask)].put(paramK, i, paramV, false);
}

  ConcurrentHashMap 引入了分割(Segment),上面代码中的最后一行其实就可以理解为把一个大的 Map 拆分成 N 个小的 HashTable,在 put 方法中,会根据hash(paramK.hashCode())来决定具体存放进哪个 Segment,如果查看 Segment 的 put 操作,我们会发现内部使用的同步机制是基于 lock 操作的,这样就可以对 Map 的一部分(Segment)进行上锁,这样影响的只是将要放入同一个 Segment 的元素的 put 操作,保证同步的时候,锁住的不是整个 Map(HashTable 就是这么做的),相对于 HashTable 提高了多线程环境下的性能,因此 HashTable已经被淘汰了。

  Hashtable 是遗留类,很多映射的常用功能与 HashMap 类似,不同的是它承自 Dictionary 类,并且是线程安全的,任一时间只有一个线程能写 Hashtable,并发性不如ConcurrentHashMap,因为 ConcurrentHashMap 引入了分段锁。Hashtable 不建议在新代码中使用,不需要线程安全的场合可以用 HashMap 替换,需要线程安全的场合可以用ConcurrentHashMap 替换。

1.5 JDK9对集合添加的优化

   Java 9,添加了几种集合工厂方法,更方便创建少量元素的集合、map实例。新的List、Set、Map的静态工厂方法可以更方便地创建集合的不可变实例。

public class HelloJDK9 {
    public static void main(String[] args) {
        Set<String> str1 = Set.of("a","b","c");
        //str1.add("c");这里编译的时候不会错,但是执行的时候会报错,因为是不可变的集合
        System.out.println(str1);
        Map<String,Integer> str2 = Map.of("a",1,"b",2);
        System.out.println(str2);
        List<String> str3 = List.of("a","b");
        System.out.println(str3);
    }
}

  需要注意以下两点:

1:of() 方法只是Map,List,Set这三个接口的静态方法,其父类接口和子类实现并没有这类方法;

2:返回的集合是不可变的。

 

二、Properties类

2.1 概述

  java.util.Properties 继承于Hashtable的双列集合 ,来表示一个持久的属性集。是唯一和IO流相结合的集合。

  它使用键值结构存储数据,没有泛型,每个键及其对应值都是一个字符串。

该类也被许多Java类使用,比如获取系统属性时,System.getProperties 方法就是返回一个Properties对象。

可以通过java -D来给虚拟机动态添加属性信息

[root@iZbp1e6df2m25lgcmuajgzZ ~]# java
Usage: java [-options] class [args...]
           (to execute a class)
   or  java [-options] -jar jarfile [args...]
           (to execute a jar file)
where options include:
    -d32          use a 32-bit data model if available
    -d64          use a 64-bit data model if available
    -server       to select the "server" VM
                  The default VM is server.

    -cp <class search path of directories and zip/jar files>
    -classpath <class search path of directories and zip/jar files>
                  A : separated list of directories, JAR archives,
                  and ZIP archives to search for class files.
    -D<name>=<value>
                  set a system property
    -verbose:[class|gc|jni]
                  enable verbose output
    -version      print product version and exit
    -version:<value>
                  Warning: this feature is deprecated and will be removed
                  in a future release.
                  require the specified version to run
    -showversion  print product version and continue
    -jre-restrict-search | -no-jre-restrict-search
                  Warning: this feature is deprecated and will be removed
                  in a future release.
                  include/exclude user private JREs in the version search
    -? -help      print this help message
    -X            print help on non-standard options
    -ea[:<packagename>...|:<classname>]
    -enableassertions[:<packagename>...|:<classname>]
                  enable assertions with specified granularity
    -da[:<packagename>...|:<classname>]
    -disableassertions[:<packagename>...|:<classname>]
                  disable assertions with specified granularity
    -esa | -enablesystemassertions
                  enable system assertions
    -dsa | -disablesystemassertions
                  disable system assertions
    -agentlib:<libname>[=<options>]
                  load native agent library <libname>, e.g. -agentlib:hprof
                  see also, -agentlib:jdwp=help and -agentlib:hprof=help
    -agentpath:<pathname>[=<options>]
                  load native agent library by full pathname
    -javaagent:<jarpath>[=<options>]
                  load Java programming language agent, see java.lang.instrument
    -splash:<imagepath>
                  show splash screen with specified image
See http://www.oracle.com/technetwork/java/javase/documentation/index.html for more details.
[root@iZbp1e6df2m25lgcmuajgzZ ~]# java -D key=value 类名

2.2 构造方法

  • public Properties() : 创建一个空的属性列表。

2.3 基本的存储方法

  • public Object setProperty(String key, String value) : 保存一对属性。 

  • public String getProperty(String key) :使用此属性列表中指定的键搜索属性值。

  • public Set<String> stringPropertyNames() :所有键的名称的集合。

2.4 与流相关的方法

  • public void load(InputStream is): 从字节输入流中读取键值对。

  案例:读取文本数据

    文本数据格式:

  filename=wfx.txt
  length=20200819
  location=D:\wfx.txt

    代码如下:

public class ProDemo {
    public static void main(String[] args) throws FileNotFoundException {
        // 创建属性集对象
        Properties pro = new Properties();
        // 加载文本中信息到属性集
        pro.load(new FileInputStream("read.txt"));
        // 遍历集合并打印
        Set<String> strings = pro.stringPropertyNames();
        for (String key : strings ) {
          	System.out.println(key+" -- "+pro.getProperty(key));
        }
     }
}
输出结果:
filename -- wfx.txt
length -- 20200819
location -- D:\wfx.txt
  • public void store(Writer, comments):把集合中的数据,保存到指定的流所对应的文件中,参数commonts代表描述信息
Public class PropertiesDemo {
	Public static void main(String[] args) throws IOException {
		//1,创建Properties集合
		Properties prop = new Properties();
		//2,添加元素到集合
		prop.setProperty("name", "jack");
		prop.setProperty("age", "25");
		prop.setProperty("address", "usa");
		
		//3,创建流
		FileWriter out = new FileWriter("prop.properties");
		//4,把集合中的数据存储到流所对应的文件中
		prop.store(out, "save data");
		//5,关闭流
		out.close();
	}
}

小贴士:文本中的数据,必须是键值对形式,可以使用空格、等号、冒号等符号分隔。

 

posted @ 2020-08-13 08:20  九点的太阳  阅读(347)  评论(0编辑  收藏  举报