Skip to content

proposal for BTreeMap/Set min/max, #62924 #65637

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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