Skip to content

Commit

Permalink
Auto merge of #65637 - ssomers:master, r=scottmcm
Browse files Browse the repository at this point in the history
proposal for BTreeMap/Set min/max, #62924

- Which pair of names: #62924 lists the existing possibilities min/max, first/last, (EDIT) front/back, peek(/peek_back?). Iterators have next/next_back or next/last. I'm slightly in favour of first/last because min/max might suggest they search over the entire map, and front/back pretends they are only about position.
- Return key only instead of pair like iterator does?
- If not, then keep the _key_value suffix? ~~Also provide variant with mutable value? But there is no such variant for get_key_value.~~
- Look for and upgrade more usages of `.iter().next()` and such in the libraries? I only upgraded the ones I contributed myself, all very recently.
  • Loading branch information
bors committed Nov 13, 2019
2 parents 0f12bad + ffeac1f commit 374ad1b
Show file tree
Hide file tree
Showing 7 changed files with 314 additions and 11 deletions.
25 changes: 25 additions & 0 deletions src/liballoc/benches/btree/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,28 @@ pub fn iter_1000(b: &mut Bencher) {
pub fn iter_100000(b: &mut Bencher) {
bench_iter(b, 100000);
}

fn bench_first_and_last(b: &mut Bencher, size: i32) {
let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect();
b.iter(|| {
for _ in 0..10 {
black_box(map.first_key_value());
black_box(map.last_key_value());
}
});
}

#[bench]
pub fn first_and_last_0(b: &mut Bencher) {
bench_first_and_last(b, 0);
}

#[bench]
pub fn first_and_last_100(b: &mut Bencher) {
bench_first_and_last(b, 100);
}

#[bench]
pub fn first_and_last_10k(b: &mut Bencher) {
bench_first_and_last(b, 10_000);
}
1 change: 1 addition & 0 deletions src/liballoc/benches/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![feature(map_first_last)]
#![feature(repr_simd)]
#![feature(test)]

Expand Down
115 changes: 115 additions & 0 deletions src/liballoc/collections/btree/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,121 @@ impl<K: Ord, V> BTreeMap<K, V> {
}
}

/// Returns the first key-value pair in the map.
/// The key in this pair is the minimum key in the map.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(map_first_last)]
/// use std::collections::BTreeMap;
///
/// let mut map = BTreeMap::new();
/// assert_eq!(map.first_key_value(), None);
/// map.insert(1, "b");
/// map.insert(2, "a");
/// assert_eq!(map.first_key_value(), Some((&1, &"b")));
/// ```
#[unstable(feature = "map_first_last", issue = "62924")]
pub fn first_key_value<T: ?Sized>(&self) -> Option<(&K, &V)>
where T: Ord, K: Borrow<T>
{
let front = first_leaf_edge(self.root.as_ref());
front.right_kv().ok().map(Handle::into_kv)
}

/// Returns the first entry in the map for in-place manipulation.
/// The key of this entry is the minimum key in the map.
///
/// # Examples
///
/// Contrived way to `clear` a map:
///
/// ```
/// #![feature(map_first_last)]
/// use std::collections::BTreeMap;
///
/// let mut map = BTreeMap::new();
/// map.insert(1, "a");
/// map.insert(2, "b");
/// while let Some(entry) = map.first_entry() {
/// let (key, val) = entry.remove_entry();
/// assert!(!map.contains_key(&key));
/// }
/// ```
#[unstable(feature = "map_first_last", issue = "62924")]
pub fn first_entry<T: ?Sized>(&mut self) -> Option<OccupiedEntry<'_, K, V>>
where T: Ord, K: Borrow<T>
{
match self.length {
0 => None,
_ => Some(OccupiedEntry {
handle: self.root.as_mut().first_kv(),
length: &mut self.length,
_marker: PhantomData,
}),
}
}

/// Returns the last key-value pair in the map.
/// The key in this pair is the maximum key in the map.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(map_first_last)]
/// use std::collections::BTreeMap;
///
/// let mut map = BTreeMap::new();
/// map.insert(1, "b");
/// map.insert(2, "a");
/// assert_eq!(map.last_key_value(), Some((&2, &"a")));
/// ```
#[unstable(feature = "map_first_last", issue = "62924")]
pub fn last_key_value<T: ?Sized>(&self) -> Option<(&K, &V)>
where T: Ord, K: Borrow<T>
{
let back = last_leaf_edge(self.root.as_ref());
back.left_kv().ok().map(Handle::into_kv)
}

/// Returns the last entry in the map for in-place manipulation.
/// The key of this entry is the maximum key in the map.
///
/// # Examples
///
/// Contrived way to `clear` a map:
///
/// ```
/// #![feature(map_first_last)]
/// use std::collections::BTreeMap;
///
/// let mut map = BTreeMap::new();
/// map.insert(1, "a");
/// map.insert(2, "b");
/// while let Some(entry) = map.last_entry() {
/// let (key, val) = entry.remove_entry();
/// assert!(!map.contains_key(&key));
/// }
/// ```
#[unstable(feature = "map_first_last", issue = "62924")]
pub fn last_entry<T: ?Sized>(&mut self) -> Option<OccupiedEntry<'_, K, V>>
where T: Ord, K: Borrow<T>
{
match self.length {
0 => None,
_ => Some(OccupiedEntry {
handle: self.root.as_mut().last_kv(),
length: &mut self.length,
_marker: PhantomData,
}),
}
}

/// Returns `true` if the map contains a value for the specified key.
///
/// The key may be any borrowed form of the map's key type, but the ordering
Expand Down
110 changes: 99 additions & 11 deletions src/liballoc/collections/btree/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,16 +194,16 @@ pub struct Difference<'a, T: 'a> {
#[derive(Debug)]
enum DifferenceInner<'a, T: 'a> {
Stitch {
// iterate all of self and some of other, spotting matches along the way
// iterate all of `self` and some of `other`, spotting matches along the way
self_iter: Iter<'a, T>,
other_iter: Peekable<Iter<'a, T>>,
},
Search {
// iterate a small set, look up in the large set
// iterate `self`, look up in `other`
self_iter: Iter<'a, T>,
other_set: &'a BTreeSet<T>,
},
Iterate(Iter<'a, T>), // simply stream self's elements
Iterate(Iter<'a, T>), // simply produce all values in `self`
}

#[stable(feature = "collection_debug", since = "1.17.0")]
Expand Down Expand Up @@ -356,7 +356,7 @@ impl<T: Ord> BTreeSet<T> {
#[stable(feature = "rust1", since = "1.0.0")]
pub fn difference<'a>(&'a self, other: &'a BTreeSet<T>) -> Difference<'a, T> {
let (self_min, self_max) = if let (Some(self_min), Some(self_max)) =
(self.iter().next(), self.iter().next_back())
(self.first(), self.last())
{
(self_min, self_max)
} else {
Expand All @@ -365,7 +365,7 @@ impl<T: Ord> BTreeSet<T> {
};
};
let (other_min, other_max) = if let (Some(other_min), Some(other_max)) =
(other.iter().next(), other.iter().next_back())
(other.first(), other.last())
{
(other_min, other_max)
} else {
Expand Down Expand Up @@ -450,7 +450,7 @@ impl<T: Ord> BTreeSet<T> {
#[stable(feature = "rust1", since = "1.0.0")]
pub fn intersection<'a>(&'a self, other: &'a BTreeSet<T>) -> Intersection<'a, T> {
let (self_min, self_max) = if let (Some(self_min), Some(self_max)) =
(self.iter().next(), self.iter().next_back())
(self.first(), self.last())
{
(self_min, self_max)
} else {
Expand All @@ -459,7 +459,7 @@ impl<T: Ord> BTreeSet<T> {
};
};
let (other_min, other_max) = if let (Some(other_min), Some(other_max)) =
(other.iter().next(), other.iter().next_back())
(other.first(), other.last())
{
(other_min, other_max)
} else {
Expand Down Expand Up @@ -625,14 +625,14 @@ impl<T: Ord> BTreeSet<T> {
return false;
}
let (self_min, self_max) = if let (Some(self_min), Some(self_max)) =
(self.iter().next(), self.iter().next_back())
(self.first(), self.last())
{
(self_min, self_max)
} else {
return true; // self is empty
};
let (other_min, other_max) = if let (Some(other_min), Some(other_max)) =
(other.iter().next(), other.iter().next_back())
(other.first(), other.last())
{
(other_min, other_max)
} else {
Expand All @@ -654,14 +654,12 @@ impl<T: Ord> BTreeSet<T> {
Less => (),
}
if self_iter.len() <= other.len() / ITER_PERFORMANCE_TIPPING_SIZE_DIFF {
// Big difference in number of elements.
for next in self_iter {
if !other.contains(next) {
return false;
}
}
} else {
// Self is not much smaller than other set.
let mut other_iter = other.iter();
other_iter.next();
other_iter.next_back();
Expand Down Expand Up @@ -702,6 +700,96 @@ impl<T: Ord> BTreeSet<T> {
other.is_subset(self)
}

/// Returns a reference to the first value in the set, if any.
/// This value is always the minimum of all values in the set.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(map_first_last)]
/// use std::collections::BTreeSet;
///
/// let mut map = BTreeSet::new();
/// assert_eq!(map.first(), None);
/// map.insert(1);
/// assert_eq!(map.first(), Some(&1));
/// map.insert(2);
/// assert_eq!(map.first(), Some(&1));
/// ```
#[unstable(feature = "map_first_last", issue = "62924")]
pub fn first(&self) -> Option<&T> {
self.map.first_key_value().map(|(k, _)| k)
}

/// Returns a reference to the last value in the set, if any.
/// This value is always the maximum of all values in the set.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(map_first_last)]
/// use std::collections::BTreeSet;
///
/// let mut map = BTreeSet::new();
/// assert_eq!(map.first(), None);
/// map.insert(1);
/// assert_eq!(map.last(), Some(&1));
/// map.insert(2);
/// assert_eq!(map.last(), Some(&2));
/// ```
#[unstable(feature = "map_first_last", issue = "62924")]
pub fn last(&self) -> Option<&T> {
self.map.last_key_value().map(|(k, _)| k)
}

/// Removes the first value from the set and returns it, if any.
/// The first value is always the minimum value in the set.
///
/// # Examples
///
/// ```
/// #![feature(map_first_last)]
/// use std::collections::BTreeSet;
///
/// let mut set = BTreeSet::new();
///
/// set.insert(1);
/// while let Some(n) = set.pop_first() {
/// assert_eq!(n, 1);
/// }
/// assert!(set.is_empty());
/// ```
#[unstable(feature = "map_first_last", issue = "62924")]
pub fn pop_first(&mut self) -> Option<T> {
self.map.first_entry().map(|entry| entry.remove_entry().0)
}

/// Removes the last value from the set and returns it, if any.
/// The last value is always the maximum value in the set.
///
/// # Examples
///
/// ```
/// #![feature(map_first_last)]
/// use std::collections::BTreeSet;
///
/// let mut set = BTreeSet::new();
///
/// set.insert(1);
/// while let Some(n) = set.pop_last() {
/// assert_eq!(n, 1);
/// }
/// assert!(set.is_empty());
/// ```
#[unstable(feature = "map_first_last", issue = "62924")]
pub fn pop_last(&mut self) -> Option<T> {
self.map.last_entry().map(|entry| entry.remove_entry().0)
}

/// Adds a value to the set.
///
/// If the set did not have this value present, `true` is returned.
Expand Down
Loading

0 comments on commit 374ad1b

Please sign in to comment.