35

For my program I want to read a key from a properties file and an associated List of values for the key.
Recently I was trying like that

public static Map<String,List<String>>categoryMap = new Hashtable<String, List<String>>();


    Properties prop = new Properties();


    try {

        prop2.load(new FileInputStream(/displayCategerization.properties));
        Set<Object> keys = prop.keySet();
        List<String> categoryList = new ArrayList<String>();
        for (Object key : keys) {
            categoryList.add((String)prop2.get(key));
            LogDisplayService.categoryMap.put((String)key,categoryList);
        }
        System.out.println(categoryList);
        System.out.println("Category Map :"+LogDisplayService.categoryMap);

        keys = null;
        prop = null;

    } catch (Throwable e) {
        e.printStackTrace();
    }

and my properties file is like below -

A=APPLE
A=ALPHABET
A=ANT
B=BAT
B=BALL
B=BUS

I want for key A there should be a list which contain [APPLE, ALPHABET,ANT] and B contain [BAT,BALL,BUS].
So Map should be like this {A=[APPLE, ALPHABET,ANT], B=[BAT,BALL,BUS]} but I get
{A=[ANT], B=[BUS]}
I searched on the internet for such a way but found nothing. I wish there should be a way. Any help?

DwB
  • 37,124
  • 11
  • 56
  • 82
NIVESH SENGAR
  • 1,285
  • 5
  • 19
  • 43
  • 1
    Property only support 'key,value' pair. when there multiple rows with same key, the last one wins(even that could 'undefined' behaviour). That is your input fed to categoryMap is not same as you think. I would suggest you write a small 'junit' test. – Jayan Mar 16 '12 at 13:10

6 Answers6

54

Try writing the properties as a comma separated list, then split the value after the properties file is loaded. For example

a=one,two,three
b=nine,ten,fourteen

You can also use org.apache.commons.configuration and change the value delimiter using the AbstractConfiguration.setListDelimiter(char) method if you're using comma in your values.

DwB
  • 37,124
  • 11
  • 56
  • 82
  • Good answer. I was thinking about having an xml file, and then writing a lot of code parsing it and storing objects from it. – Yan Khonski Jul 01 '16 at 09:37
  • 1
    One-liner using Apache configuration2: `new DefaultListDelimiterHandler(',').split(" a ,b , c ", true)` gives: a list of `"a", "b", "c"`. Javadoc: https://commons.apache.org/proper/commons-configuration/apidocs/org/apache/commons/configuration2/convert/DefaultListDelimiterHandler.html – Marcin Nov 23 '17 at 09:01
12

The comma separated list option is the easiest but becomes challenging if the values could include commas.

Here is an example of the a.1, a.2, ... approach:

for (String value : getPropertyList(prop, "a"))
{
    System.out.println(value);
}

public static List<String> getPropertyList(Properties properties, String name) 
{
    List<String> result = new ArrayList<String>();
    for (Map.Entry<Object, Object> entry : properties.entrySet())
    {
        if (((String)entry.getKey()).matches("^" + Pattern.quote(name) + "\\.\\d+$"))
        {
            result.add((String) entry.getValue());
        }
    }
    return result;
}
Eric Tobias
  • 3,225
  • 4
  • 32
  • 50
subie
  • 335
  • 3
  • 9
4

If this is for some configuration file processing, consider using Apache configuration. https://commons.apache.org/proper/commons-configuration/javadocs/v1.10/apidocs/index.html?org/apache/commons/configuration/PropertiesConfiguration.html It has way to multiple values to single key- The format is bit different though

key=value1,value2,valu3 gives three values against same key.

guerda
  • 23,388
  • 27
  • 97
  • 146
Jayan
  • 18,003
  • 15
  • 89
  • 143
3

Your logic is flawed... basically, you need to:

  1. get the list for the key
  2. if the list is null, create a new list and put it in the map
  3. add the word to the list

You're not doing step 2.

Here's the code you want:

Properties prop = new Properties();
prop.load(new FileInputStream("/displayCategerization.properties"));
for (Map.Entry<Object, Object> entry : prop.entrySet())
{
    List<String> categoryList = categoryMap.get((String) entry.getKey());
    if (categoryList == null)
    {
        categoryList = new ArrayList<String>();
        LogDisplayService.categoryMap.put((String) entry.getKey(), categoryList);
    }
    categoryList.add((String) entry.getValue());
}

Note also the "correct" way to iterate over the entries of a map/properties - via its entrySet().

Bohemian
  • 412,405
  • 93
  • 575
  • 722
2

There's probably a another way or better. But this is how I do this in Spring Boot.

My property file contains the following lines. "," is the delimiter in each line.

mml.pots=STDEP:DETY=LI3;,STDEP:DETY=LIMA;
mml.isdn.grunntengingar=STDEP:DETY=LIBAE;,STDEP:DETY=LIBAMA;
mml.isdn.stofntengingar=STDEP:DETY=LIPRAE;,STDEP:DETY=LIPRAM;,STDEP:DETY=LIPRAGS;,STDEP:DETY=LIPRVGS;

My server config

@Configuration
public class ServerConfig {

    @Inject
    private Environment env;

    @Bean
    public MMLProperties mmlProperties() {
        MMLProperties properties = new MMLProperties();
        properties.setMmmlPots(env.getProperty("mml.pots"));
        properties.setMmmlPots(env.getProperty("mml.isdn.grunntengingar"));
        properties.setMmmlPots(env.getProperty("mml.isdn.stofntengingar"));
        return properties;
    }
}

MMLProperties class.

public class MMLProperties {
    private String mmlPots;
    private String mmlIsdnGrunntengingar;
    private String mmlIsdnStofntengingar;

    public MMLProperties() {
        super();
    }

    public void setMmmlPots(String mmlPots) {
        this.mmlPots = mmlPots;
    }

    public void setMmlIsdnGrunntengingar(String mmlIsdnGrunntengingar) {
        this.mmlIsdnGrunntengingar = mmlIsdnGrunntengingar;
    }

    public void setMmlIsdnStofntengingar(String mmlIsdnStofntengingar) {
        this.mmlIsdnStofntengingar = mmlIsdnStofntengingar;
    }

    // These three public getXXX functions then take care of spliting the properties into List
    public List<String> getMmmlCommandForPotsAsList() {
        return getPropertieAsList(mmlPots);
    }

    public List<String> getMmlCommandsForIsdnGrunntengingarAsList() {
        return getPropertieAsList(mmlIsdnGrunntengingar);
    }

    public List<String> getMmlCommandsForIsdnStofntengingarAsList() {
        return getPropertieAsList(mmlIsdnStofntengingar);
    }

    private List<String> getPropertieAsList(String propertie) {
        return ((propertie != null) || (propertie.length() > 0))
        ? Arrays.asList(propertie.split("\\s*,\\s*"))
        : Collections.emptyList();
    }
}

Then in my Runner class I Autowire MMLProperties

@Component
public class Runner implements CommandLineRunner {

    @Autowired
    MMLProperties mmlProperties;

    @Override
    public void run(String... arg0) throws Exception {
        // Now I can call my getXXX function to retrieve the properties as List
        for (String command : mmlProperties.getMmmlCommandForPotsAsList()) {
            System.out.println(command);
        }
    }
}

Hope this helps

Gunnlaugur
  • 341
  • 1
  • 8
  • 19
2

Create a wrapper around properties and assume your A value has keys A.1, A.2, etc. Then when asked for A your wrapper will read all the A.* items and build the list. HTH

davidfrancis
  • 3,734
  • 2
  • 24
  • 21