This is one way. Map<String, String> h = new HashMap<String, String>() {{ put("a","b"); }};
The Static Initializer for a Static HashMap We can also initialize the map using the double-brace syntax: Map<String, String> doubleBraceMap = new HashMap<String, String>() {{ put("key1", "value1"); put("key2", "value2"); }};
To populate HashMap in previous Java versions, we have to use the put() method. You can also make a map immutable once you populate the static HashMap. If we try to modify the immutable Map, we will get java. lang.
There isn't a map literal syntax in Rust. I don't know the exact reason, but I expect that the fact that there are multiple data structures that act maplike (such as both BTreeMap
and HashMap
) would make it hard to pick one.
As of Rust 1.51, you can use by-value array iterators and FromIterator
to collect into many kinds of collections:
use std::array::IntoIter;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::iter::FromIterator;
fn main() {
// Rust 1.53
let s = Vec::from_iter([1, 2, 3]);
println!("{:?}", s);
// Rust 1.51
let s = Vec::from_iter(IntoIter::new([1, 2, 3]));
println!("{:?}", s);
let s = BTreeSet::from_iter(IntoIter::new([1, 2, 3]));
println!("{:?}", s);
let s = HashSet::<_>::from_iter(IntoIter::new([1, 2, 3]));
println!("{:?}", s);
let s = BTreeMap::from_iter(IntoIter::new([(1, 2), (3, 4)]));
println!("{:?}", s);
let s = HashMap::<_, _>::from_iter(IntoIter::new([(1, 2), (3, 4)]));
println!("{:?}", s);
}
Note that in Rust 1.53, std::array::IntoIter
isn't always needed.
This logic can be wrapped back into a macro for some syntax sugar:
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
macro_rules! collection {
// map-like
($($k:expr => $v:expr),* $(,)?) => {{
use std::iter::{Iterator, IntoIterator};
Iterator::collect(IntoIterator::into_iter([$(($k, $v),)*]))
}};
// set-like
($($v:expr),* $(,)?) => {{
use std::iter::{Iterator, IntoIterator};
Iterator::collect(IntoIterator::into_iter([$($v,)*]))
}};
}
fn main() {
let s: Vec<_> = collection![1, 2, 3];
println!("{:?}", s);
let s: BTreeSet<_> = collection! { 1, 2, 3 };
println!("{:?}", s);
let s: HashSet<_> = collection! { 1, 2, 3 };
println!("{:?}", s);
let s: BTreeMap<_, _> = collection! { 1 => 2, 3 => 4 };
println!("{:?}", s);
let s: HashMap<_, _> = collection! { 1 => 2, 3 => 4 };
println!("{:?}", s);
}
These solutions avoid both unneeded allocation and reallocation.
See also:
You can create a macro to do the job for you, as demonstrated in Why does this rust HashMap macro no longer work?. Here is that macro simplified a bit and with enough structure to make it runnable in the playground:
macro_rules! map(
{ $($key:expr => $value:expr),+ } => {
{
let mut m = ::std::collections::HashMap::new();
$(
m.insert($key, $value);
)+
m
}
};
);
fn main() {
let names = map!{ 1 => "one", 2 => "two" };
println!("{} -> {:?}", 1, names.get(&1));
println!("{} -> {:?}", 10, names.get(&10));
}
This macro avoids allocating an unneeded intermediate Vec
, but it doesn't use HashMap::with_capacity
so there may be some useless reallocations of the HashMap
as values are added. A more complicated version of the macro that counts the values is possible, but the performance benefits are probably not something most uses of the macro would benefit from.
I recommend the maplit crate.
To quote from the documentation:
Macros for container literals with specific type.
use maplit::hashmap;
let map = hashmap!{
"a" => 1,
"b" => 2,
};
The maplit crate uses
=>
syntax for the mapping macros. It is not possible to use:
as separator due to syntactic the restrictions in regularmacro_rules!
macros.Note that rust macros are flexible in which brackets you use for the invocation. You can use them as
hashmap!{}
orhashmap![]
orhashmap!()
. This crate suggests{}
as the convention for the map&
set macros, it matches their Debug output.Macros
btreemap
Create aBTreeMap
from a list of key-value pairsbtreeset
Create aBTreeSet
from a list of elements.hashmap
Create aHashMap
from a list of key-value pairshashset
Create aHashSet
from a list of elements.
There is an example of how to achieve this in the documentation for HashMap
:
let timber_resources: HashMap<&str, i32> = [("Norway", 100), ("Denmark", 50), ("Iceland", 10)]
.iter()
.cloned()
.collect();
Starting with Rust 1.56, you can initialize a HashMap using from()
, which is somewhat like having a HashMap literal. from()
takes an array of key-value pairs. You can use it like this:
use std::collections::HashMap;
fn main() {
let hashmap = HashMap::from([
("foo", 1),
("bar", 2)
]);
}
As noted by @Johannes in the comments, it's possible to use vec![]
because:
Vec<T>
implements the IntoIterator<T>
traitHashMap<K, V>
implements FromIterator<Item = (K, V)>
which means you can do this:
let map: HashMap<String, String> = vec![("key".to_string(), "value".to_string())]
.into_iter()
.collect();
You can use &str
but you might need to annotate lifetimes if it's not 'static
:
let map: HashMap<&str, usize> = vec![("one", 1), ("two", 2)].into_iter().collect();
As of Rust 1.51, IntoIterator
is implemented for arrays, so you can create a HashMap
with the from_iter
method without cloning:
use std::collections::HashMap;
use std::iter::FromIterator;
// note that this type annotation is required
let mut map: HashMap<_, _> = HashMap::from_iter([("a", 1), ("b", 2), ("c", 3)]);
And as of Rust 1.56 (currently nightly), you can use the From<[(K, V); N]>
implementation, which is even cleaner:
let mut map = HashMap::from([
("a", 1),
("b", 2),
("c", 3),
]);
You can use the velcro
crate*. It is similar to maplit
, as recommended in other answers, but with more collection types, better syntax (in my opinion at least!) and more features.
Assuming you want to use String
s rather than &str
, your exact example would look like this:
use std::collections::HashMap;
use velcro::hash_map;
struct Node {
name: String
children: HashMap<String, Node>,
}
let map = hash_map! {
String::from("element0"): Node {
name: "My New Element".into(),
children: hash_map! {
String::from("child0"): Node {
name: "child0".into(),
children: hash_map!{}
}
}
}
};
That's a bit ugly because of how String
s are constructed. But it can be made a bit cleaner, without changing the key type, by using hash_map_from!
which will automatically do conversions:
use std::collections::HashMap;
use velcro::{hash_map, hash_map_from};
let map: HashMap<String, Node> = hash_map_from! {
"element0": Node {
name: "My New Element".into(),
children: hash_map_from! {
"child0": Node {
name: "child0".into(),
children: hash_map!{}
}
}
}
};
Which isn't much more verbose than the Go version.
*Full disclosure: I am the author of this crate.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With